mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
E2E: Add support for data-test-id over aria labels and add importDashboard flow (#36483)
* E2E: Add support for data-testids and not just aria-labels.
This commit is contained in:
@@ -1,3 +1,9 @@
|
||||
// NOTE: by default Component string selectors are set up to be aria-labels,
|
||||
// however there are many cases where your component may not need an aria-label
|
||||
// (a <button> with clear text, for example, does not need an aria-label as it's already labeled)
|
||||
// but you still might need to select it for testing,
|
||||
// in that case please add the attribute data-test-id={selector} in the component and
|
||||
// prefix your selector string with 'data-test-id' so that when create the selectors we know to search for it on the right attribute
|
||||
export const Components = {
|
||||
TimePicker: {
|
||||
openButton: 'TimePicker Open Button',
|
||||
@@ -222,4 +228,12 @@ export const Components = {
|
||||
CodeEditor: {
|
||||
container: 'Code editor container',
|
||||
},
|
||||
DashboardImportPage: {
|
||||
textarea: 'data-testid-import-dashboard-textarea',
|
||||
submit: 'data-testid-load-dashboard',
|
||||
},
|
||||
ImportDashboardForm: {
|
||||
name: 'data-testid-import-dashboard-title',
|
||||
submit: 'data-testid-import-dashboard-submit',
|
||||
},
|
||||
};
|
||||
|
||||
55
packages/grafana-e2e/src/flows/importDashboard.ts
Normal file
55
packages/grafana-e2e/src/flows/importDashboard.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { DeleteDashboardConfig } from '.';
|
||||
import { e2e } from '../index';
|
||||
import { fromBaseUrl, getDashboardUid } from '../support/url';
|
||||
|
||||
type Panel = {
|
||||
title: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
type Dashboard = { title: string; panels: Panel[]; [key: string]: unknown };
|
||||
|
||||
/**
|
||||
* Smoke test a datasource by quickly importing a test dashboard for it
|
||||
* @param dashboardToImport a sample dashboard
|
||||
*/
|
||||
export const importDashboard = (dashboardToImport: Dashboard) => {
|
||||
e2e().visit(fromBaseUrl('/dashboard/import'));
|
||||
|
||||
// Note: normally we'd use 'click' and then 'type' here, but the json object is so big that using 'val' is much faster
|
||||
e2e.components.DashboardImportPage.textarea().click({ force: true }).invoke('val', JSON.stringify(dashboardToImport));
|
||||
e2e.components.DashboardImportPage.submit().click({ force: true });
|
||||
e2e.components.ImportDashboardForm.name().click({ force: true }).clear().type(dashboardToImport.title);
|
||||
e2e.components.ImportDashboardForm.submit().click({ force: true });
|
||||
e2e().wait(3000);
|
||||
|
||||
// save the newly imported dashboard to context so it'll get properly deleted later
|
||||
e2e()
|
||||
.url()
|
||||
.should('contain', '/d/')
|
||||
.then((url: string) => {
|
||||
const uid = getDashboardUid(url);
|
||||
|
||||
e2e.getScenarioContext().then(({ addedDashboards }: { addedDashboards: DeleteDashboardConfig[] }) => {
|
||||
e2e.setScenarioContext({
|
||||
addedDashboards: [...addedDashboards, { title: dashboardToImport.title, uid }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// inspect first panel and verify data has been processed for it
|
||||
e2e.components.Panels.Panel.title(dashboardToImport.panels[0].title).click({ force: true });
|
||||
e2e.components.Panels.Panel.headerItems('Inspect').click({ force: true });
|
||||
e2e.components.Tab.title('JSON').click({ force: true });
|
||||
e2e().wait(3000);
|
||||
e2e.components.PanelInspector.Json.content().contains('Panel JSON').click({ force: true });
|
||||
e2e().wait(3000);
|
||||
e2e.components.Select.option().contains('Data').click({ force: true });
|
||||
e2e().wait(3000);
|
||||
|
||||
// ensures that panel has loaded without knowingly hitting an error
|
||||
// note: this does not prove that data came back as we expected it,
|
||||
// it could get `state: Done` for no data for example
|
||||
// but it ensures we didn't hit a 401 or 500 or something like that
|
||||
e2e.components.CodeEditor.container().contains('"state": "Done"');
|
||||
};
|
||||
@@ -12,6 +12,7 @@ export * from './openPanelMenuItem';
|
||||
export * from './revertAllChanges';
|
||||
export * from './saveDashboard';
|
||||
export * from './selectOption';
|
||||
export * from './importDashboard';
|
||||
|
||||
export {
|
||||
VISUALIZATION_ALERT_LIST,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
export interface SelectorApi {
|
||||
fromAriaLabel: (selector: string) => string;
|
||||
fromDataTestId: (selector: string) => string;
|
||||
fromSelector: (selector: string) => string;
|
||||
}
|
||||
|
||||
export const Selector: SelectorApi = {
|
||||
fromAriaLabel: (selector: string) => `[aria-label="${selector}"]`,
|
||||
fromDataTestId: (selector: string) => `[data-testid="${selector}"]`,
|
||||
fromSelector: (selector: string) => selector,
|
||||
};
|
||||
|
||||
@@ -61,7 +61,11 @@ const processSelectors = <S extends Selectors>(e2eObjects: E2EFunctions<S>, sele
|
||||
// @ts-ignore
|
||||
e2eObjects[key] = (options?: CypressOptions) => {
|
||||
logOutput(value);
|
||||
return e2e().get(Selector.fromAriaLabel(value), options);
|
||||
const selector = value.startsWith('data-testid')
|
||||
? Selector.fromDataTestId(value)
|
||||
: Selector.fromAriaLabel(value);
|
||||
|
||||
return e2e().get(selector, options);
|
||||
};
|
||||
|
||||
continue;
|
||||
@@ -81,8 +85,10 @@ const processSelectors = <S extends Selectors>(e2eObjects: E2EFunctions<S>, sele
|
||||
// the input can be (text) or (options)
|
||||
if (arguments.length === 1) {
|
||||
if (typeof textOrOptions === 'string') {
|
||||
const ariaText = value(textOrOptions);
|
||||
const selector = Selector.fromAriaLabel(ariaText);
|
||||
const selectorText = value(textOrOptions);
|
||||
const selector = selectorText.startsWith('data-testid')
|
||||
? Selector.fromDataTestId(selectorText)
|
||||
: Selector.fromAriaLabel(selectorText);
|
||||
|
||||
logOutput(selector);
|
||||
return e2e().get(selector);
|
||||
@@ -94,9 +100,12 @@ const processSelectors = <S extends Selectors>(e2eObjects: E2EFunctions<S>, sele
|
||||
}
|
||||
|
||||
// the input can only be (text, options)
|
||||
if (arguments.length === 2) {
|
||||
const ariaText = value(textOrOptions as string);
|
||||
const selector = Selector.fromAriaLabel(ariaText);
|
||||
if (arguments.length === 2 && typeof textOrOptions === 'string') {
|
||||
const text = textOrOptions;
|
||||
const selectorText = value(text);
|
||||
const selector = text.startsWith('data-testid')
|
||||
? Selector.fromDataTestId(selectorText)
|
||||
: Selector.fromAriaLabel(selectorText);
|
||||
|
||||
logOutput(selector);
|
||||
return e2e().get(selector, options);
|
||||
|
||||
Reference in New Issue
Block a user