diff --git a/e2e/suite1/specs/panel-edit.spec.ts b/e2e/suite1/specs/panel-edit.spec.ts new file mode 100644 index 00000000000..5685258a427 --- /dev/null +++ b/e2e/suite1/specs/panel-edit.spec.ts @@ -0,0 +1,143 @@ +import { e2e } from '@grafana/e2e'; + +const PANEL_UNDER_TEST = 'Random walk series'; + +e2e.scenario({ + describeName: 'Panel edit tests', + itName: 'Testes various Panel edit scenarios', + addScenarioDataSource: false, + addScenarioDashBoard: false, + skipScenario: false, + scenario: () => { + e2e.flows.openDashboard('5SdHCadmz'); + + e2e.flows.openPanelMenuItem(e2e.flows.PanelMenuItems.Edit, PANEL_UNDER_TEST); + + // New panel editor opens when navigating from Panel menu + e2e.components.PanelEditor.General.content().should('be.visible'); + + // Queries tab is rendered and open by default + e2e.components.PanelEditor.DataPane.content() + .should('be.visible') + .within(() => { + e2e.components.Tab.title('Query').should('be.visible'); + // data should be the active tab + e2e.components.Tab.active().within((li: JQuery) => { + expect(li.text()).equals('Query1'); // there's already a query so therefore Query + 1 + }); + e2e.components.QueryTab.content().should('be.visible'); + e2e.components.TransformTab.content().should('not.be.visible'); + e2e.components.AlertTab.content().should('not.be.visible'); + + // Bottom pane tabs + // Can change to Transform tab + e2e.components.Tab.title('Transform') + .should('be.visible') + .click(); + e2e.components.Tab.active().within((li: JQuery) => { + expect(li.text()).equals('Transform0'); // there's no transform so therefore Transform + 0 + }); + e2e.components.TransformTab.content().should('be.visible'); + e2e.components.QueryTab.content().should('not.be.visible'); + e2e.components.AlertTab.content().should('not.be.visible'); + + // Can change to Alerts tab (graph panel is the default vis so the alerts tab should be rendered) + e2e.components.Tab.title('Alert') + .should('be.visible') + .click(); + e2e.components.Tab.active().within((li: JQuery) => { + expect(li.text()).equals('Alert0'); // there's no alert so therefore Alert + 0 + }); + e2e.components.AlertTab.content().should('be.visible'); + e2e.components.QueryTab.content().should('not.be.visible'); + e2e.components.TransformTab.content().should('not.be.visible'); + + e2e.components.Tab.title('Query') + .should('be.visible') + .click(); + }); + + // Panel sidebar is rendered open by default + e2e.components.PanelEditor.OptionsPane.content().should('be.visible'); + + // Can toggle on/off sidebar + e2e.components.PanelEditor.OptionsPane.close().should('be.visible'); + e2e.components.PanelEditor.OptionsPane.open().should('not.be.visible'); + + // close options pane + e2e.components.PanelEditor.OptionsPane.close().click(); + e2e.components.PanelEditor.OptionsPane.open().should('be.visible'); + e2e.components.PanelEditor.OptionsPane.close().should('not.be.visible'); + e2e.components.PanelEditor.OptionsPane.content().should('not.be.visible'); + + // open options pane + e2e.components.PanelEditor.OptionsPane.open().click(); + e2e.components.PanelEditor.OptionsPane.close().should('be.visible'); + e2e.components.PanelEditor.OptionsPane.open().should('not.be.visible'); + e2e.components.PanelEditor.OptionsPane.content().should('be.visible'); + + // Can change visualisation type + e2e.components.OptionsGroup.toggle('Panel type') + .should('be.visible') + .click(); + + // Check that Graph is chosen + e2e.components.PluginVisualization.item('Graph').should('be.visible'); + e2e.components.PluginVisualization.current().within((div: JQuery) => { + expect(div.text()).equals('Graph'); + }); + + // Change to Text panel + e2e.components.PluginVisualization.item('Text') + .scrollIntoView() + .should('be.visible') + .click(); + e2e.components.PluginVisualization.current().within((div: JQuery) => { + expect(div.text()).equals('Text'); + }); + + // Data pane should not be rendered + e2e.components.PanelEditor.DataPane.content().should('not.be.visible'); + + // Change to Table panel + e2e.components.PluginVisualization.item('Table') + .scrollIntoView() + .should('be.visible') + .click(); + e2e.components.PluginVisualization.current().within((div: JQuery) => { + expect(div.text()).equals('Table'); + }); + + // Data pane should be rendered + e2e.components.PanelEditor.DataPane.content().should('be.visible'); + + // Field & Overrides tabs (need to switch to React based vis, i.e. Table) + e2e.components.PanelEditor.OptionsPane.select() + .should('be.visible') + .click() + .within(() => { + // Can change to Fields tab + e2e.components.Select.option() + .should('be.visible') + .eq(1) + .click(); + }); + + e2e.components.FieldConfigEditor.content().should('be.visible'); + e2e.components.OverridesConfigEditor.content().should('not.be.visible'); + + e2e.components.PanelEditor.OptionsPane.select() + .should('be.visible') + .click() + .within(() => { + // Can change to Overrides tab + e2e.components.Select.option() + .should('be.visible') + .eq(2) + .click(); + }); + + e2e.components.OverridesConfigEditor.content().should('be.visible'); + e2e.components.FieldConfigEditor.content().should('not.be.visible'); + }, +}); diff --git a/packages/grafana-e2e/src/components/index.ts b/packages/grafana-e2e/src/components/index.ts index 8ea8cdd2132..3a5b09462aa 100644 --- a/packages/grafana-e2e/src/components/index.ts +++ b/packages/grafana-e2e/src/components/index.ts @@ -1,8 +1,8 @@ import { TestData } from '../pages/testdata'; import { Panel } from '../pages/panel'; -import { EditPanel } from '../pages/editPanel'; import { Graph } from '../pages/graph'; import { componentFactory } from '../support'; +import { Dashboard } from '../pages/dashboard'; export const Components = { DataSource: { @@ -10,7 +10,6 @@ export const Components = { }, Panels: { Panel, - EditPanel, Visualization: { Graph, }, @@ -26,6 +25,27 @@ export const Components = { }, }), }, + PanelEditor: { + General: componentFactory({ + selectors: { + content: 'Panel editor content', + }, + }), + OptionsPane: componentFactory({ + selectors: { + content: 'Panel editor option pane content', + close: Dashboard.selectors.toolbarItems('Close options pane'), + open: Dashboard.selectors.toolbarItems('Open options pane'), + select: 'Panel editor option pane select', + }, + }), + // not sure about the naming *DataPane* + DataPane: componentFactory({ + selectors: { + content: 'Panel editor data pane content', + }, + }), + }, PanelInspector: { Data: componentFactory({ selectors: { @@ -54,6 +74,21 @@ export const Components = { active: () => '[class*="-activeTabStyle"]', }, }), + QueryTab: componentFactory({ + selectors: { + content: 'Query editor tab content', + }, + }), + AlertTab: componentFactory({ + selectors: { + content: 'Alert editor tab content', + }, + }), + TransformTab: componentFactory({ + selectors: { + content: 'Transform editor tab content', + }, + }), QueryEditorToolbarItem: componentFactory({ selectors: { button: (title: string) => `QueryEditor toolbar item button ${title}`, @@ -64,4 +99,30 @@ export const Components = { backArrow: 'Go Back button', }, }), + OptionsGroup: componentFactory({ + selectors: { + toggle: (title: string) => `Options group ${title}`, + }, + }), + PluginVisualization: componentFactory({ + selectors: { + item: (title: string) => `Plugin visualization item ${title}`, + current: () => '[class*="-currentVisualizationItem"]', + }, + }), + Select: componentFactory({ + selectors: { + option: 'Select option', + }, + }), + FieldConfigEditor: componentFactory({ + selectors: { + content: 'Field config editor content', + }, + }), + OverridesConfigEditor: componentFactory({ + selectors: { + content: 'Field overrides editor content', + }, + }), }; diff --git a/packages/grafana-e2e/src/pages/editPanel.ts b/packages/grafana-e2e/src/pages/editPanel.ts deleted file mode 100644 index 1dfdb6dcd02..00000000000 --- a/packages/grafana-e2e/src/pages/editPanel.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { pageFactory } from '../support'; - -export const EditPanel = pageFactory({ - url: '', - selectors: { - tabItems: (text: string) => `Edit panel tab item ${text}`, - }, -}); diff --git a/public/app/features/alerting/AlertTab.tsx b/public/app/features/alerting/AlertTab.tsx index 7c114bbdd35..07583a69624 100644 --- a/public/app/features/alerting/AlertTab.tsx +++ b/public/app/features/alerting/AlertTab.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { connect, MapStateToProps, MapDispatchToProps } from 'react-redux'; +import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; import { css } from 'emotion'; import { Alert, Button, IconName } from '@grafana/ui'; @@ -15,10 +15,10 @@ import 'app/features/alerting/AlertTabCtrl'; import { DashboardModel } from '../dashboard/state/DashboardModel'; import { PanelModel } from '../dashboard/state/PanelModel'; import { TestRuleResult } from './TestRuleResult'; -import { AppNotificationSeverity, StoreState } from 'app/types'; -import { CoreEvents } from 'app/types'; +import { AppNotificationSeverity, CoreEvents, StoreState } from 'app/types'; import { updateLocation } from 'app/core/actions'; import { PanelEditorTabId } from '../dashboard/components/PanelEditor/types'; +import { e2e } from '@grafana/e2e'; interface OwnProps { dashboard: DashboardModel; @@ -206,7 +206,7 @@ class UnConnectedAlertTab extends PureComponent { return ( - <> +
{alert && hasTransformations && ( {
(this.element = element)} /> {!alert && !validatonMessage && } - +
); } diff --git a/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx index 46907bae8cb..7c5f1c3e54a 100644 --- a/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx +++ b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx @@ -13,6 +13,7 @@ import { getDataLinksVariableSuggestions } from '../../../panel/panellinks/link_ import { OverrideEditor } from './OverrideEditor'; import groupBy from 'lodash/groupBy'; import { OptionsGroup } from './OptionsGroup'; +import { e2e } from '@grafana/e2e'; interface Props { plugin: PanelPlugin; @@ -102,7 +103,7 @@ export const OverrideFieldConfigEditor: React.FC = props => { }; return ( -
+
{renderOverrides()} {renderAddOverride()}
@@ -182,7 +183,7 @@ export const DefaultFieldConfigEditor: React.FC = ({ data, onChange, conf const groupedConfigs = groupBy(plugin.fieldConfigRegistry.list(), i => i.category && i.category[0]); return ( - <> +
{Object.keys(groupedConfigs).map((k, i) => { const groupItemsCounter = countGroupItems(groupedConfigs[k], config); @@ -204,7 +205,7 @@ export const DefaultFieldConfigEditor: React.FC = ({ data, onChange, conf ); })} - +
); }; diff --git a/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx b/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx index fa5cc2cd8ca..358e2558928 100644 --- a/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx +++ b/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx @@ -4,6 +4,7 @@ import { GrafanaTheme } from '@grafana/data'; import { Icon, stylesFactory, useTheme } from '@grafana/ui'; import { PANEL_EDITOR_UI_STATE_STORAGE_KEY } from './state/reducers'; import { useLocalStorage } from 'react-use'; +import { e2e } from '@grafana/e2e'; export interface OptionsGroupProps { id: string; @@ -46,6 +47,7 @@ export const OptionsGroup: FC = ({ return ( = memo(props => { return ; }); -const CollapsibleSection: FC> = ({ +const CollapsibleSection: FC> = ({ + id, title, children, defaultToClosed, @@ -95,7 +98,11 @@ const CollapsibleSection: FC> = ({ return (
-
toggleExpand(!isExpanded)}> +
toggleExpand(!isExpanded)} + aria-label={e2e.components.OptionsGroup.selectors.toggle(id)} + >
diff --git a/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx b/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx index 038af5ff97d..46bab8f917a 100644 --- a/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx +++ b/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx @@ -1,13 +1,14 @@ -import React, { useCallback, useState, CSSProperties } from 'react'; +import React, { CSSProperties, useCallback, useState } from 'react'; import Transition from 'react-transition-group/Transition'; import { FieldConfigSource, GrafanaTheme, PanelPlugin, SelectableValue } from '@grafana/data'; import { DashboardModel, PanelModel } from '../../state'; -import { CustomScrollbar, stylesFactory, Tab, TabContent, TabsBar, Select, useTheme, Icon, Input } from '@grafana/ui'; +import { CustomScrollbar, Icon, Input, Select, stylesFactory, Tab, TabContent, TabsBar, useTheme } from '@grafana/ui'; import { DefaultFieldConfigEditor, OverrideFieldConfigEditor } from './FieldConfigEditor'; import { css } from 'emotion'; import { PanelOptionsTab } from './PanelOptionsTab'; import { DashNavButton } from 'app/features/dashboard/components/DashNav/DashNavButton'; import { usePanelLatestData } from './usePanelLatestData'; +import { e2e } from '@grafana/e2e'; interface Props { plugin: PanelPlugin; @@ -80,7 +81,7 @@ export const OptionsPaneContent: React.FC = ({ const showMainTab = activeTab === 'options' || plugin.meta.skipDataQuery; return ( -
+
{plugin && (
@@ -184,7 +185,7 @@ export const TabsBarContent: React.FC<{ return ( <> {width < 352 ? ( -
+