mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
@grafana/e2e: improvements (#25342)
* Minor changes * Remove console.* logger plugin ... as it doesn't work in Electron * Only open/close panel editor options and groups when state is inverted ... meaning, only open when closed and only close when open. This avoids unpredictable states, causing inconsistent results. * Support for adding multiple datasources and dashboards ... and having them all auto-removed when tests are completed * Avoid page errors when removing dashboards and datasources [keep?] * Wait for chart data before saving panel ... so that everything is ready when returning to the dashboard
This commit is contained in:
parent
5f767e2c9a
commit
d62926b5a3
@ -6,6 +6,9 @@ export const Pages = {
|
|||||||
submit: 'Login button',
|
submit: 'Login button',
|
||||||
skip: 'Skip change password button',
|
skip: 'Skip change password button',
|
||||||
},
|
},
|
||||||
|
Home: {
|
||||||
|
url: '/',
|
||||||
|
},
|
||||||
DataSource: {
|
DataSource: {
|
||||||
name: 'Data source settings page name input field',
|
name: 'Data source settings page name input field',
|
||||||
delete: 'Data source settings page Delete button',
|
delete: 'Data source settings page Delete button',
|
||||||
|
@ -2,21 +2,17 @@ const compareScreenshots = require('./compareScreenshots');
|
|||||||
const extendConfig = require('./extendConfig');
|
const extendConfig = require('./extendConfig');
|
||||||
const readProvisions = require('./readProvisions');
|
const readProvisions = require('./readProvisions');
|
||||||
const typescriptPreprocessor = require('./typescriptPreprocessor');
|
const typescriptPreprocessor = require('./typescriptPreprocessor');
|
||||||
const { install: installConsoleLogger } = require('cypress-log-to-output');
|
|
||||||
|
|
||||||
module.exports = (on, config) => {
|
module.exports = (on, config) => {
|
||||||
on('file:preprocessor', typescriptPreprocessor);
|
on('file:preprocessor', typescriptPreprocessor);
|
||||||
on('task', { compareScreenshots, readProvisions });
|
on('task', { compareScreenshots, readProvisions });
|
||||||
on('task', {
|
on('task', {
|
||||||
// @todo remove
|
|
||||||
log({ message, optional }) {
|
log({ message, optional }) {
|
||||||
optional ? console.log(message, optional) : console.log(message);
|
optional ? console.log(message, optional) : console.log(message);
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
installConsoleLogger(on);
|
|
||||||
|
|
||||||
// Always extend with this library's config and return for diffing
|
// Always extend with this library's config and return for diffing
|
||||||
// @todo remove this when possible: https://github.com/cypress-io/cypress/issues/5674
|
// @todo remove this when possible: https://github.com/cypress-io/cypress/issues/5674
|
||||||
return extendConfig(config);
|
return extendConfig(config);
|
||||||
|
@ -11,7 +11,6 @@ Cypress.Commands.add('compareScreenshots', (config: CompareScreenshotsConfig | s
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// @todo remove
|
|
||||||
Cypress.Commands.add('logToConsole', (message: string, optional?: any) => {
|
Cypress.Commands.add('logToConsole', (message: string, optional?: any) => {
|
||||||
cy.task('log', { message, optional });
|
cy.task('log', { message, optional });
|
||||||
});
|
});
|
||||||
|
@ -50,7 +50,6 @@
|
|||||||
"blink-diff": "1.0.13",
|
"blink-diff": "1.0.13",
|
||||||
"commander": "5.0.0",
|
"commander": "5.0.0",
|
||||||
"cypress": "^4.7.0",
|
"cypress": "^4.7.0",
|
||||||
"cypress-log-to-output": "^1.0.8",
|
|
||||||
"execa": "4.0.0",
|
"execa": "4.0.0",
|
||||||
"resolve-as-bin": "2.1.0",
|
"resolve-as-bin": "2.1.0",
|
||||||
"ts-loader": "6.2.1",
|
"ts-loader": "6.2.1",
|
||||||
|
@ -1,19 +1,50 @@
|
|||||||
|
import { DeleteDashboardConfig } from './deleteDashboard';
|
||||||
import { e2e } from '../index';
|
import { e2e } from '../index';
|
||||||
import { getDashboardUid } from '../support/url';
|
import { getDashboardUid } from '../support/url';
|
||||||
|
|
||||||
export const addDashboard = () => {
|
export interface AddDashboardConfig {
|
||||||
e2e().logToConsole('Adding dashboard');
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo this actually returns type `Cypress.Chainable`
|
||||||
|
export const addDashboard = (config?: Partial<AddDashboardConfig>): any => {
|
||||||
|
const fullConfig = {
|
||||||
|
title: `e2e-${Date.now()}`,
|
||||||
|
...config,
|
||||||
|
} as AddDashboardConfig;
|
||||||
|
|
||||||
|
const { title } = fullConfig;
|
||||||
|
|
||||||
|
e2e().logToConsole('Adding dashboard with title:', title);
|
||||||
|
|
||||||
e2e.pages.AddDashboard.visit();
|
e2e.pages.AddDashboard.visit();
|
||||||
|
|
||||||
const dashboardTitle = e2e.flows.saveNewDashboard();
|
e2e.pages.Dashboard.Toolbar.toolbarItems('Save dashboard').click();
|
||||||
e2e().logToConsole('Added dashboard with title:', dashboardTitle);
|
|
||||||
|
|
||||||
e2e()
|
e2e.pages.SaveDashboardAsModal.newName()
|
||||||
|
.clear()
|
||||||
|
.type(title);
|
||||||
|
e2e.pages.SaveDashboardAsModal.save().click();
|
||||||
|
|
||||||
|
e2e.flows.assertSuccessNotification();
|
||||||
|
|
||||||
|
e2e().logToConsole('Added dashboard with title:', title);
|
||||||
|
|
||||||
|
return e2e()
|
||||||
.url()
|
.url()
|
||||||
.then((url: string) => {
|
.then((url: string) => {
|
||||||
e2e.setScenarioContext({
|
const uid = getDashboardUid(url);
|
||||||
lastAddedDashboard: dashboardTitle,
|
|
||||||
lastAddedDashboardUid: getDashboardUid(url),
|
e2e.getScenarioContext().then(({ addedDashboards }: any) => {
|
||||||
|
e2e.setScenarioContext({
|
||||||
|
addedDashboards: [...addedDashboards, { title, uid } as DeleteDashboardConfig],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo remove `wrap` when possible
|
||||||
|
return e2e().wrap({
|
||||||
|
config: fullConfig,
|
||||||
|
uid,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,64 +1,68 @@
|
|||||||
|
import { DeleteDataSourceConfig } from './deleteDataSource';
|
||||||
import { e2e } from '../index';
|
import { e2e } from '../index';
|
||||||
import { fromBaseUrl, getDataSourceId } from '../support/url';
|
import { fromBaseUrl, getDataSourceId } from '../support/url';
|
||||||
import { setScenarioContext } from '../support/scenarioContext';
|
|
||||||
|
|
||||||
export interface AddDataSourceConfig {
|
export interface AddDataSourceConfig {
|
||||||
checkHealth: boolean;
|
checkHealth: boolean;
|
||||||
expectedAlertMessage: string | RegExp;
|
expectedAlertMessage: string | RegExp;
|
||||||
form: Function;
|
form: Function;
|
||||||
name: string;
|
name: string;
|
||||||
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_ADD_DATA_SOURCE_CONFIG: AddDataSourceConfig = {
|
// @todo this actually returns type `Cypress.Chainable`
|
||||||
checkHealth: false,
|
export const addDataSource = (config?: Partial<AddDataSourceConfig>): any => {
|
||||||
expectedAlertMessage: 'Data source is working',
|
const fullConfig = {
|
||||||
form: () => {},
|
checkHealth: false,
|
||||||
name: 'TestData DB',
|
expectedAlertMessage: 'Data source is working',
|
||||||
};
|
form: () => {},
|
||||||
|
name: `e2e-${Date.now()}`,
|
||||||
|
type: 'TestData DB',
|
||||||
|
...config,
|
||||||
|
} as AddDataSourceConfig;
|
||||||
|
|
||||||
export const addDataSource = (config?: Partial<AddDataSourceConfig>): string => {
|
const { checkHealth, expectedAlertMessage, form, name, type } = fullConfig;
|
||||||
const { checkHealth, expectedAlertMessage, form, name } = { ...DEFAULT_ADD_DATA_SOURCE_CONFIG, ...config };
|
|
||||||
|
|
||||||
e2e().logToConsole('Adding data source with name:', name);
|
e2e().logToConsole('Adding data source with name:', name);
|
||||||
e2e.pages.AddDataSource.visit();
|
e2e.pages.AddDataSource.visit();
|
||||||
e2e.pages.AddDataSource.dataSourcePlugins(name)
|
e2e.pages.AddDataSource.dataSourcePlugins(type)
|
||||||
.scrollIntoView()
|
.scrollIntoView()
|
||||||
.should('be.visible') // prevents flakiness
|
.should('be.visible') // prevents flakiness
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
const dataSourceName = `e2e-${Date.now()}`;
|
|
||||||
e2e.pages.DataSource.name().clear();
|
e2e.pages.DataSource.name().clear();
|
||||||
e2e.pages.DataSource.name().type(dataSourceName);
|
e2e.pages.DataSource.name().type(name);
|
||||||
form();
|
form();
|
||||||
e2e.pages.DataSource.saveAndTest().click();
|
e2e.pages.DataSource.saveAndTest().click();
|
||||||
e2e.pages.DataSource.alert().should('exist');
|
e2e.pages.DataSource.alert().should('exist');
|
||||||
e2e.pages.DataSource.alertMessage().contains(expectedAlertMessage); // assertion
|
e2e.pages.DataSource.alertMessage().contains(expectedAlertMessage); // assertion
|
||||||
e2e().logToConsole('Added data source with name:', dataSourceName);
|
e2e().logToConsole('Added data source with name:', name);
|
||||||
|
|
||||||
if (checkHealth) {
|
return e2e()
|
||||||
e2e()
|
.url()
|
||||||
.url()
|
.then((url: string) => {
|
||||||
.then((url: string) => {
|
const id = getDataSourceId(url);
|
||||||
const dataSourceId = getDataSourceId(url);
|
|
||||||
|
|
||||||
setScenarioContext({
|
e2e.getScenarioContext().then(({ addedDataSources }: any) => {
|
||||||
lastAddedDataSource: dataSourceName,
|
e2e.setScenarioContext({
|
||||||
lastAddedDataSourceId: dataSourceId,
|
addedDataSources: [...addedDataSources, { id, name } as DeleteDataSourceConfig],
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const healthUrl = fromBaseUrl(`/api/datasources/${dataSourceId}/health`);
|
if (checkHealth) {
|
||||||
|
const healthUrl = fromBaseUrl(`/api/datasources/${id}/health`);
|
||||||
e2e().logToConsole(`Fetching ${healthUrl}`);
|
e2e().logToConsole(`Fetching ${healthUrl}`);
|
||||||
e2e()
|
e2e()
|
||||||
.request(healthUrl)
|
.request(healthUrl)
|
||||||
.its('body')
|
.its('body')
|
||||||
.should('have.property', 'status')
|
.should('have.property', 'status')
|
||||||
.and('eq', 'OK');
|
.and('eq', 'OK');
|
||||||
});
|
}
|
||||||
} else {
|
|
||||||
setScenarioContext({
|
|
||||||
lastAddedDataSource: dataSourceName,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataSourceName;
|
// @todo remove `wrap` when possible
|
||||||
|
return e2e().wrap({
|
||||||
|
config: fullConfig,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { e2e } from '../index';
|
import { e2e } from '../index';
|
||||||
|
import { getLocalStorage, requireLocalStorage } from '../support/localStorage';
|
||||||
import { getScenarioContext } from '../support/scenarioContext';
|
import { getScenarioContext } from '../support/scenarioContext';
|
||||||
|
|
||||||
export interface AddPanelConfig {
|
export interface AddPanelConfig {
|
||||||
@ -7,6 +8,7 @@ export interface AddPanelConfig {
|
|||||||
queriesForm: (config: AddPanelConfig) => void;
|
queriesForm: (config: AddPanelConfig) => void;
|
||||||
panelTitle: string;
|
panelTitle: string;
|
||||||
visualizationName: string;
|
visualizationName: string;
|
||||||
|
waitForChartData: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo this actually returns type `Cypress.Chainable`
|
// @todo this actually returns type `Cypress.Chainable`
|
||||||
@ -18,6 +20,7 @@ export const addPanel = (config?: Partial<AddPanelConfig>): any =>
|
|||||||
panelTitle: `e2e-${Date.now()}`,
|
panelTitle: `e2e-${Date.now()}`,
|
||||||
queriesForm: () => {},
|
queriesForm: () => {},
|
||||||
visualizationName: 'Table',
|
visualizationName: 'Table',
|
||||||
|
waitForChartData: true,
|
||||||
...config,
|
...config,
|
||||||
} as AddPanelConfig;
|
} as AddPanelConfig;
|
||||||
|
|
||||||
@ -31,42 +34,98 @@ export const addPanel = (config?: Partial<AddPanelConfig>): any =>
|
|||||||
.get('.ds-picker')
|
.get('.ds-picker')
|
||||||
.click()
|
.click()
|
||||||
.contains('[id^="react-select-"][id*="-option-"]', dataSourceName)
|
.contains('[id^="react-select-"][id*="-option-"]', dataSourceName)
|
||||||
|
.scrollIntoView()
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
|
isOptionsOpen().then((isOpen: any) => {
|
||||||
|
if (!isOpen) {
|
||||||
|
toggleOptions();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
openOptionsGroup('settings');
|
||||||
getOptionsGroup('settings')
|
getOptionsGroup('settings')
|
||||||
.find('[value="Panel Title"]')
|
.find('[value="Panel Title"]')
|
||||||
|
.scrollIntoView()
|
||||||
.clear()
|
.clear()
|
||||||
.type(panelTitle);
|
.type(panelTitle);
|
||||||
toggleOptionsGroup('settings');
|
closeOptionsGroup('settings');
|
||||||
|
|
||||||
toggleOptionsGroup('type');
|
openOptionsGroup('type');
|
||||||
e2e()
|
e2e()
|
||||||
.get(`[aria-label="Plugin visualization item ${visualizationName}"]`)
|
.get(`[aria-label="Plugin visualization item ${visualizationName}"]`)
|
||||||
.scrollIntoView()
|
.scrollIntoView()
|
||||||
.click();
|
.click();
|
||||||
toggleOptionsGroup('type');
|
closeOptionsGroup('type');
|
||||||
|
|
||||||
|
e2e().server();
|
||||||
|
e2e()
|
||||||
|
.route('POST', '/api/ds/query')
|
||||||
|
.as('chartData');
|
||||||
|
|
||||||
queriesForm(fullConfig);
|
queriesForm(fullConfig);
|
||||||
|
|
||||||
|
e2e().wait('@chartData');
|
||||||
|
|
||||||
// @todo enable when plugins have this implemented
|
// @todo enable when plugins have this implemented
|
||||||
//e2e.components.QueryEditorRow.actionButton('Disable/enable query').click();
|
//e2e.components.QueryEditorRow.actionButton('Disable/enable query').click();
|
||||||
//e2e.components.Panels.Panel.containerByTitle(panelTitle).find('.panel-content').contains('No data');
|
//e2e.components.Panels.Panel.containerByTitle(panelTitle).find('.panel-content').contains('No data');
|
||||||
//e2e.components.QueryEditorRow.actionButton('Disable/enable query').click();
|
//e2e.components.QueryEditorRow.actionButton('Disable/enable query').click();
|
||||||
|
|
||||||
e2e.components.PanelEditor.OptionsPane.close().click();
|
isOptionsOpen().then((isOpen: any) => {
|
||||||
|
if (isOpen) {
|
||||||
|
toggleOptions();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
e2e()
|
e2e()
|
||||||
.get('button[title="Apply changes and go back to dashboard"]')
|
.get('button[title="Apply changes and go back to dashboard"]')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
// @todo remove `wrap` when possible
|
// @todo remove `wrap` when possible
|
||||||
return e2e().wrap(fullConfig);
|
return e2e().wrap({ config: fullConfig });
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo this actually returns type `Cypress.Chainable`
|
||||||
|
const closeOptionsGroup = (name: string): any =>
|
||||||
|
isOptionsGroupOpen(name).then((isOpen: any) => {
|
||||||
|
if (isOpen) {
|
||||||
|
toggleOptionsGroup(name);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const getOptionsGroup = (name: string) => e2e().get(`.options-group:has([aria-label="Options group Panel ${name}"])`);
|
const getOptionsGroup = (name: string) => e2e().get(`.options-group:has([aria-label="Options group Panel ${name}"])`);
|
||||||
|
|
||||||
|
// @todo this actually returns type `Cypress.Chainable`
|
||||||
|
const isOptionsGroupOpen = (name: string): any =>
|
||||||
|
requireLocalStorage(`grafana.dashboard.editor.ui.optionGroup[Panel ${name}]`).then(({ defaultToClosed }: any) => {
|
||||||
|
// @todo remove `wrap` when possible
|
||||||
|
return e2e().wrap(!defaultToClosed);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo this actually returns type `Cypress.Chainable`
|
||||||
|
const isOptionsOpen = (): any =>
|
||||||
|
getLocalStorage('grafana.dashboard.editor.ui').then((data: any) => {
|
||||||
|
if (data) {
|
||||||
|
// @todo remove `wrap` when possible
|
||||||
|
return e2e().wrap(data.isPanelOptionsVisible);
|
||||||
|
} else {
|
||||||
|
// @todo remove `wrap` when possible
|
||||||
|
return e2e().wrap(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo this actually returns type `Cypress.Chainable`
|
||||||
|
const openOptionsGroup = (name: string): any =>
|
||||||
|
isOptionsGroupOpen(name).then((isOpen: any) => {
|
||||||
|
if (!isOpen) {
|
||||||
|
toggleOptionsGroup(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const toggleOptions = () => e2e.components.PanelEditor.OptionsPane.close().click();
|
||||||
|
|
||||||
const toggleOptionsGroup = (name: string) =>
|
const toggleOptionsGroup = (name: string) =>
|
||||||
getOptionsGroup(name)
|
getOptionsGroup(name)
|
||||||
.find('.editor-options-group-toggle')
|
.find('.editor-options-group-toggle')
|
||||||
.scrollIntoView()
|
|
||||||
.click();
|
.click();
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
import { e2e } from '../index';
|
import { e2e } from '../index';
|
||||||
import { fromBaseUrl } from '../support/url';
|
import { fromBaseUrl } from '../support/url';
|
||||||
|
|
||||||
export const deleteDashboard = (dashBoardUid: string) => {
|
export interface DeleteDashboardConfig {
|
||||||
e2e().logToConsole('Deleting dashboard with uid:', dashBoardUid);
|
title: string;
|
||||||
e2e().request('DELETE', fromBaseUrl(`/api/dashboards/uid/${dashBoardUid}`));
|
uid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteDashboard = ({ 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}`));
|
||||||
|
|
||||||
/* https://github.com/cypress-io/cypress/issues/2831
|
/* https://github.com/cypress-io/cypress/issues/2831
|
||||||
Flows.openDashboard(dashboardName);
|
Flows.openDashboard(title);
|
||||||
|
|
||||||
Pages.Dashboard.settings().click();
|
Pages.Dashboard.settings().click();
|
||||||
|
|
||||||
@ -20,9 +28,17 @@ export const deleteDashboard = (dashBoardUid: string) => {
|
|||||||
Pages.Dashboards.dashboards().each(item => {
|
Pages.Dashboards.dashboards().each(item => {
|
||||||
const text = item.text();
|
const text = item.text();
|
||||||
Cypress.log({ message: [text] });
|
Cypress.log({ message: [text] });
|
||||||
if (text && text.indexOf(dashboardName) !== -1) {
|
if (text && text.indexOf(title) !== -1) {
|
||||||
expect(false).equals(true, `Dashboard ${dashboardName} was found although it was deleted.`);
|
expect(false).equals(true, `Dashboard ${title} was found although it was deleted.`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
e2e.getScenarioContext().then(({ addedDashboards }: any) => {
|
||||||
|
e2e.setScenarioContext({
|
||||||
|
addedDashboards: addedDashboards.filter((dashboard: DeleteDashboardConfig) => {
|
||||||
|
return dashboard.title !== title && dashboard.uid !== uid;
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,13 +1,21 @@
|
|||||||
import { e2e } from '../index';
|
import { e2e } from '../index';
|
||||||
import { fromBaseUrl } from '../support/url';
|
import { fromBaseUrl } from '../support/url';
|
||||||
|
|
||||||
export const deleteDataSource = (dataSourceName: string) => {
|
export interface DeleteDataSourceConfig {
|
||||||
e2e().logToConsole('Deleting data source with name:', dataSourceName);
|
id: string;
|
||||||
e2e().request('DELETE', fromBaseUrl(`/api/datasources/name/${dataSourceName}`));
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteDataSource = ({ id, name }: 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}`));
|
||||||
|
|
||||||
/* https://github.com/cypress-io/cypress/issues/2831
|
/* https://github.com/cypress-io/cypress/issues/2831
|
||||||
Pages.DataSources.visit();
|
Pages.DataSources.visit();
|
||||||
Pages.DataSources.dataSources(dataSourceName).click();
|
Pages.DataSources.dataSources(name).click();
|
||||||
|
|
||||||
Pages.DataSource.delete().click();
|
Pages.DataSource.delete().click();
|
||||||
|
|
||||||
@ -16,9 +24,17 @@ export const deleteDataSource = (dataSourceName: string) => {
|
|||||||
Pages.DataSources.visit();
|
Pages.DataSources.visit();
|
||||||
Pages.DataSources.dataSources().each(item => {
|
Pages.DataSources.dataSources().each(item => {
|
||||||
const text = item.text();
|
const text = item.text();
|
||||||
if (text && text.indexOf(dataSourceName) !== -1) {
|
if (text && text.indexOf(name) !== -1) {
|
||||||
expect(false).equals(true, `Data source ${dataSourceName} was found although it was deleted.`);
|
expect(false).equals(true, `Data source ${name} was found although it was deleted.`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
e2e.getScenarioContext().then(({ addedDataSources }: any) => {
|
||||||
|
e2e.setScenarioContext({
|
||||||
|
addedDataSources: addedDataSources.filter((dataSource: DeleteDataSourceConfig) => {
|
||||||
|
return dataSource.id !== id && dataSource.name !== name;
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -7,7 +7,6 @@ import { deleteDataSource } from './deleteDataSource';
|
|||||||
import { login } from './login';
|
import { login } from './login';
|
||||||
import { openDashboard } from './openDashboard';
|
import { openDashboard } from './openDashboard';
|
||||||
import { saveDashboard } from './saveDashboard';
|
import { saveDashboard } from './saveDashboard';
|
||||||
import { saveNewDashboard } from './saveNewDashboard';
|
|
||||||
import { openPanelMenuItem, PanelMenuItems } from './openPanelMenuItem';
|
import { openPanelMenuItem, PanelMenuItems } from './openPanelMenuItem';
|
||||||
|
|
||||||
export const Flows = {
|
export const Flows = {
|
||||||
@ -20,7 +19,6 @@ export const Flows = {
|
|||||||
login,
|
login,
|
||||||
openDashboard,
|
openDashboard,
|
||||||
saveDashboard,
|
saveDashboard,
|
||||||
saveNewDashboard,
|
|
||||||
openPanelMenuItem,
|
openPanelMenuItem,
|
||||||
PanelMenuItems,
|
PanelMenuItems,
|
||||||
};
|
};
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
import { e2e } from '../index';
|
|
||||||
|
|
||||||
export const saveNewDashboard = () => {
|
|
||||||
e2e.pages.Dashboard.Toolbar.toolbarItems('Save dashboard').click();
|
|
||||||
|
|
||||||
const dashboardTitle = `e2e-${Date.now()}`;
|
|
||||||
e2e.pages.SaveDashboardAsModal.newName().clear();
|
|
||||||
e2e.pages.SaveDashboardAsModal.newName().type(dashboardTitle);
|
|
||||||
e2e.pages.SaveDashboardAsModal.save().click();
|
|
||||||
|
|
||||||
e2e.flows.assertSuccessNotification();
|
|
||||||
|
|
||||||
return dashboardTitle;
|
|
||||||
};
|
|
@ -1,3 +1,4 @@
|
|||||||
export * from './types';
|
export * from './localStorage';
|
||||||
export * from './selector';
|
|
||||||
export * from './scenarioContext';
|
export * from './scenarioContext';
|
||||||
|
export * from './selector';
|
||||||
|
export * from './types';
|
||||||
|
23
packages/grafana-e2e/src/support/localStorage.ts
Normal file
23
packages/grafana-e2e/src/support/localStorage.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { e2e } from '../index';
|
||||||
|
|
||||||
|
// @todo this actually returns type `Cypress.Chainable`
|
||||||
|
const get = (key: string): any =>
|
||||||
|
e2e()
|
||||||
|
.wrap({ getLocalStorage: () => localStorage.getItem(key) })
|
||||||
|
.invoke('getLocalStorage');
|
||||||
|
|
||||||
|
// @todo this actually returns type `Cypress.Chainable`
|
||||||
|
export const getLocalStorage = (key: string): any =>
|
||||||
|
get(key).then((value: any) => {
|
||||||
|
if (value === null) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
return JSON.parse(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo this actually returns type `Cypress.Chainable`
|
||||||
|
export const requireLocalStorage = (key: string): any =>
|
||||||
|
get(key) // `getLocalStorage()` would turn 'null' into `null`
|
||||||
|
.should('not.equal', null)
|
||||||
|
.then((value: any) => JSON.parse(value as string));
|
@ -34,14 +34,9 @@ export const e2eScenario = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
getScenarioContext().then(({ lastAddedDashboardUid, lastAddedDataSource }: any) => {
|
getScenarioContext().then(({ addedDashboards, addedDataSources }: any) => {
|
||||||
if (lastAddedDashboardUid) {
|
addedDashboards.forEach((dashboard: any) => Flows.deleteDashboard(dashboard));
|
||||||
Flows.deleteDashboard(lastAddedDashboardUid);
|
addedDataSources.forEach((dataSource: any) => Flows.deleteDataSource(dataSource));
|
||||||
}
|
|
||||||
|
|
||||||
if (lastAddedDataSource) {
|
|
||||||
Flows.deleteDataSource(lastAddedDataSource);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,20 +1,39 @@
|
|||||||
import { e2e } from '../index';
|
import { e2e } from '../index';
|
||||||
|
import { DeleteDashboardConfig } from '../flows/deleteDashboard';
|
||||||
|
import { DeleteDataSourceConfig } from '../flows/deleteDataSource';
|
||||||
|
|
||||||
export interface ScenarioContext {
|
export interface ScenarioContext {
|
||||||
lastAddedDashboard: string;
|
addedDashboards: DeleteDashboardConfig[];
|
||||||
|
addedDataSources: DeleteDataSourceConfig[];
|
||||||
|
lastAddedDashboard: string; // @todo rename to `lastAddedDashboardTitle`
|
||||||
lastAddedDashboardUid: string;
|
lastAddedDashboardUid: string;
|
||||||
lastAddedDataSource: string;
|
lastAddedDataSource: string; // @todo rename to `lastAddedDataSourceName`
|
||||||
lastAddedDataSourceId: string;
|
lastAddedDataSourceId: string;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scenarioContext: ScenarioContext = {
|
const scenarioContext: ScenarioContext = {
|
||||||
lastAddedDashboard: '',
|
addedDashboards: [],
|
||||||
lastAddedDashboardUid: '',
|
addedDataSources: [],
|
||||||
lastAddedDataSource: '',
|
get lastAddedDashboard() {
|
||||||
lastAddedDataSourceId: '',
|
return lastProperty(this.addedDashboards, 'title');
|
||||||
|
},
|
||||||
|
get lastAddedDashboardUid() {
|
||||||
|
return lastProperty(this.addedDashboards, 'uid');
|
||||||
|
},
|
||||||
|
get lastAddedDataSource() {
|
||||||
|
return lastProperty(this.addedDataSources, 'name');
|
||||||
|
},
|
||||||
|
get lastAddedDataSourceId() {
|
||||||
|
return lastProperty(this.addedDataSources, 'id');
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const lastProperty = <T extends DeleteDashboardConfig | DeleteDataSourceConfig, K extends keyof T>(
|
||||||
|
items: T[],
|
||||||
|
key: K
|
||||||
|
) => items[items.length - 1]?.[key] ?? '';
|
||||||
|
|
||||||
// @todo this actually returns type `Cypress.Chainable`
|
// @todo this actually returns type `Cypress.Chainable`
|
||||||
export const getScenarioContext = (): any =>
|
export const getScenarioContext = (): any =>
|
||||||
e2e()
|
e2e()
|
||||||
|
Loading…
Reference in New Issue
Block a user