@grafana/e2e: improvements (#25497)

* Minor changes

* Use UI for removing dashboards and datasources

… with an optional “quick” mode that instead does so via a request

* Improved URL helpers

* Simplified test teardown

* Added support for multiple tests

... instead of being forced to cram everything into a single test because the session was cleared
This commit is contained in:
Steven Vachon 2020-06-11 16:31:49 -04:00 committed by GitHub
parent 034abaa73a
commit 4c8ad8d031
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 95 additions and 65 deletions

View File

@ -15,6 +15,7 @@ module.exports = async baseConfig => {
// @todo: https://github.com/cypress-io/cypress/issues/6406
const jsonReporter = require.resolve('@mochajs/json-file-reporter');
// @todo `baseUrl: env.CYPRESS_BASEURL`
const projectConfig = {
fixturesFolder: `${CWD}/cypress/fixtures`,
integrationFolder: `${CWD}/cypress/integration`,

View File

@ -26,10 +26,18 @@ export const addPanel = (config?: Partial<AddPanelConfig>): any =>
const { dashboardUid, dataSourceName, panelTitle, queriesForm, visualizationName } = fullConfig;
e2e.flows.openDashboard(dashboardUid);
e2e.pages.Dashboard.visit(dashboardUid);
e2e.pages.Dashboard.Toolbar.toolbarItems('Add panel').click();
e2e.pages.AddDashboard.addNewPanel().click();
e2e().server();
// @todo alias '/**/*.js*' as '@pluginModule' when possible: https://github.com/cypress-io/cypress/issues/1296
e2e()
.route('POST', '/api/ds/query')
.as('chartData');
e2e()
.get('.ds-picker')
.click()
@ -58,11 +66,6 @@ export const addPanel = (config?: Partial<AddPanelConfig>): any =>
.click();
closeOptionsGroup('type');
e2e().server();
e2e()
.route('POST', '/api/ds/query')
.as('chartData');
queriesForm(fullConfig);
e2e().wait('@chartData');

View File

@ -2,37 +2,21 @@ import { e2e } from '../index';
import { fromBaseUrl } from '../support/url';
export interface DeleteDashboardConfig {
quick?: boolean;
title: string;
uid: string;
}
export const deleteDashboard = ({ title, uid }: DeleteDashboardConfig) => {
export const deleteDashboard = ({ quick = false, title, uid }: DeleteDashboardConfig) => {
e2e().logToConsole('Deleting dashboard with uid:', uid);
// Avoid dashboard page errors
e2e.pages.Home.visit();
e2e().request('DELETE', fromBaseUrl(`/api/dashboards/uid/${uid}`));
if (quick) {
quickDelete(uid);
} else {
uiDelete(uid, title);
}
/* https://github.com/cypress-io/cypress/issues/2831
Flows.openDashboard(title);
Pages.Dashboard.settings().click();
Pages.DashboardSettings.deleteDashBoard().click();
Pages.ConfirmModal.delete().click();
Flows.assertSuccessNotification();
Pages.Dashboards.visit();
Pages.Dashboards.dashboards().each(item => {
const text = item.text();
Cypress.log({ message: [text] });
if (text && text.indexOf(title) !== -1) {
expect(false).equals(true, `Dashboard ${title} was found although it was deleted.`);
}
});
*/
e2e().logToConsole('Deleted dashboard with uid:', uid);
e2e.getScenarioContext().then(({ addedDashboards }: any) => {
e2e.setScenarioContext({
@ -42,3 +26,26 @@ export const deleteDashboard = ({ title, uid }: DeleteDashboardConfig) => {
});
});
};
const quickDelete = (uid: string) => {
e2e().request('DELETE', fromBaseUrl(`/api/dashboards/uid/${uid}`));
};
const uiDelete = (uid: string, title: string) => {
e2e.pages.Dashboard.visit(uid);
e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings').click();
e2e.pages.Dashboard.Settings.General.deleteDashBoard().click();
e2e.pages.ConfirmModal.delete().click();
e2e.flows.assertSuccessNotification();
e2e.pages.Dashboards.visit();
// @todo replace `e2e.pages.Dashboards.dashboards` with this when argument is empty
e2e()
.get('[aria-label^="Dashboard search item "]')
.each(item =>
e2e()
.wrap(item)
.should('not.contain', title)
);
};

View File

@ -4,31 +4,19 @@ import { fromBaseUrl } from '../support/url';
export interface DeleteDataSourceConfig {
id: string;
name: string;
quick?: boolean;
}
export const deleteDataSource = ({ id, name }: DeleteDataSourceConfig) => {
export const deleteDataSource = ({ id, name, quick = false }: DeleteDataSourceConfig) => {
e2e().logToConsole('Deleting data source with name:', name);
// Avoid datasources page errors
e2e.pages.Home.visit();
e2e().request('DELETE', fromBaseUrl(`/api/datasources/name/${name}`));
if (quick) {
quickDelete(name);
} else {
uiDelete(name);
}
/* https://github.com/cypress-io/cypress/issues/2831
Pages.DataSources.visit();
Pages.DataSources.dataSources(name).click();
Pages.DataSource.delete().click();
Pages.ConfirmModal.delete().click();
Pages.DataSources.visit();
Pages.DataSources.dataSources().each(item => {
const text = item.text();
if (text && text.indexOf(name) !== -1) {
expect(false).equals(true, `Data source ${name} was found although it was deleted.`);
}
});
*/
e2e().logToConsole('Deleted data source with name:', name);
e2e.getScenarioContext().then(({ addedDataSources }: any) => {
e2e.setScenarioContext({
@ -38,3 +26,25 @@ export const deleteDataSource = ({ id, name }: DeleteDataSourceConfig) => {
});
});
};
const quickDelete = (name: string) => {
e2e().request('DELETE', fromBaseUrl(`/api/datasources/name/${name}`));
};
const uiDelete = (name: string) => {
e2e.pages.DataSources.visit();
e2e.pages.DataSources.dataSources(name).click();
e2e.pages.DataSource.delete().click();
e2e.pages.ConfirmModal.delete().click();
e2e.pages.DataSources.visit();
// @todo replace `e2e.pages.DataSources.dataSources` with this when argument is empty
e2e()
.get('[aria-label^="Data source list item "]')
.each(item =>
e2e()
.wrap(item)
.should('not.contain', name)
);
};

View File

@ -8,6 +8,7 @@ import { login } from './login';
import { openDashboard } from './openDashboard';
import { saveDashboard } from './saveDashboard';
import { openPanelMenuItem, PanelMenuItems } from './openPanelMenuItem';
import { revertAllChanges } from './revertAllChanges';
export const Flows = {
addDashboard,
@ -21,4 +22,5 @@ export const Flows = {
saveDashboard,
openPanelMenuItem,
PanelMenuItems,
revertAllChanges,
};

View File

@ -1,7 +1,7 @@
import { e2e } from '../index';
export const login = (username: string = 'admin', password: string = 'admin') => {
e2e().logToConsole('Trying to login with username:', username);
e2e().logToConsole('Logging in with username:', username);
e2e.pages.Login.visit();
e2e.pages.Login.username()
.should('be.visible') // prevents flakiness

View File

@ -1,5 +1,6 @@
import { e2e } from '../index';
// @todo remove this, as it's a page change and not a flow
export const openDashboard = (dashboardUid: string) => {
e2e.pages.Dashboard.visit(dashboardUid);
};

View File

@ -0,0 +1,8 @@
import { e2e } from '../index';
export const revertAllChanges = () => {
e2e.getScenarioContext().then(({ addedDashboards, addedDataSources }: any) => {
addedDashboards.forEach((dashboard: any) => e2e.flows.deleteDashboard({ ...dashboard, quick: true }));
addedDataSources.forEach((dataSource: any) => e2e.flows.deleteDataSource({ ...dataSource, quick: true }));
});
};

View File

@ -1,6 +1,5 @@
import { e2e } from '../';
import { Flows } from '../flows';
import { getScenarioContext } from './scenarioContext';
export interface ScenarioArguments {
describeName: string;
@ -23,8 +22,11 @@ export const e2eScenario = ({
if (skipScenario) {
it.skip(itName, () => scenario());
} else {
before(() => Flows.login(e2e.env('USERNAME'), e2e.env('PASSWORD')));
beforeEach(() => {
Flows.login(e2e.env('USERNAME'), e2e.env('PASSWORD'));
Cypress.Cookies.preserveOnce('grafana_session');
if (addScenarioDataSource) {
Flows.addDataSource();
}
@ -33,14 +35,13 @@ export const e2eScenario = ({
}
});
afterEach(() => {
getScenarioContext().then(({ addedDashboards, addedDataSources }: any) => {
addedDashboards.forEach((dashboard: any) => Flows.deleteDashboard(dashboard));
addedDataSources.forEach((dataSource: any) => Flows.deleteDataSource(dataSource));
});
});
afterEach(() => Flows.revertAllChanges());
after(() => e2e().clearCookies());
it(itName, () => scenario());
// @todo remove when possible: https://github.com/cypress-io/cypress/issues/2831
it('temporary', () => {});
}
});
};

View File

@ -2,13 +2,10 @@ import { e2e } from '../index';
const getBaseUrl = () => e2e.env('BASE_URL') || e2e.config().baseUrl || 'http://localhost:3000';
export const fromBaseUrl = (url = ''): string => {
const strippedUrl = url.replace('^/', '');
return `${getBaseUrl()}${strippedUrl}`;
};
export const fromBaseUrl = (url = '') => new URL(url, getBaseUrl()).href;
export const getDashboardUid = (url: string): string => {
const matches = url.match(/\/d\/(.*)\//);
const matches = new URL(url).pathname.match(/\/d\/([^/]+)/);
if (!matches) {
throw new Error(`Couldn't parse uid from ${url}`);
} else {
@ -17,7 +14,7 @@ export const getDashboardUid = (url: string): string => {
};
export const getDataSourceId = (url: string): string => {
const matches = url.match(/\/edit\/(.*)\//);
const matches = new URL(url).pathname.match(/\/edit\/([^/]+)/);
if (!matches) {
throw new Error(`Couldn't parse id from ${url}`);
} else {