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:
Sarah Zinger
2021-07-19 14:01:31 -04:00
committed by GitHub
parent ef689d0c24
commit f32d200fc0
9 changed files with 255 additions and 7 deletions

View File

@@ -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',
},
};

View 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"');
};

View File

@@ -12,6 +12,7 @@ export * from './openPanelMenuItem';
export * from './revertAllChanges';
export * from './saveDashboard';
export * from './selectOption';
export * from './importDashboard';
export {
VISUALIZATION_ALERT_LIST,

View File

@@ -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,
};

View File

@@ -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);