mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
@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
This commit is contained in:
parent
5d7cd2e9fc
commit
fb3bec058a
@ -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();
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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';
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
});
|
||||
|
@ -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()
|
||||
|
@ -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: {
|
||||
|
@ -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<AddDashboardConfig>): 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()
|
||||
|
@ -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<AddPanelConfig>): 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<AddPanelConfig>): 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);
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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<OpenDashboardConfig>) =>
|
||||
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 });
|
||||
});
|
||||
|
16
packages/grafana-e2e/src/flows/selectOption.ts
Normal file
16
packages/grafana-e2e/src/flows/selectOption.ts
Normal file
@ -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();
|
||||
});
|
@ -200,7 +200,11 @@ const NarrowScreenForm: React.FC<FormProps> = props => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.header} onClick={() => setCollapsed(!collapsed)}>
|
||||
<div
|
||||
aria-label="TimePicker absolute time range"
|
||||
className={styles.header}
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
>
|
||||
<TimePickerTitle>Absolute time range</TimePickerTitle>
|
||||
{<Icon name={collapsed ? 'angle-up' : 'angle-down'} />}
|
||||
</div>
|
||||
@ -239,7 +243,7 @@ const FullScreenForm: React.FC<FormProps> = props => {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.title}>
|
||||
<div aria-label="TimePicker absolute time range" className={styles.title}>
|
||||
<TimePickerTitle>Absolute time range</TimePickerTitle>
|
||||
</div>
|
||||
<TimeRangeForm value={props.value} timeZone={props.timeZone} onApply={props.onChange} isFullscreen={true} />
|
||||
|
@ -84,6 +84,7 @@ export const TimeRangeForm: React.FC<Props> = props => {
|
||||
onFocus={onFocus}
|
||||
onChange={event => setFrom(eventToState(event, false, timeZone))}
|
||||
addonAfter={icon}
|
||||
aria-label="TimePicker from field"
|
||||
value={from.value}
|
||||
/>
|
||||
</Field>
|
||||
@ -93,10 +94,13 @@ export const TimeRangeForm: React.FC<Props> = props => {
|
||||
onFocus={onFocus}
|
||||
onChange={event => setTo(eventToState(event, true, timeZone))}
|
||||
addonAfter={icon}
|
||||
aria-label="TimePicker to field"
|
||||
value={to.value}
|
||||
/>
|
||||
</Field>
|
||||
<Button onClick={onApply}>Apply time range</Button>
|
||||
<Button aria-label="TimePicker submit button" onClick={onApply}>
|
||||
Apply time range
|
||||
</Button>
|
||||
|
||||
<TimePickerCalendar
|
||||
isFullscreen={isFullscreen}
|
||||
|
Loading…
Reference in New Issue
Block a user