From fb3bec058ac971bf6d070fef832f5544e35394a1 Mon Sep 17 00:00:00 2001 From: Steven Vachon Date: Fri, 26 Jun 2020 13:33:05 -0400 Subject: [PATCH] @grafana/e2e: improvements (#25708) * Set time range when opening a dashboard * Set UTC timezone when creating a dashboard * Added flow for selecting options in custom select fields * Fix flaky test --- e2e/shared/smokeTestScenario.ts | 4 +- e2e/suite1/specs/bar-gauge.spec.ts | 2 +- e2e/suite1/specs/dashboard-time-zone.spec.ts | 2 +- e2e/suite1/specs/inspect-drawer.spec.ts | 2 +- e2e/suite1/specs/panelEdit_base.spec.ts | 2 +- e2e/suite1/specs/panelEdit_queries.spec.ts | 6 ++- e2e/suite1/specs/panelEdit_transforms.spec.ts | 2 +- e2e/suite1/specs/queryVariableCrud.spec.ts | 2 +- e2e/suite1/specs/select-focus.spec.ts | 2 +- .../src/selectors/pages.ts | 1 + .../grafana-e2e/src/flows/addDashboard.ts | 11 +++- packages/grafana-e2e/src/flows/addPanel.ts | 16 ++---- packages/grafana-e2e/src/flows/index.ts | 2 + .../grafana-e2e/src/flows/openDashboard.ts | 51 +++++++++++++++++-- .../grafana-e2e/src/flows/selectOption.ts | 16 ++++++ .../TimeRangePicker/TimePickerContent.tsx | 8 ++- .../TimeRangePicker/TimeRangeForm.tsx | 6 ++- 17 files changed, 102 insertions(+), 33 deletions(-) create mode 100644 packages/grafana-e2e/src/flows/selectOption.ts diff --git a/e2e/shared/smokeTestScenario.ts b/e2e/shared/smokeTestScenario.ts index b9fd2f85d2f..fdfbab68fa1 100644 --- a/e2e/shared/smokeTestScenario.ts +++ b/e2e/shared/smokeTestScenario.ts @@ -7,9 +7,7 @@ export const smokeTestScenario = { addScenarioDashBoard: true, skipScenario: false, scenario: () => { - e2e.getScenarioContext().then(({ lastAddedDashboardUid }: any) => { - e2e.flows.openDashboard(lastAddedDashboardUid); - }); + e2e.flows.openDashboard(); e2e.pages.Dashboard.Toolbar.toolbarItems('Add panel').click(); e2e.pages.AddDashboard.addNewPanel().click(); diff --git a/e2e/suite1/specs/bar-gauge.spec.ts b/e2e/suite1/specs/bar-gauge.spec.ts index 204df33c69e..c78f498906c 100644 --- a/e2e/suite1/specs/bar-gauge.spec.ts +++ b/e2e/suite1/specs/bar-gauge.spec.ts @@ -8,7 +8,7 @@ e2e.scenario({ skipScenario: false, scenario: () => { // open Panel Tests - Bar Gauge - e2e.flows.openDashboard('O6f11TZWk'); + e2e.flows.openDashboard({ uid: 'O6f11TZWk' }); e2e() .get('#panel-6 .bar-gauge__value') diff --git a/e2e/suite1/specs/dashboard-time-zone.spec.ts b/e2e/suite1/specs/dashboard-time-zone.spec.ts index d7828bab906..819dab79f93 100644 --- a/e2e/suite1/specs/dashboard-time-zone.spec.ts +++ b/e2e/suite1/specs/dashboard-time-zone.spec.ts @@ -7,7 +7,7 @@ e2e.scenario({ addScenarioDashBoard: false, skipScenario: false, scenario: () => { - e2e.flows.openDashboard('5SdHCasdf'); + e2e.flows.openDashboard({ uid: '5SdHCasdf' }); const fromTimeZone = 'Coordinated Universal Time'; const toTimeZone = 'America/Chicago'; diff --git a/e2e/suite1/specs/inspect-drawer.spec.ts b/e2e/suite1/specs/inspect-drawer.spec.ts index c2b703e7af8..70210875b45 100644 --- a/e2e/suite1/specs/inspect-drawer.spec.ts +++ b/e2e/suite1/specs/inspect-drawer.spec.ts @@ -10,7 +10,7 @@ e2e.scenario({ skipScenario: false, scenario: () => { const viewPortWidth = e2e.config().viewportWidth; - e2e.flows.openDashboard('5SdHCadmz'); + e2e.flows.openDashboard({ uid: '5SdHCadmz' }); // testing opening inspect drawer directly by clicking on Inspect in header menu e2e.flows.openPanelMenuItem(e2e.flows.PanelMenuItems.Inspect, PANEL_UNDER_TEST); diff --git a/e2e/suite1/specs/panelEdit_base.spec.ts b/e2e/suite1/specs/panelEdit_base.spec.ts index 0107d383a8b..8854912f422 100644 --- a/e2e/suite1/specs/panelEdit_base.spec.ts +++ b/e2e/suite1/specs/panelEdit_base.spec.ts @@ -9,7 +9,7 @@ e2e.scenario({ addScenarioDashBoard: false, skipScenario: false, scenario: () => { - e2e.flows.openDashboard('5SdHCadmz'); + e2e.flows.openDashboard({ uid: '5SdHCadmz' }); e2e.flows.openPanelMenuItem(e2e.flows.PanelMenuItems.Edit, PANEL_UNDER_TEST); diff --git a/e2e/suite1/specs/panelEdit_queries.spec.ts b/e2e/suite1/specs/panelEdit_queries.spec.ts index bf38faffbaa..02d9db38b4a 100644 --- a/e2e/suite1/specs/panelEdit_queries.spec.ts +++ b/e2e/suite1/specs/panelEdit_queries.spec.ts @@ -10,7 +10,7 @@ e2e.scenario({ addScenarioDashBoard: false, skipScenario: false, scenario: () => { - e2e.flows.openDashboard('5SdHCadmz'); + e2e.flows.openDashboard({ uid: '5SdHCadmz' }); e2e.flows.openPanelMenuItem(e2e.flows.PanelMenuItems.Edit, PANEL_UNDER_TEST); @@ -96,6 +96,10 @@ e2e.scenario({ e2e().wait('@apiPostQuery'); + // Avoid flaky tests + // Maybe the virtual dom performs optimzations such as node position swapping, meaning 1 becomes 0 and it gets that element before the change because and never finds title 'A' + e2e().wait(250); + // Check the order of the rows after change e2e.components.QueryEditorRows.rows() .eq(0) diff --git a/e2e/suite1/specs/panelEdit_transforms.spec.ts b/e2e/suite1/specs/panelEdit_transforms.spec.ts index bd1f4fb42a6..cd87fb63a69 100644 --- a/e2e/suite1/specs/panelEdit_transforms.spec.ts +++ b/e2e/suite1/specs/panelEdit_transforms.spec.ts @@ -9,7 +9,7 @@ e2e.scenario({ addScenarioDashBoard: false, skipScenario: false, scenario: () => { - e2e.flows.openDashboard('5SdHCadmz'); + e2e.flows.openDashboard({ uid: '5SdHCadmz' }); e2e.flows.openPanelMenuItem(e2e.flows.PanelMenuItems.Edit, PANEL_UNDER_TEST); diff --git a/e2e/suite1/specs/queryVariableCrud.spec.ts b/e2e/suite1/specs/queryVariableCrud.spec.ts index 5ea51c4ee97..8b76d542a07 100644 --- a/e2e/suite1/specs/queryVariableCrud.spec.ts +++ b/e2e/suite1/specs/queryVariableCrud.spec.ts @@ -35,7 +35,7 @@ describe.skip('Variables', () => { } e2e.getScenarioContext().then(({ lastAddedDashboardUid, lastAddedDataSource }: any) => { - e2e.flows.openDashboard(lastAddedDashboardUid); + e2e.flows.openDashboard({ uid: lastAddedDashboardUid }); lastUid = lastAddedDashboardUid; lastData = lastAddedDataSource; }); diff --git a/e2e/suite1/specs/select-focus.spec.ts b/e2e/suite1/specs/select-focus.spec.ts index 2dd98f86faa..d8579c09295 100644 --- a/e2e/suite1/specs/select-focus.spec.ts +++ b/e2e/suite1/specs/select-focus.spec.ts @@ -7,7 +7,7 @@ e2e.scenario({ addScenarioDashBoard: false, skipScenario: false, scenario: () => { - e2e.flows.openDashboard('5SdHCadmz'); + e2e.flows.openDashboard({ uid: '5SdHCadmz' }); e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings').click(); e2e.components.FolderPicker.container() diff --git a/packages/grafana-e2e-selectors/src/selectors/pages.ts b/packages/grafana-e2e-selectors/src/selectors/pages.ts index 88263a25dbe..48d4605925e 100644 --- a/packages/grafana-e2e-selectors/src/selectors/pages.ts +++ b/packages/grafana-e2e-selectors/src/selectors/pages.ts @@ -52,6 +52,7 @@ export const Pages = { sectionItems: (item: string) => `Dashboard settings section item ${item}`, saveDashBoard: 'Dashboard settings aside actions Save button', saveAsDashBoard: 'Dashboard settings aside actions Save As button', + timezone: 'Time zone picker select container', title: 'Dashboard settings page title', }, Variables: { diff --git a/packages/grafana-e2e/src/flows/addDashboard.ts b/packages/grafana-e2e/src/flows/addDashboard.ts index 1373327b4e9..be216a3d74b 100644 --- a/packages/grafana-e2e/src/flows/addDashboard.ts +++ b/packages/grafana-e2e/src/flows/addDashboard.ts @@ -1,25 +1,32 @@ import { DeleteDashboardConfig } from './deleteDashboard'; import { e2e } from '../index'; import { getDashboardUid } from '../support/url'; +import { selectOption } from './selectOption'; export interface AddDashboardConfig { + timezone: string; title: string; } // @todo this actually returns type `Cypress.Chainable` export const addDashboard = (config?: Partial): any => { const fullConfig = { + timezone: 'Coordinated Universal Time', title: `e2e-${Date.now()}`, ...config, } as AddDashboardConfig; - const { title } = fullConfig; + const { timezone, title } = fullConfig; e2e().logToConsole('Adding dashboard with title:', title); e2e.pages.AddDashboard.visit(); - e2e.pages.Dashboard.Toolbar.toolbarItems('Save dashboard').click(); + e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings').click(); + + selectOption(e2e.pages.Dashboard.Settings.General.timezone(), timezone); + + e2e.pages.Dashboard.Settings.General.saveDashBoard().click(); e2e.pages.SaveDashboardAsModal.newName() .clear() diff --git a/packages/grafana-e2e/src/flows/addPanel.ts b/packages/grafana-e2e/src/flows/addPanel.ts index 1447d595e79..d02429a1028 100644 --- a/packages/grafana-e2e/src/flows/addPanel.ts +++ b/packages/grafana-e2e/src/flows/addPanel.ts @@ -1,6 +1,7 @@ import { e2e } from '../index'; import { getLocalStorage, requireLocalStorage } from '../support/localStorage'; import { getScenarioContext } from '../support/scenarioContext'; +import { selectOption } from './selectOption'; export interface AddPanelConfig { dashboardUid: string; @@ -26,7 +27,7 @@ export const addPanel = (config?: Partial): any => const { dashboardUid, dataSourceName, panelTitle, queriesForm, visualizationName } = fullConfig; - e2e.pages.Dashboard.visit(dashboardUid); + e2e.flows.openDashboard({ uid: dashboardUid }); e2e.pages.Dashboard.Toolbar.toolbarItems('Add panel').click(); e2e.pages.AddDashboard.addNewPanel().click(); @@ -38,18 +39,7 @@ export const addPanel = (config?: Partial): any => .route('POST', '/api/ds/query') .as('chartData'); - e2e.components.DataSourcePicker.container().within(() => { - e2e() - .get('[class$="-input-suffix"]') - .click(); - e2e.components.Select.option() - .filter(`:contains("${dataSourceName}")`) - .scrollIntoView() - .click(); - e2e() - .root() - .scrollIntoView(); - }); + selectOption(e2e.components.DataSourcePicker.container(), dataSourceName); // @todo instead wait for '@pluginModule' e2e().wait(2000); diff --git a/packages/grafana-e2e/src/flows/index.ts b/packages/grafana-e2e/src/flows/index.ts index 8cc537ca2a6..bb907b1211d 100644 --- a/packages/grafana-e2e/src/flows/index.ts +++ b/packages/grafana-e2e/src/flows/index.ts @@ -9,6 +9,7 @@ import { openDashboard } from './openDashboard'; import { saveDashboard } from './saveDashboard'; import { openPanelMenuItem, PanelMenuItems } from './openPanelMenuItem'; import { revertAllChanges } from './revertAllChanges'; +import { selectOption } from './selectOption'; export const Flows = { addDashboard, @@ -23,4 +24,5 @@ export const Flows = { openPanelMenuItem, PanelMenuItems, revertAllChanges, + selectOption, }; diff --git a/packages/grafana-e2e/src/flows/openDashboard.ts b/packages/grafana-e2e/src/flows/openDashboard.ts index 9bf26d36921..52fbb7214aa 100644 --- a/packages/grafana-e2e/src/flows/openDashboard.ts +++ b/packages/grafana-e2e/src/flows/openDashboard.ts @@ -1,6 +1,49 @@ import { e2e } from '../index'; +import { getScenarioContext } from '../support/scenarioContext'; -// @todo remove this, as it's a page change and not a flow -export const openDashboard = (dashboardUid: string) => { - e2e.pages.Dashboard.visit(dashboardUid); -}; +export interface OpenDashboardConfig { + uid: string; + timeRange: { + from: string; + to: string; + }; +} + +export const openDashboard = (config?: Partial) => + getScenarioContext().then(({ lastAddedDashboardUid }: any) => { + const fullConfig = { + timeRange: { + from: '2020-01-01 00:00:00', + to: '2020-01-01 01:00:00', + }, + uid: lastAddedDashboardUid, + ...config, + } as OpenDashboardConfig; + + const { timeRange, uid } = fullConfig; + + e2e.pages.Dashboard.visit(uid); + + e2e.pages.Dashboard.Toolbar.navBar().within(() => { + e2e() + .get('[aria-label="TimePicker Open Button"]') + .click(); + e2e() + .get('[aria-label="TimePicker absolute time range"]') + .click(); + e2e() + .get('[aria-label="TimePicker from field"]') + .clear() + .type(timeRange.from); + e2e() + .get('[aria-label="TimePicker to field"]') + .clear() + .type(timeRange.to); + e2e() + .get('[aria-label="TimePicker submit button"]') + .click(); + }); + + // @todo remove `wrap` when possible + return e2e().wrap({ config: fullConfig }); + }); diff --git a/packages/grafana-e2e/src/flows/selectOption.ts b/packages/grafana-e2e/src/flows/selectOption.ts new file mode 100644 index 00000000000..b38d96c7251 --- /dev/null +++ b/packages/grafana-e2e/src/flows/selectOption.ts @@ -0,0 +1,16 @@ +import { e2e } from '../index'; + +// @todo this actually returns type `Cypress.Chainable` +export const selectOption = (select: any, optionText: string): any => + select.within(() => { + e2e() + .get('[class$="-input-suffix"]') + .click(); + e2e.components.Select.option() + .filter(`:contains("${optionText}")`) + .scrollIntoView() + .click(); + e2e() + .root() + .scrollIntoView(); + }); diff --git a/packages/grafana-ui/src/components/TimePicker/TimeRangePicker/TimePickerContent.tsx b/packages/grafana-ui/src/components/TimePicker/TimeRangePicker/TimePickerContent.tsx index 8d4fa1479a6..aa84f8fcb11 100644 --- a/packages/grafana-ui/src/components/TimePicker/TimeRangePicker/TimePickerContent.tsx +++ b/packages/grafana-ui/src/components/TimePicker/TimeRangePicker/TimePickerContent.tsx @@ -200,7 +200,11 @@ const NarrowScreenForm: React.FC = props => { return ( <> -
setCollapsed(!collapsed)}> +
setCollapsed(!collapsed)} + > Absolute time range {}
@@ -239,7 +243,7 @@ const FullScreenForm: React.FC = props => { return ( <>
-
+
Absolute time range
diff --git a/packages/grafana-ui/src/components/TimePicker/TimeRangePicker/TimeRangeForm.tsx b/packages/grafana-ui/src/components/TimePicker/TimeRangePicker/TimeRangeForm.tsx index 9008ed557f3..787ebb1c45c 100644 --- a/packages/grafana-ui/src/components/TimePicker/TimeRangePicker/TimeRangeForm.tsx +++ b/packages/grafana-ui/src/components/TimePicker/TimeRangePicker/TimeRangeForm.tsx @@ -84,6 +84,7 @@ export const TimeRangeForm: React.FC = props => { onFocus={onFocus} onChange={event => setFrom(eventToState(event, false, timeZone))} addonAfter={icon} + aria-label="TimePicker from field" value={from.value} /> @@ -93,10 +94,13 @@ export const TimeRangeForm: React.FC = props => { onFocus={onFocus} onChange={event => setTo(eventToState(event, true, timeZone))} addonAfter={icon} + aria-label="TimePicker to field" value={to.value} /> - +