From e4d492fd3580abee02b03493780394c5a2fdc648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 24 Apr 2020 08:48:04 +0200 Subject: [PATCH] e2e: adds inspect drawer tests (#23823) * Explore: Create basic E2E test * Feature: adds e2e tests for panel inspector * Refactor: adds ts-ignore because of type checking errors * Refactor: changes after PR comments and updates snapshot * Refactor: adds typings back for IScope * Refactor: changes after PR comments Co-authored-by: Andreas Opferkuch --- ...moketests.spec.ts => 1-smoketests.spec.ts} | 0 e2e/suite1/specs/explore.spec.ts | 19 +++ e2e/suite1/specs/inspect-drawer.spec.ts | 134 +++++++++++++++++ e2e/suite1/specs/queryVariableCrud.spec.ts | 138 +++++++++--------- packages/grafana-e2e/cypress/support/index.ts | 7 + packages/grafana-e2e/index.js | 2 +- packages/grafana-e2e/src/components/index.ts | 67 +++++++++ packages/grafana-e2e/src/flows/index.ts | 3 + .../src/flows/openPanelMenuItem.ts | 16 ++ packages/grafana-e2e/src/noTypeCheck.ts | 8 +- packages/grafana-e2e/src/pages/explore.ts | 9 ++ packages/grafana-e2e/src/pages/index.ts | 26 +--- .../src/pages/testdata/queryTab.ts | 11 +- packages/grafana-e2e/src/support/scenario.ts | 29 +++- packages/grafana-e2e/src/support/types.ts | 26 +++- .../src/components/Drawer/Drawer.tsx | 30 +++- .../grafana-ui/src/components/Tabs/Tab.tsx | 8 +- .../components/Inspector/InspectDataTab.tsx | 3 +- .../components/Inspector/InspectJSONTab.tsx | 9 +- .../components/Inspector/PanelInspector.tsx | 19 +-- .../components/Inspector/QueryInspector.tsx | 5 +- .../dashboard/panel_editor/EditorTabBody.tsx | 11 +- public/app/features/explore/Explore.tsx | 3 +- public/app/features/explore/RunButton.tsx | 2 + .../__snapshots__/Explore.test.tsx.snap | 1 + .../testdata/partials/query.editor.html | 29 +++- .../plugins/datasource/testdata/query_ctrl.ts | 4 +- 27 files changed, 471 insertions(+), 148 deletions(-) rename e2e/suite1/specs/{smoketests.spec.ts => 1-smoketests.spec.ts} (100%) create mode 100644 e2e/suite1/specs/explore.spec.ts create mode 100644 e2e/suite1/specs/inspect-drawer.spec.ts create mode 100644 packages/grafana-e2e/src/components/index.ts create mode 100644 packages/grafana-e2e/src/flows/openPanelMenuItem.ts create mode 100644 packages/grafana-e2e/src/pages/explore.ts diff --git a/e2e/suite1/specs/smoketests.spec.ts b/e2e/suite1/specs/1-smoketests.spec.ts similarity index 100% rename from e2e/suite1/specs/smoketests.spec.ts rename to e2e/suite1/specs/1-smoketests.spec.ts diff --git a/e2e/suite1/specs/explore.spec.ts b/e2e/suite1/specs/explore.spec.ts new file mode 100644 index 00000000000..3f59095462a --- /dev/null +++ b/e2e/suite1/specs/explore.spec.ts @@ -0,0 +1,19 @@ +import { e2e } from '@grafana/e2e'; + +e2e.scenario({ + describeName: 'Explore', + itName: 'Basic path through Explore.', + addScenarioDataSource: true, + addScenarioDashBoard: false, + skipScenario: false, + scenario: () => { + e2e.pages.Explore.visit(); + e2e.pages.Explore.General.container().should('have.length', 1); + e2e.pages.Explore.General.runButton().should('have.length', 1); + + const canvases = e2e().get('canvas'); + canvases.should('have.length', 2); + + e2e.components.DataSource.TestData.QueryTab.noise().should('have.length', 1); + }, +}); diff --git a/e2e/suite1/specs/inspect-drawer.spec.ts b/e2e/suite1/specs/inspect-drawer.spec.ts new file mode 100644 index 00000000000..97e69c76a14 --- /dev/null +++ b/e2e/suite1/specs/inspect-drawer.spec.ts @@ -0,0 +1,134 @@ +import { e2e } from '@grafana/e2e'; + +const PANEL_UNDER_TEST = '2 yaxis and axis labels'; + +e2e.scenario({ + describeName: 'Inspect drawer tests', + itName: 'Testes various Inpect Drawer scenarios', + addScenarioDataSource: false, + addScenarioDashBoard: false, + skipScenario: false, + scenario: () => { + const viewPortWidth = e2e.config().viewportWidth; + e2e.flows.openDashboard('5SdHCadmz'); + + // testing opening inspect drawer directly by clicking on Inspect in header menu + e2e.flows.openPanelMenuItem(e2e.flows.PanelMenuItems.Inspect, PANEL_UNDER_TEST); + + expectDrawerTabsAndContent(); + + expectDrawerExpandAndContract(viewPortWidth); + + expectDrawerClose(); + + expectSubMenuScenario('Data'); + expectSubMenuScenario('Query'); + expectSubMenuScenario('Panel JSON', 'JSON'); + + e2e.flows.openPanelMenuItem(e2e.flows.PanelMenuItems.Edit, PANEL_UNDER_TEST); + + e2e.components.QueryEditorToolbarItem.button('Query inspector') + .should('be.visible') + .click(); + + e2e.components.Drawer.General.title(PANEL_UNDER_TEST) + .should('be.visible') + .within(() => { + e2e.components.Tab.title('Query').should('be.visible'); + // query should be the active tab + e2e.components.Tab.active().should('have.text', 'Query'); + }); + + e2e.components.PanelInspector.Query.content().should('be.visible'); + }, +}); + +const expectDrawerTabsAndContent = () => { + e2e.components.Drawer.General.title(PANEL_UNDER_TEST) + .should('be.visible') + .within(() => { + e2e.components.Tab.title('Data').should('be.visible'); + // data should be the active tab + e2e.components.Tab.active().within((li: JQuery) => { + expect(li.text()).equals('Data'); + }); + e2e.components.PanelInspector.Data.content().should('be.visible'); + e2e.components.PanelInspector.Stats.content().should('not.be.visible'); + e2e.components.PanelInspector.Json.content().should('not.be.visible'); + e2e.components.PanelInspector.Query.content().should('not.be.visible'); + + // other tabs should also be visible, click on each to see if we get any console errors + e2e.components.Tab.title('Stats') + .should('be.visible') + .click(); + e2e.components.PanelInspector.Stats.content().should('be.visible'); + e2e.components.PanelInspector.Data.content().should('not.be.visible'); + e2e.components.PanelInspector.Json.content().should('not.be.visible'); + e2e.components.PanelInspector.Query.content().should('not.be.visible'); + + e2e.components.Tab.title('JSON') + .should('be.visible') + .click(); + e2e.components.PanelInspector.Json.content().should('be.visible'); + e2e.components.PanelInspector.Data.content().should('not.be.visible'); + e2e.components.PanelInspector.Stats.content().should('not.be.visible'); + e2e.components.PanelInspector.Query.content().should('not.be.visible'); + + e2e.components.Tab.title('Query') + .should('be.visible') + .click(); + e2e.components.PanelInspector.Query.content().should('be.visible'); + e2e.components.PanelInspector.Data.content().should('not.be.visible'); + e2e.components.PanelInspector.Stats.content().should('not.be.visible'); + e2e.components.PanelInspector.Json.content().should('not.be.visible'); + }); +}; + +const expectDrawerClose = () => { + // close using close button + e2e.components.Drawer.General.close().click(); + e2e.components.Drawer.General.title(PANEL_UNDER_TEST).should('not.be.visible'); +}; + +const expectDrawerExpandAndContract = (viewPortWidth: number) => { + // try expand button + // drawer should take up half the screen + e2e.components.Drawer.General.rcContentWrapper() + .should('be.visible') + .should('have.css', 'width', `${viewPortWidth / 2}px`); + + e2e.components.Drawer.General.expand().click(); + e2e.components.Drawer.General.contract().should('be.visible'); + + // drawer should take up the whole screen + e2e.components.Drawer.General.rcContentWrapper() + .should('be.visible') + .should('have.css', 'width', `${viewPortWidth}px`); + + // try contract button + e2e.components.Drawer.General.contract().click(); + e2e.components.Drawer.General.expand().should('be.visible'); + + e2e.components.Drawer.General.rcContentWrapper() + .should('be.visible') + .should('have.css', 'width', `${viewPortWidth / 2}px`); +}; + +const expectSubMenuScenario = (subMenu: string, tabTitle?: string) => { + tabTitle = tabTitle ?? subMenu; + // testing opening inspect drawer from sub menus under Inspect in header menu + e2e.components.Panels.Panel.title(PANEL_UNDER_TEST) + .scrollIntoView() + .should('be.visible') + .click(); + + // sub menus are in the DOM but not visible and because there is no hover support in Cypress force click + // https://github.com/cypress-io/cypress-example-recipes/blob/master/examples/testing-dom__hover-hidden-elements/cypress/integration/hover-hidden-elements-spec.js + e2e.components.Panels.Panel.headerItems(subMenu).click({ force: true }); + + // data should be the default tab + e2e.components.Tab.title(tabTitle).should('be.visible'); + e2e.components.Tab.active().should('have.text', tabTitle); + + expectDrawerClose(); +}; diff --git a/e2e/suite1/specs/queryVariableCrud.spec.ts b/e2e/suite1/specs/queryVariableCrud.spec.ts index 109c1a41991..6817091d1d6 100644 --- a/e2e/suite1/specs/queryVariableCrud.spec.ts +++ b/e2e/suite1/specs/queryVariableCrud.spec.ts @@ -1,5 +1,74 @@ import { e2e } from '@grafana/e2e'; +// This test should really be broken into several smaller tests +e2e.scenario({ + describeName: 'Variables', + itName: 'Query Variables CRUD', + addScenarioDataSource: true, + addScenarioDashBoard: true, + skipScenario: false, + scenario: () => { + // @todo remove `@ts-ignore` when possible + // @ts-ignore + e2e.getScenarioContext().then(({ lastAddedDashboardUid }) => { + e2e.flows.openDashboard(lastAddedDashboardUid); + }); + e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings').click(); + e2e.pages.Dashboard.Settings.General.sectionItems('Variables').click(); + e2e.pages.Dashboard.Settings.Variables.List.addVariableCTA().click(); + + assertDefaultsForNewVariable(); + + e2e.pages.Dashboard.Settings.General.sectionItems('General').click(); + e2e.pages.Dashboard.Settings.General.sectionItems('Variables').click(); + e2e.pages.Dashboard.Settings.Variables.List.addVariableCTA().click(); + + let queryVariables: QueryVariableData[] = [ + { + name: 'query1', + query: '*', + label: 'query1-label', + options: ['All', 'A', 'B', 'C'], + selectedOption: 'A', + }, + { + name: 'query2', + query: '$query1.*', + label: 'query2-label', + options: ['All', 'AA', 'AB', 'AC'], + selectedOption: 'AA', + }, + { + name: 'query3', + query: '$query1.$query2.*', + label: 'query3-label', + options: ['All', 'AAA', 'AAB', 'AAC'], + selectedOption: 'AAA', + }, + ]; + + assertAdding3dependantQueryVariablesScenario(queryVariables); + + // assert select updates + assertSelects(queryVariables); + + // assert that duplicate works + queryVariables = assertDuplicateItem(queryVariables); + + // assert that delete works + queryVariables = assertDeleteItem(queryVariables); + + // assert that update works + queryVariables = assertUpdateItem(queryVariables); + + // assert that move down works + queryVariables = assertMoveDownItem(queryVariables); + + // assert that move up works + assertMoveUpItem(queryVariables); + }, +}); + const assertDefaultsForNewVariable = () => { logSection('Asserting defaults for new variable'); e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInput().within(input => { @@ -550,72 +619,3 @@ const assertMoveUpItem = (data: QueryVariableData[]) => { return queryVariables; }; - -// This test should really be broken into several smaller tests -e2e.scenario({ - describeName: 'Variables', - itName: 'Query Variables CRUD', - addScenarioDataSource: true, - addScenarioDashBoard: true, - skipScenario: false, - scenario: () => { - // @todo remove `@ts-ignore` when possible - // @ts-ignore - e2e.getScenarioContext().then(({ lastAddedDashboardUid }) => { - e2e.flows.openDashboard(lastAddedDashboardUid); - }); - e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings').click(); - e2e.pages.Dashboard.Settings.General.sectionItems('Variables').click(); - e2e.pages.Dashboard.Settings.Variables.List.addVariableCTA().click(); - - assertDefaultsForNewVariable(); - - e2e.pages.Dashboard.Settings.General.sectionItems('General').click(); - e2e.pages.Dashboard.Settings.General.sectionItems('Variables').click(); - e2e.pages.Dashboard.Settings.Variables.List.addVariableCTA().click(); - - let queryVariables: QueryVariableData[] = [ - { - name: 'query1', - query: '*', - label: 'query1-label', - options: ['All', 'A', 'B', 'C'], - selectedOption: 'A', - }, - { - name: 'query2', - query: '$query1.*', - label: 'query2-label', - options: ['All', 'AA', 'AB', 'AC'], - selectedOption: 'AA', - }, - { - name: 'query3', - query: '$query1.$query2.*', - label: 'query3-label', - options: ['All', 'AAA', 'AAB', 'AAC'], - selectedOption: 'AAA', - }, - ]; - - assertAdding3dependantQueryVariablesScenario(queryVariables); - - // assert select updates - assertSelects(queryVariables); - - // assert that duplicate works - queryVariables = assertDuplicateItem(queryVariables); - - // assert that delete works - queryVariables = assertDeleteItem(queryVariables); - - // assert that update works - queryVariables = assertUpdateItem(queryVariables); - - // assert that move down works - queryVariables = assertMoveDownItem(queryVariables); - - // assert that move up works - assertMoveUpItem(queryVariables); - }, -}); diff --git a/packages/grafana-e2e/cypress/support/index.ts b/packages/grafana-e2e/cypress/support/index.ts index c2723ed6250..0ef7bbe7a29 100644 --- a/packages/grafana-e2e/cypress/support/index.ts +++ b/packages/grafana-e2e/cypress/support/index.ts @@ -23,3 +23,10 @@ if (Cypress.env('SLOWMO')) { }); } } + +// uncomment below to prevent Cypress from failing tests when unhandled errors are thrown +// Cypress.on('uncaught:exception', (err, runnable) => { +// // returning false here prevents Cypress from +// // failing the test +// return false; +// }); diff --git a/packages/grafana-e2e/index.js b/packages/grafana-e2e/index.js index d1a4363350e..5d1c92525d2 100644 --- a/packages/grafana-e2e/index.js +++ b/packages/grafana-e2e/index.js @@ -1,4 +1,4 @@ -'use strict' +'use strict'; if (process.env.NODE_ENV === 'production') { module.exports = require('./index.production.js'); diff --git a/packages/grafana-e2e/src/components/index.ts b/packages/grafana-e2e/src/components/index.ts new file mode 100644 index 00000000000..8ea8cdd2132 --- /dev/null +++ b/packages/grafana-e2e/src/components/index.ts @@ -0,0 +1,67 @@ +import { TestData } from '../pages/testdata'; +import { Panel } from '../pages/panel'; +import { EditPanel } from '../pages/editPanel'; +import { Graph } from '../pages/graph'; +import { componentFactory } from '../support'; + +export const Components = { + DataSource: { + TestData, + }, + Panels: { + Panel, + EditPanel, + Visualization: { + Graph, + }, + }, + Drawer: { + General: componentFactory({ + selectors: { + title: (title: string) => `Drawer title ${title}`, + expand: 'Drawer expand', + contract: 'Drawer contract', + close: 'Drawer close', + rcContentWrapper: () => '.drawer-content-wrapper', + }, + }), + }, + PanelInspector: { + Data: componentFactory({ + selectors: { + content: 'Panel inspector Data content', + }, + }), + Stats: componentFactory({ + selectors: { + content: 'Panel inspector Stats content', + }, + }), + Json: componentFactory({ + selectors: { + content: 'Panel inspector Json content', + }, + }), + Query: componentFactory({ + selectors: { + content: 'Panel inspector Query content', + }, + }), + }, + Tab: componentFactory({ + selectors: { + title: (title: string) => `Tab ${title}`, + active: () => '[class*="-activeTabStyle"]', + }, + }), + QueryEditorToolbarItem: componentFactory({ + selectors: { + button: (title: string) => `QueryEditor toolbar item button ${title}`, + }, + }), + BackButton: componentFactory({ + selectors: { + backArrow: 'Go Back button', + }, + }), +}; diff --git a/packages/grafana-e2e/src/flows/index.ts b/packages/grafana-e2e/src/flows/index.ts index 4cad138cfa1..0c2e63aa14b 100644 --- a/packages/grafana-e2e/src/flows/index.ts +++ b/packages/grafana-e2e/src/flows/index.ts @@ -8,6 +8,7 @@ import { login } from './login'; import { openDashboard } from './openDashboard'; import { saveDashboard } from './saveDashboard'; import { saveNewDashboard } from './saveNewDashboard'; +import { openPanelMenuItem, PanelMenuItems } from './openPanelMenuItem'; export const Flows = { addDashboard, @@ -20,4 +21,6 @@ export const Flows = { openDashboard, saveDashboard, saveNewDashboard, + openPanelMenuItem, + PanelMenuItems, }; diff --git a/packages/grafana-e2e/src/flows/openPanelMenuItem.ts b/packages/grafana-e2e/src/flows/openPanelMenuItem.ts new file mode 100644 index 00000000000..2bc253de888 --- /dev/null +++ b/packages/grafana-e2e/src/flows/openPanelMenuItem.ts @@ -0,0 +1,16 @@ +import { e2e } from '../noTypeCheck'; + +export enum PanelMenuItems { + Edit = 'Edit', + Inspect = 'Inspect', +} + +export const openPanelMenuItem = (menu: PanelMenuItems, panelTitle = 'Panel Title') => { + e2e.components.Panels.Panel.title(panelTitle) + .should('be.visible') + .click(); + + e2e.components.Panels.Panel.headerItems(menu) + .should('be.visible') + .click(); +}; diff --git a/packages/grafana-e2e/src/noTypeCheck.ts b/packages/grafana-e2e/src/noTypeCheck.ts index 837b7152055..6754d61f0c9 100644 --- a/packages/grafana-e2e/src/noTypeCheck.ts +++ b/packages/grafana-e2e/src/noTypeCheck.ts @@ -4,15 +4,13 @@ // toBe, toEqual and so forth. That's why this file is not type checked and will be so until we // can solve the above mentioned issue with Cypress/Jest. import { e2eScenario, ScenarioArguments } from './support/scenario'; -import { Pages, Components } from './pages'; +import { Pages } from './pages'; +import { Components } from './components'; import { Flows } from './flows'; import { getScenarioContext, setScenarioContext } from './support/scenarioContext'; export type SelectorFunction = (text?: string) => Cypress.Chainable>; -export type SelectorObject = { - visit: (args?: string) => Cypress.Chainable; - selectors: S; -}; +export type VisitFunction = (args?: string) => Cypress.Chainable; const e2eObject = { env: (args: string) => Cypress.env(args), diff --git a/packages/grafana-e2e/src/pages/explore.ts b/packages/grafana-e2e/src/pages/explore.ts new file mode 100644 index 00000000000..f8223797334 --- /dev/null +++ b/packages/grafana-e2e/src/pages/explore.ts @@ -0,0 +1,9 @@ +import { pageFactory } from '../support'; + +export const Explore = pageFactory({ + url: '/explore', + selectors: { + container: 'Explore', + runButton: 'Run button', + }, +}); diff --git a/packages/grafana-e2e/src/pages/index.ts b/packages/grafana-e2e/src/pages/index.ts index f9b068dad7d..54067081345 100644 --- a/packages/grafana-e2e/src/pages/index.ts +++ b/packages/grafana-e2e/src/pages/index.ts @@ -8,14 +8,10 @@ import { Dashboard } from './dashboard'; import { SaveDashboardAsModal } from './saveDashboardAsModal'; import { Dashboards } from './dashboards'; import { DashboardSettings } from './dashboardSettings'; -import { EditPanel } from './editPanel'; -import { TestData } from './testdata'; -import { Graph } from './graph'; +import { Explore } from './explore'; import { SaveDashboardModal } from './saveDashboardModal'; -import { Panel } from './panel'; import { SharePanelModal } from './sharePanelModal'; import { ConstantVariable, QueryVariable, VariableGeneral, Variables, VariablesSubMenu } from './variables'; -import { pageFactory } from '../support'; export const Pages = { Login, @@ -44,22 +40,8 @@ export const Pages = { SaveDashboardAsModal, SaveDashboardModal, SharePanelModal, -}; - -export const Components = { - DataSource: { - TestData, + Explore: { + visit: () => Explore.visit(), + General: Explore, }, - Panels: { - Panel, - EditPanel, - Visualization: { - Graph, - }, - }, - BackButton: pageFactory({ - selectors: { - backArrow: 'Go Back button', - }, - }), }; diff --git a/packages/grafana-e2e/src/pages/testdata/queryTab.ts b/packages/grafana-e2e/src/pages/testdata/queryTab.ts index 171f365e241..dd8f6afe41e 100644 --- a/packages/grafana-e2e/src/pages/testdata/queryTab.ts +++ b/packages/grafana-e2e/src/pages/testdata/queryTab.ts @@ -1,8 +1,13 @@ -import { pageFactory } from '../../support'; +import { componentFactory } from '../../support'; -export const QueryTab = pageFactory({ - url: '', +export const QueryTab = componentFactory({ selectors: { scenarioSelect: 'Test Data Query scenario select', + max: 'TestData max', + min: 'TestData min', + noise: 'TestData noise', + seriesCount: 'TestData series count', + spread: 'TestData spread', + startValue: 'TestData start value', }, }); diff --git a/packages/grafana-e2e/src/support/scenario.ts b/packages/grafana-e2e/src/support/scenario.ts index 2c6f5bec7f4..33baee3e469 100644 --- a/packages/grafana-e2e/src/support/scenario.ts +++ b/packages/grafana-e2e/src/support/scenario.ts @@ -1,4 +1,5 @@ -import { e2e } from '../index'; +import { Flows } from '../flows'; +import { getScenarioContext } from './scenarioContext'; export interface ScenarioArguments { describeName: string; @@ -17,34 +18,48 @@ export const e2eScenario = ({ addScenarioDataSource = false, addScenarioDashBoard = false, }: ScenarioArguments) => { + // when we started to use import { e2e } from '@grafana/e2e'; in grafana/ui components + // then type checking @grafana/run-time started to fail with + // Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i @types/jest` or `npm i @types/mocha`. + // Haven't investigated deeper why this happens yet so adding ts-ignore as temporary solution + // @todo remove `@ts-ignore` when possible + // @ts-ignore describe(describeName, () => { if (skipScenario) { + // @todo remove `@ts-ignore` when possible + // @ts-ignore it.skip(itName, () => scenario()); } else { + // @todo remove `@ts-ignore` when possible + // @ts-ignore beforeEach(() => { - e2e.flows.login('admin', 'admin'); + Flows.login('admin', 'admin'); if (addScenarioDataSource) { - e2e.flows.addDataSource(); + Flows.addDataSource(); } if (addScenarioDashBoard) { - e2e.flows.addDashboard(); + Flows.addDashboard(); } }); + // @todo remove `@ts-ignore` when possible + // @ts-ignore afterEach(() => { // @todo remove `@ts-ignore` when possible // @ts-ignore - e2e.getScenarioContext().then(({ lastAddedDashboardUid, lastAddedDataSource }) => { + getScenarioContext().then(({ lastAddedDashboardUid, lastAddedDataSource }) => { if (lastAddedDataSource) { - e2e.flows.deleteDataSource(lastAddedDataSource); + Flows.deleteDataSource(lastAddedDataSource); } if (lastAddedDashboardUid) { - e2e.flows.deleteDashboard(lastAddedDashboardUid); + Flows.deleteDashboard(lastAddedDashboardUid); } }); }); + // @todo remove `@ts-ignore` when possible + // @ts-ignore it(itName, () => scenario()); } }); diff --git a/packages/grafana-e2e/src/support/types.ts b/packages/grafana-e2e/src/support/types.ts index c3a5801178e..e5417a25569 100644 --- a/packages/grafana-e2e/src/support/types.ts +++ b/packages/grafana-e2e/src/support/types.ts @@ -1,17 +1,21 @@ import { Selector } from './selector'; import { fromBaseUrl } from './url'; import { e2e } from '../index'; -import { SelectorFunction, SelectorObject } from '../noTypeCheck'; +import { SelectorFunction, VisitFunction } from '../noTypeCheck'; export type Selectors = Record; -export type PageObjects = { [P in keyof S]: SelectorFunction }; -export type PageFactory = PageObjects & SelectorObject; -export interface PageFactoryArgs { - url?: string | Function; +export type SelectorFunctions = { [P in keyof S]: SelectorFunction }; + +export type Page = SelectorFunctions & { selectors: S; + visit: VisitFunction; +}; +export interface PageFactoryArgs { + selectors: S; + url?: string | Function; } -export const pageFactory = ({ url, selectors }: PageFactoryArgs): PageFactory => { +export const pageFactory = ({ url, selectors }: PageFactoryArgs): Page => { const visit = (args?: string) => { if (!url) { return e2e().visit(''); @@ -29,7 +33,7 @@ export const pageFactory = ({ url, selectors }: PageFactory e2e().logToConsole('Visiting', parsedUrl); return e2e().visit(parsedUrl); }; - const pageObjects: PageObjects = {} as PageObjects; + const pageObjects: SelectorFunctions = {} as SelectorFunctions; const keys = Object.keys(selectors); keys.forEach(key => { @@ -62,3 +66,11 @@ export const pageFactory = ({ url, selectors }: PageFactory selectors, }; }; + +type Component = Omit, 'visit'>; +type ComponentFactoryArgs = Omit, 'url'>; + +export const componentFactory = (args: ComponentFactoryArgs): Component => { + const { visit, ...rest } = pageFactory(args); + return rest; +}; diff --git a/packages/grafana-ui/src/components/Drawer/Drawer.tsx b/packages/grafana-ui/src/components/Drawer/Drawer.tsx index 526f0b6afc1..b028e17a046 100644 --- a/packages/grafana-ui/src/components/Drawer/Drawer.tsx +++ b/packages/grafana-ui/src/components/Drawer/Drawer.tsx @@ -5,6 +5,7 @@ import { css } from 'emotion'; import CustomScrollbar from '../CustomScrollbar/CustomScrollbar'; import { IconButton } from '../IconButton/IconButton'; import { stylesFactory, useTheme } from '../../themes'; +import { e2e } from '@grafana/e2e'; export interface Props { children: ReactNode; @@ -93,17 +94,40 @@ export const Drawer: FC = ({ getContainer={inline ? false : 'body'} style={{ position: `${inline && 'absolute'}` } as CSSProperties} className={drawerStyles.drawer} + aria-label={ + typeof title === 'string' + ? e2e.components.Drawer.General.selectors.title(title) + : e2e.components.Drawer.General.selectors.title('no title') + } > {typeof title === 'string' && (
{expandable && !isExpanded && ( - setIsExpanded(true)} surface="header" /> + setIsExpanded(true)} + surface="header" + aria-label={e2e.components.Drawer.General.selectors.expand} + /> )} {expandable && isExpanded && ( - setIsExpanded(false)} surface="header" /> + setIsExpanded(false)} + surface="header" + aria-label={e2e.components.Drawer.General.selectors.contract} + /> )} - +

{title}

diff --git a/packages/grafana-ui/src/components/Tabs/Tab.tsx b/packages/grafana-ui/src/components/Tabs/Tab.tsx index a1bcdd20501..32042730c4c 100644 --- a/packages/grafana-ui/src/components/Tabs/Tab.tsx +++ b/packages/grafana-ui/src/components/Tabs/Tab.tsx @@ -5,6 +5,7 @@ import { Icon } from '../Icon/Icon'; import { IconName } from '../../types'; import { stylesFactory, useTheme } from '../../themes'; import { Counter } from './Counter'; +import { e2e } from '@grafana/e2e'; export interface TabProps { label: string; @@ -40,6 +41,7 @@ const getTabStyles = stylesFactory((theme: GrafanaTheme) => { } `, activeStyle: css` + label: activeTabStyle; border-color: ${theme.palette.orange} ${colors.pageHeaderBorder} transparent; background: ${colors.bodyBg}; color: ${colors.link}; @@ -64,7 +66,11 @@ export const Tab: FC = ({ label, active, icon, onChangeTab, counter }) const tabsStyles = getTabStyles(theme); return ( -
  • +
  • {icon && } {label} {typeof counter === 'number' && } diff --git a/public/app/features/dashboard/components/Inspector/InspectDataTab.tsx b/public/app/features/dashboard/components/Inspector/InspectDataTab.tsx index e63686b5dcd..17743a8d131 100644 --- a/public/app/features/dashboard/components/Inspector/InspectDataTab.tsx +++ b/public/app/features/dashboard/components/Inspector/InspectDataTab.tsx @@ -13,6 +13,7 @@ import { config } from 'app/core/config'; import AutoSizer from 'react-virtualized-auto-sizer'; import { saveAs } from 'file-saver'; import { cx } from 'emotion'; +import { e2e } from '@grafana/e2e'; interface Props { data: DataFrame[]; @@ -108,7 +109,7 @@ export class InspectDataTab extends PureComponent { }); return ( -
    +
    diff --git a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx index 88045d2b9f0..6145057083c 100644 --- a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx +++ b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx @@ -6,25 +6,26 @@ import { InspectJSONTab } from './InspectJSONTab'; import { QueryInspector } from './QueryInspector'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; -import { JSONFormatter, Drawer, TabContent, CustomScrollbar } from '@grafana/ui'; -import { getLocationSrv, getDataSourceSrv } from '@grafana/runtime'; +import { CustomScrollbar, Drawer, JSONFormatter, TabContent } from '@grafana/ui'; +import { getDataSourceSrv, getLocationSrv } from '@grafana/runtime'; import { DataFrame, - DataSourceApi, - SelectableValue, - getDisplayProcessor, DataQueryError, - PanelData, + DataSourceApi, FieldType, formattedValueToString, - QueryResultMetaStat, + getDisplayProcessor, LoadingState, + PanelData, PanelPlugin, + QueryResultMetaStat, + SelectableValue, } from '@grafana/data'; import { config } from 'app/core/config'; import { getPanelInspectorStyles } from './styles'; import { StoreState } from 'app/types'; import { InspectDataTab } from './InspectDataTab'; +import { e2e } from '@grafana/e2e'; interface OwnProps { dashboard: DashboardModel; @@ -222,10 +223,10 @@ export class PanelInspectorUnconnected extends PureComponent { } return ( - <> +
    {this.renderStatsTable('Stats', stats)} {this.renderStatsTable('Data source stats', dataStats)} - +
    ); } diff --git a/public/app/features/dashboard/components/Inspector/QueryInspector.tsx b/public/app/features/dashboard/components/Inspector/QueryInspector.tsx index 1f125ed078b..1050498d5a0 100644 --- a/public/app/features/dashboard/components/Inspector/QueryInspector.tsx +++ b/public/app/features/dashboard/components/Inspector/QueryInspector.tsx @@ -1,11 +1,12 @@ import React, { PureComponent } from 'react'; import appEvents from 'app/core/app_events'; import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard'; -import { JSONFormatter, LoadingPlaceholder, Button } from '@grafana/ui'; +import { Button, JSONFormatter, LoadingPlaceholder } from '@grafana/ui'; import { CoreEvents } from 'app/types'; import { AppEvents, PanelEvents } from '@grafana/data'; import { PanelModel } from 'app/features/dashboard/state'; import { getPanelInspectorStyles } from './styles'; +import { e2e } from '@grafana/e2e'; interface DsQuery { isLoading: boolean; @@ -188,7 +189,7 @@ export class QueryInspector extends PureComponent { return ( <> -
    +

    Query inspector

    Query inspector allows you to view raw request and response. To collect this data Grafana needs to issue a diff --git a/public/app/features/dashboard/panel_editor/EditorTabBody.tsx b/public/app/features/dashboard/panel_editor/EditorTabBody.tsx index 1767d0939b9..d4e65177592 100644 --- a/public/app/features/dashboard/panel_editor/EditorTabBody.tsx +++ b/public/app/features/dashboard/panel_editor/EditorTabBody.tsx @@ -1,9 +1,9 @@ // Libraries import React, { PureComponent } from 'react'; - // Components -import { CustomScrollbar, PanelOptionsGroup, Icon, IconName } from '@grafana/ui'; +import { CustomScrollbar, Icon, IconName, PanelOptionsGroup } from '@grafana/ui'; import { FadeIn } from 'app/core/components/Animations/FadeIn'; +import { e2e } from '@grafana/e2e'; interface Props { children: JSX.Element; @@ -89,7 +89,12 @@ export class EditorTabBody extends PureComponent { return (

    -
    diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index e98051ae888..44565d708eb 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -63,6 +63,7 @@ import { ExploreGraphPanel } from './ExploreGraphPanel'; import { TraceView } from './TraceView/TraceView'; import { SecondaryActions } from './SecondaryActions'; import { compose } from 'redux'; +import { e2e } from '@grafana/e2e'; const getStyles = stylesFactory((theme: GrafanaTheme) => { return { @@ -314,7 +315,7 @@ export class Explore extends React.PureComponent { const queryError = getFirstNonQueryRowSpecificError(queryErrors); return ( -
    +
    {datasourceMissing ? this.renderEmptyState() : null} {datasourceInstance && ( diff --git a/public/app/features/explore/RunButton.tsx b/public/app/features/explore/RunButton.tsx index 33fa1982b60..5c3f28832d0 100644 --- a/public/app/features/explore/RunButton.tsx +++ b/public/app/features/explore/RunButton.tsx @@ -3,6 +3,7 @@ import { RefreshPicker } from '@grafana/ui'; import memoizeOne from 'memoize-one'; import { css } from 'emotion'; import classNames from 'classnames'; +import { e2e } from '@grafana/e2e'; import { ResponsiveButton } from './ResponsiveButton'; @@ -42,6 +43,7 @@ export function RunButton(props: Props) { })} icon={loading ? 'fa fa-spinner' : 'sync'} iconClassName={loading && ' fa-spin run-icon'} + aria-label={e2e.pages.Explore.General.selectors.runButton} /> ); diff --git a/public/app/features/explore/__snapshots__/Explore.test.tsx.snap b/public/app/features/explore/__snapshots__/Explore.test.tsx.snap index 2f20a32111d..c6831908971 100644 --- a/public/app/features/explore/__snapshots__/Explore.test.tsx.snap +++ b/public/app/features/explore/__snapshots__/Explore.test.tsx.snap @@ -2,6 +2,7 @@ exports[`Explore should render component 1`] = `
    + ng-change="ctrl.refresh()" + aria-label="{{::ctrl.selectors.seriesCount}}" + />
    @@ -66,7 +68,9 @@ placeholder="auto" ng-model="ctrl.target.startValue" step="1" - ng-change="ctrl.refresh()" /> + ng-change="ctrl.refresh()" + aria-label="{{::ctrl.selectors.startValue}}" + />
    @@ -76,7 +80,9 @@ ng-model="ctrl.target.spread" min="0.5" step="0.1" - ng-change="ctrl.refresh()" /> + ng-change="ctrl.refresh()" + aria-label="{{::ctrl.selectors.spread}}" + />
    @@ -86,7 +92,9 @@ ng-model="ctrl.target.noise" min="0" step="0.1" - ng-change="ctrl.refresh()" /> + ng-change="ctrl.refresh()" + aria-label="{{::ctrl.selectors.noise}}" + />
    @@ -95,7 +103,9 @@ placeholder="none" ng-model="ctrl.target.min" step="0.1" - ng-change="ctrl.refresh()" /> + ng-change="ctrl.refresh()" + aria-label="{{::ctrl.selectors.min}}" + />
    @@ -104,7 +114,9 @@ placeholder="none" ng-model="ctrl.target.max" step="0.1" - ng-change="ctrl.refresh()" /> + ng-change="ctrl.refresh()" + aria-label="{{::ctrl.selectors.max}}" + />
    @@ -127,7 +139,8 @@ ng-model="ctrl.target.stream.speed" min="10" step="10" - ng-change="ctrl.streamChanged()" /> + ng-change="ctrl.streamChanged()" + />
    @@ -188,7 +201,7 @@
    - +
    diff --git a/public/app/plugins/datasource/testdata/query_ctrl.ts b/public/app/plugins/datasource/testdata/query_ctrl.ts index 48bfa3bb605..7b1f89d0fb6 100644 --- a/public/app/plugins/datasource/testdata/query_ctrl.ts +++ b/public/app/plugins/datasource/testdata/query_ctrl.ts @@ -1,12 +1,12 @@ import _ from 'lodash'; +import { IScope } from 'angular'; +import { getBackendSrv } from '@grafana/runtime'; import { dateMath, dateTime } from '@grafana/data'; import { e2e } from '@grafana/e2e'; import { QueryCtrl } from 'app/plugins/sdk'; import { defaultQuery } from './runStreams'; -import { getBackendSrv } from '@grafana/runtime'; import { promiseToDigest } from 'app/core/utils/promiseToDigest'; -import { IScope } from 'angular'; export const defaultPulse: any = { timeStep: 60,