mirror of https://github.com/grafana/grafana
@grafana/e2e: improvements (#26939)
* Minor changes * Added an `editPanel` flow function ... and moved the internals of `addPanel` to a common function for use by both * Added optional template variables to `addDashboard` config * Use latest Cypress 4.x version * Updated lockfilepull/27108/head^2
parent
a3c3484286
commit
04249ae7ad
@ -1,169 +1,24 @@ |
||||
import { e2e } from '../index'; |
||||
import { getLocalStorage, requireLocalStorage } from '../support/localStorage'; |
||||
import { configurePanel, ConfigurePanelConfig } from './configurePanel'; |
||||
import { getScenarioContext } from '../support/scenarioContext'; |
||||
import { selectOption } from './selectOption'; |
||||
|
||||
export interface AddPanelConfig { |
||||
chartData: { |
||||
method: string; |
||||
route: string | RegExp; |
||||
}; |
||||
dashboardUid: string; |
||||
export interface AddPanelConfig extends ConfigurePanelConfig { |
||||
dataSourceName: string; |
||||
matchScreenshot: boolean; |
||||
queriesForm: (config: AddPanelConfig) => void; |
||||
panelTitle: string; |
||||
screenshotName: string; |
||||
visualizationName: string; |
||||
} |
||||
|
||||
// @todo improve config input/output: https://stackoverflow.com/a/63507459/923745
|
||||
// @todo this actually returns type `Cypress.Chainable`
|
||||
export const addPanel = (config?: Partial<AddPanelConfig>): any => |
||||
getScenarioContext().then(({ lastAddedDashboardUid, lastAddedDataSource }: any) => { |
||||
const fullConfig = { |
||||
chartData: { |
||||
method: 'POST', |
||||
route: '/api/ds/query', |
||||
}, |
||||
dashboardUid: lastAddedDashboardUid, |
||||
dataSourceName: lastAddedDataSource, |
||||
matchScreenshot: false, |
||||
panelTitle: `e2e-${Date.now()}`, |
||||
queriesForm: () => {}, |
||||
screenshotName: 'chart', |
||||
visualizationName: 'Table', |
||||
...config, |
||||
} as AddPanelConfig; |
||||
|
||||
const { |
||||
chartData, |
||||
dashboardUid, |
||||
dataSourceName, |
||||
matchScreenshot, |
||||
panelTitle, |
||||
queriesForm, |
||||
screenshotName, |
||||
visualizationName, |
||||
} = fullConfig; |
||||
|
||||
e2e.flows.openDashboard({ uid: 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(chartData.method, chartData.route) |
||||
.as('chartData'); |
||||
|
||||
selectOption(e2e.components.DataSourcePicker.container(), dataSourceName); |
||||
|
||||
// @todo instead wait for '@pluginModule'
|
||||
e2e().wait(2000); |
||||
|
||||
openOptions(); |
||||
|
||||
openOptionsGroup('settings'); |
||||
getOptionsGroup('settings') |
||||
.find('[value="Panel Title"]') |
||||
.scrollIntoView() |
||||
.clear() |
||||
.type(panelTitle); |
||||
closeOptionsGroup('settings'); |
||||
|
||||
openOptionsGroup('type'); |
||||
e2e.components.PluginVisualization.item(visualizationName) |
||||
.scrollIntoView() |
||||
.click(); |
||||
closeOptionsGroup('type'); |
||||
|
||||
closeOptions(); |
||||
|
||||
queriesForm(fullConfig); |
||||
|
||||
e2e().wait('@chartData'); |
||||
|
||||
// @todo enable when plugins have this implemented
|
||||
//e2e.components.QueryEditorRow.actionButton('Disable/enable query').click();
|
||||
//e2e.components.Panels.Panel.containerByTitle(panelTitle).find('.panel-content').contains('No data');
|
||||
//e2e.components.QueryEditorRow.actionButton('Disable/enable query').click();
|
||||
|
||||
e2e() |
||||
.get('button[title="Apply changes and go back to dashboard"]') |
||||
.click(); |
||||
|
||||
e2e().wait('@chartData'); |
||||
|
||||
// Wait for RxJS
|
||||
e2e().wait(500); |
||||
|
||||
if (matchScreenshot) { |
||||
e2e.components.Panels.Panel.containerByTitle(panelTitle) |
||||
.find('.panel-content') |
||||
.screenshot(screenshotName); |
||||
e2e().compareScreenshots(screenshotName); |
||||
} |
||||
|
||||
// @todo remove `wrap` when possible
|
||||
return e2e().wrap({ config: fullConfig }); |
||||
}); |
||||
|
||||
// @todo this actually returns type `Cypress.Chainable`
|
||||
const closeOptions = (): any => |
||||
isOptionsOpen().then((isOpen: any) => { |
||||
if (isOpen) { |
||||
e2e.components.PanelEditor.OptionsPane.close().click(); |
||||
} |
||||
}); |
||||
|
||||
// @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}"])`); |
||||
|
||||
// @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 openOptions = (): any => |
||||
isOptionsOpen().then((isOpen: any) => { |
||||
if (!isOpen) { |
||||
e2e.components.PanelEditor.OptionsPane.open().click(); |
||||
} |
||||
}); |
||||
|
||||
// @todo this actually returns type `Cypress.Chainable`
|
||||
const openOptionsGroup = (name: string): any => |
||||
isOptionsGroupOpen(name).then((isOpen: any) => { |
||||
if (!isOpen) { |
||||
toggleOptionsGroup(name); |
||||
} |
||||
}); |
||||
|
||||
const toggleOptionsGroup = (name: string) => |
||||
getOptionsGroup(name) |
||||
.find('.editor-options-group-toggle') |
||||
.click(); |
||||
getScenarioContext().then(({ lastAddedDataSource }: any) => |
||||
configurePanel( |
||||
{ |
||||
dataSourceName: lastAddedDataSource, |
||||
panelTitle: `e2e-${Date.now()}`, |
||||
visualizationName: 'Table', |
||||
...config, |
||||
} as AddPanelConfig, |
||||
false |
||||
) |
||||
); |
||||
|
@ -0,0 +1,201 @@ |
||||
import { e2e } from '../index'; |
||||
import { getLocalStorage, requireLocalStorage } from '../support/localStorage'; |
||||
import { getScenarioContext } from '../support/scenarioContext'; |
||||
import { selectOption } from './selectOption'; |
||||
|
||||
export interface ConfigurePanelConfig { |
||||
chartData: { |
||||
method: string; |
||||
route: string | RegExp; |
||||
}; |
||||
dashboardUid: string; |
||||
dataSourceName?: string; |
||||
matchScreenshot: boolean; |
||||
queriesForm?: (config: any) => void; |
||||
panelTitle: string; |
||||
screenshotName: string; |
||||
visitDashboardAtStart: boolean; // @todo remove when possible
|
||||
visualizationName?: string; |
||||
} |
||||
|
||||
// @todo improve config input/output: https://stackoverflow.com/a/63507459/923745
|
||||
// @todo this actually returns type `Cypress.Chainable`
|
||||
export const configurePanel = (config: Partial<ConfigurePanelConfig>, edit: boolean): any => |
||||
getScenarioContext().then(({ lastAddedDashboardUid }: any) => { |
||||
const fullConfig = { |
||||
chartData: { |
||||
method: 'POST', |
||||
route: '/api/ds/query', |
||||
}, |
||||
dashboardUid: lastAddedDashboardUid, |
||||
matchScreenshot: false, |
||||
saveDashboard: true, |
||||
screenshotName: 'chart', |
||||
visitDashboardAtStart: true, |
||||
...config, |
||||
} as ConfigurePanelConfig; |
||||
|
||||
const { |
||||
chartData, |
||||
dashboardUid, |
||||
dataSourceName, |
||||
matchScreenshot, |
||||
panelTitle, |
||||
queriesForm, |
||||
screenshotName, |
||||
visitDashboardAtStart, |
||||
visualizationName, |
||||
} = fullConfig; |
||||
|
||||
if (visitDashboardAtStart) { |
||||
e2e.flows.openDashboard({ uid: dashboardUid }); |
||||
} |
||||
|
||||
if (edit) { |
||||
e2e.components.Panels.Panel.title(panelTitle).click(); |
||||
e2e.components.Panels.Panel.headerItems('Edit').click(); |
||||
} else { |
||||
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(chartData.method, chartData.route) |
||||
.as('chartData'); |
||||
|
||||
if (dataSourceName) { |
||||
selectOption(e2e.components.DataSourcePicker.container(), dataSourceName); |
||||
} |
||||
|
||||
// @todo instead wait for '@pluginModule'
|
||||
e2e().wait(2000); |
||||
|
||||
e2e().wait('@chartData'); |
||||
|
||||
// `panelTitle` is needed to edit the panel, and unlikely to have its value changed at that point
|
||||
const changeTitle = panelTitle && !edit; |
||||
|
||||
if (changeTitle || visualizationName) { |
||||
openOptions(); |
||||
|
||||
if (changeTitle) { |
||||
openOptionsGroup('settings'); |
||||
getOptionsGroup('settings') |
||||
.find('[value="Panel Title"]') |
||||
.scrollIntoView() |
||||
.clear() |
||||
.type(panelTitle); |
||||
closeOptionsGroup('settings'); |
||||
} |
||||
|
||||
if (visualizationName) { |
||||
openOptionsGroup('type'); |
||||
e2e.components.PluginVisualization.item(visualizationName) |
||||
.scrollIntoView() |
||||
.click(); |
||||
closeOptionsGroup('type'); |
||||
} |
||||
|
||||
closeOptions(); |
||||
} else { |
||||
// Options are consistently closed
|
||||
closeOptions(); |
||||
} |
||||
|
||||
if (queriesForm) { |
||||
queriesForm(fullConfig); |
||||
e2e().wait('@chartData'); |
||||
} |
||||
|
||||
// @todo enable when plugins have this implemented
|
||||
//e2e.components.QueryEditorRow.actionButton('Disable/enable query').click();
|
||||
//e2e().wait('@chartData');
|
||||
//e2e.components.Panels.Panel.containerByTitle(panelTitle).find('.panel-content').contains('No data');
|
||||
//e2e.components.QueryEditorRow.actionButton('Disable/enable query').click();
|
||||
//e2e().wait('@chartData');
|
||||
|
||||
e2e() |
||||
.get('button[title="Apply changes and go back to dashboard"]') |
||||
.click(); |
||||
|
||||
e2e() |
||||
.url() |
||||
.should('include', `/d/${dashboardUid}`); |
||||
|
||||
e2e().wait('@chartData'); |
||||
|
||||
// Wait for RxJS
|
||||
e2e().wait(500); |
||||
|
||||
if (matchScreenshot) { |
||||
e2e.components.Panels.Panel.containerByTitle(panelTitle) |
||||
.find('.panel-content') |
||||
.screenshot(screenshotName); |
||||
e2e().compareScreenshots(screenshotName); |
||||
} |
||||
|
||||
// @todo remove `wrap` when possible
|
||||
return e2e().wrap({ config: fullConfig }, { log: false }); |
||||
}); |
||||
|
||||
// @todo this actually returns type `Cypress.Chainable`
|
||||
const closeOptions = (): any => |
||||
isOptionsOpen().then((isOpen: any) => { |
||||
if (isOpen) { |
||||
e2e.components.PanelEditor.OptionsPane.close().click(); |
||||
} |
||||
}); |
||||
|
||||
// @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}"])`); |
||||
|
||||
// @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, { log: false }); |
||||
}); |
||||
|
||||
// @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, { log: false }); |
||||
} else { |
||||
// @todo remove `wrap` when possible
|
||||
return e2e().wrap(true, { log: false }); |
||||
} |
||||
}); |
||||
|
||||
// @todo this actually returns type `Cypress.Chainable`
|
||||
const openOptions = (): any => |
||||
isOptionsOpen().then((isOpen: any) => { |
||||
if (!isOpen) { |
||||
e2e.components.PanelEditor.OptionsPane.open().click(); |
||||
} |
||||
}); |
||||
|
||||
// @todo this actually returns type `Cypress.Chainable`
|
||||
const openOptionsGroup = (name: string): any => |
||||
isOptionsGroupOpen(name).then((isOpen: any) => { |
||||
if (!isOpen) { |
||||
toggleOptionsGroup(name); |
||||
} |
||||
}); |
||||
|
||||
const toggleOptionsGroup = (name: string) => |
||||
getOptionsGroup(name) |
||||
.find('.editor-options-group-toggle') |
||||
.click(); |
@ -0,0 +1,9 @@ |
||||
import { configurePanel, ConfigurePanelConfig } from './configurePanel'; |
||||
|
||||
export interface EditPanelConfig extends ConfigurePanelConfig { |
||||
queriesForm?: (config: EditPanelConfig) => void; |
||||
} |
||||
|
||||
// @todo improve config input/output: https://stackoverflow.com/a/63507459/923745
|
||||
// @todo this actually returns type `Cypress.Chainable`
|
||||
export const editPanel = (config: Partial<EditPanelConfig>): any => configurePanel(config, true); |
@ -1,28 +1,13 @@ |
||||
import { addDashboard } from './addDashboard'; |
||||
import { addDataSource } from './addDataSource'; |
||||
import { addPanel } from './addPanel'; |
||||
import { assertSuccessNotification } from './assertSuccessNotification'; |
||||
import { deleteDashboard } from './deleteDashboard'; |
||||
import { deleteDataSource } from './deleteDataSource'; |
||||
import { login } from './login'; |
||||
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, |
||||
addDataSource, |
||||
addPanel, |
||||
assertSuccessNotification, |
||||
deleteDashboard, |
||||
deleteDataSource, |
||||
login, |
||||
openDashboard, |
||||
saveDashboard, |
||||
openPanelMenuItem, |
||||
PanelMenuItems, |
||||
revertAllChanges, |
||||
selectOption, |
||||
}; |
||||
export * from './addDashboard'; |
||||
export * from './addDataSource'; |
||||
export * from './addPanel'; |
||||
export * from './assertSuccessNotification'; |
||||
export * from './deleteDashboard'; |
||||
export * from './deleteDataSource'; |
||||
export * from './editPanel'; |
||||
export * from './login'; |
||||
export * from './openDashboard'; |
||||
export * from './openPanelMenuItem'; |
||||
export * from './revertAllChanges'; |
||||
export * from './saveDashboard'; |
||||
export * from './selectOption'; |
||||
|
Loading…
Reference in new issue