e2e: creates a separate package for selectors (#23858)

* Initial commit

* Chore: fixes after merge

* Chore: removes todos

* Chore: uncomment test

* Chore: adds missing externals to rollup config

* Refactor: selectors is master for everything

* Docs: updates Docs

* Chore: adds e2e-selectors to publish
This commit is contained in:
Hugo Häggmark 2020-04-27 09:09:05 +02:00 committed by GitHub
parent 9ac7263e66
commit b09b49fb37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
105 changed files with 691 additions and 760 deletions

View File

@ -26,7 +26,8 @@ All the integration tests are located at `e2e/suite<x>/specs`. The page objects
Here is a good introduction to e2e best practices: https://martinfowler.com/bliki/PageObject.html.
- `Selector`: A unique identifier that is used from the e2e framework to retrieve an element from the Browser
- `Page`: An abstraction for an object that contains one or more `Selectors`
- `Page`: An abstraction for an object that contains one or more `Selectors` with `visit` function to navigate to the page.
- `Component`: An abstraction for an object that contains one or more `Selectors` but without `visit` function
- `Flow`: An abstraction that contains a sequence of actions on one or more `Pages` that can be reused and shared between tests
## Basic example
@ -57,15 +58,13 @@ Now that we added the `aria-label` we suddenly get more information about this p
The next step is to create a `Page` representation in our e2e test framework to glue the test with the real implementation using the `pageFactory` function. For that function we can supply a `url` and `selectors` like in the example below:
```typescript
export const Login = pageFactory({
export const Login = {
url: "/login", // used when called from Login.visit()
selectors: {
username: "Username input field", // used when called from Login.username().type('Hello World')
},
});
username: "Username input field", // used when called from Login.username().type('Hello World')
};
```
The next step is to add the `Login` page to the exported const `Pages` in `packages/grafana-e2e/src/pages/index.ts` so that it appears when we type `e2e.pages` in our IDE.
The next step is to add the `Login` page to the exported const `Pages` in `packages/grafana-e2e-selectors/src/selectors/pages.ts` so that it appears when we type `e2e.pages` in our IDE.
```ecmascript 6
export const Pages = {
@ -81,15 +80,15 @@ Now that we have a `Page` called `Login` in our `Pages` const we can use that to
```jsx harmony
<div>
<input type="text" className="gf-form-input login-form-input" aria-label={e2e.pages.Login.selectors.username} />
<input type="text" className="gf-form-input login-form-input" aria-label={selectors.pages.Login.username} />
</div>
```
The last step in our example is to use our `Login` page as part of a test. The `pageFactory` function we used before gives us two things:
The last step in our example is to use our `Login` page as part of a test.
- The `url` property is used whenever we call the `visit` function and is equivalent to the Cypress function [cy.visit()](https://docs.cypress.io/api/commands/visit.html#Syntax).
> Best practice after calling `visit` is to always call `should` on a selector to prevent flaky tests when you try to access an element that isn't ready. For more information, refer to [Commands vs. assertions](https://docs.cypress.io/guides/core-concepts/retry-ability.html#Commands-vs-assertions).
- Any defined selector in the `selectors` property can be accessed from the `Login` page by invoking it. This is equivalent to the result of the Cypress function [cy.get(...)](https://docs.cypress.io/api/commands/get.html#Syntax).
- Any defined selector can be accessed from the `Login` page by invoking it. This is equivalent to the result of the Cypress function [cy.get(...)](https://docs.cypress.io/api/commands/get.html#Syntax).
```ecmascript 6
describe('Login test', () => {
@ -123,17 +122,15 @@ Let's take a look at an example that uses the same `selector` for multiple items
Just as before in the basic example we'll start by creating a page abstraction using the `pageFactory` function:
```typescript
export const DataSources = pageFactory({
export const DataSources = {
url: '/datasources',
selectors: {
dataSources: (dataSourceName: string) => `Data source list item ${dataSourceName}`,
},
});
dataSources: (dataSourceName: string) => `Data source list item ${dataSourceName}`,
};
````
You might have noticed that instead of a simple `string` as the `selector`, we're using a `function` that takes a string parameter as an argument and returns a formatted string using the argument.
Just as before we need to add the `DataSources` page to the exported const `Pages` in `packages/grafana-e2e/src/pages/index.ts`.
Just as before we need to add the `DataSources` page to the exported const `Pages` in `packages/grafana-e2e-selectors/src/selectors/pages.ts`.
The next step is to use the `dataSources` selector function as in our example below:
@ -142,7 +139,7 @@ The next step is to use the `dataSources` selector function as in our example be
{dataSources.map(dataSource => (
<li className="card-item-wrapper" key={dataSource.id}>
<a className="card-item" href={`datasources/edit/${dataSource.id}`}>
<div className="card-item-name" aria-label={e2e.pages.DataSources.selectors.dataSources(dataSource.name)}>
<div className="card-item-name" aria-label={selectors.pages.DataSources.dataSources(dataSource.name)}>
{dataSource.name}
</div>
</a>

View File

@ -7,9 +7,7 @@ e2e.scenario({
addScenarioDashBoard: true,
skipScenario: false,
scenario: () => {
// @todo remove `@ts-ignore` when possible
// @ts-ignore
e2e.getScenarioContext().then(({ lastAddedDashboardUid }) => {
e2e.getScenarioContext().then(({ lastAddedDashboardUid }: any) => {
e2e.flows.openDashboard(lastAddedDashboardUid);
});
e2e.pages.Dashboard.Toolbar.toolbarItems('Add panel').click();

View File

@ -8,9 +8,7 @@ e2e.scenario({
addScenarioDashBoard: true,
skipScenario: false,
scenario: () => {
// @todo remove `@ts-ignore` when possible
// @ts-ignore
e2e.getScenarioContext().then(({ lastAddedDashboardUid }) => {
e2e.getScenarioContext().then(({ lastAddedDashboardUid }: any) => {
e2e.flows.openDashboard(lastAddedDashboardUid);
});
e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings').click();
@ -253,9 +251,7 @@ const assertAdding3dependantQueryVariablesScenario = (queryVariables: QueryVaria
for (let queryVariableIndex = 0; queryVariableIndex < queryVariables.length; queryVariableIndex++) {
const { name, label, query, options, selectedOption } = queryVariables[queryVariableIndex];
const asserts = queryVariables.slice(0, queryVariableIndex + 1);
// @todo remove `@ts-ignore` when possible
// @ts-ignore
e2e.getScenarioContext().then(({ lastAddedDataSource }) => {
e2e.getScenarioContext().then(({ lastAddedDataSource }: any) => {
createQueryVariable({
dataSourceName: lastAddedDataSource,
name,

View File

@ -0,0 +1,2 @@

View File

@ -0,0 +1,3 @@
# Grafana End-to-End Test Selectors library
> **@grafana/e2e-selectors is currently in ALPHA**. Core API is unstable and can be a subject of breaking changes!

View File

@ -0,0 +1,3 @@
{
"extends": "../../api-extractor.json"
}

View File

@ -0,0 +1,7 @@
'use strict';
if (process.env.NODE_ENV === 'production') {
module.exports = require('./index.production.js');
} else {
module.exports = require('./index.development.js');
}

View File

@ -0,0 +1,50 @@
{
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/e2e-selectors",
"version": "7.0.0-pre.0",
"description": "Grafana End-to-End Test Selectors Library",
"keywords": [
"cli",
"grafana",
"e2e",
"typescript"
],
"repository": {
"type": "git",
"url": "http://github.com/grafana/grafana.git",
"directory": "packages/grafana-e2e-selectors"
},
"main": "src/index.ts",
"scripts": {
"build": "grafana-toolkit package:build --scope=e2e-selectors",
"bundle": "rollup -c rollup.config.ts",
"clean": "rimraf ./dist ./compiled",
"docsExtract": "mkdir -p ../../reports/docs && api-extractor run 2>&1 | tee ../../reports/docs/$(basename $(pwd)).log",
"lint": "eslint src/ --ext=.js,.ts,.tsx",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@types/node": "13.7.7",
"@rollup/plugin-commonjs": "11.0.2",
"@rollup/plugin-node-resolve": "7.1.1",
"@types/rollup-plugin-visualizer": "2.6.0",
"@types/systemjs": "^0.20.6",
"pretty-format": "25.1.0",
"rollup": "2.0.6",
"rollup-plugin-sourcemaps": "0.5.0",
"rollup-plugin-terser": "5.3.0",
"rollup-plugin-typescript2": "0.26.0",
"rollup-plugin-visualizer": "3.3.1",
"ts-loader": "6.2.1",
"ts-node": "8.8.1"
},
"types": "src/index.ts",
"dependencies": {
"@grafana/tsconfig": "^1.0.0-rc1",
"commander": "5.0.0",
"execa": "4.0.0",
"typescript": "3.7.5",
"yaml": "^1.8.3"
}
}

View File

@ -0,0 +1,25 @@
import resolve from '@rollup/plugin-node-resolve';
import sourceMaps from 'rollup-plugin-sourcemaps';
import { terser } from 'rollup-plugin-terser';
const pkg = require('./package.json');
const libraryName = pkg.name;
const buildCjsPackage = ({ env }) => {
return {
input: `compiled/index.js`,
output: [
{
file: `dist/index.${env}.js`,
name: libraryName,
format: 'cjs',
sourcemap: true,
exports: 'named',
globals: {},
},
],
plugins: [resolve(), sourceMaps(), env === 'production' && terser()],
};
};
export default [buildCjsPackage({ env: 'development' }), buildCjsPackage({ env: 'production' })];

View File

@ -0,0 +1,7 @@
/**
* A library containing the different design components of the Grafana ecosystem.
*
* @packageDocumentation
*/
export * from './selectors';
export * from './types';

View File

@ -0,0 +1,108 @@
import { Pages } from './pages';
export const Components = {
DataSource: {
TestData: {
QueryTab: {
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',
},
},
},
Panels: {
Panel: {
title: (title: string) => `Panel header title item ${title}`,
headerItems: (item: string) => `Panel header item ${item}`,
},
Visualization: {
Graph: {
VisualizationTab: {
legendSection: 'Legend section',
},
Legend: {
legendItemAlias: (name: string) => `gpl alias ${name}`,
showLegendSwitch: 'gpl show legend',
},
},
},
},
Drawer: {
General: {
title: (title: string) => `Drawer title ${title}`,
expand: 'Drawer expand',
contract: 'Drawer contract',
close: 'Drawer close',
rcContentWrapper: () => '.drawer-content-wrapper',
},
},
PanelEditor: {
General: {
content: 'Panel editor content',
},
OptionsPane: {
content: 'Panel editor option pane content',
close: Pages.Dashboard.Toolbar.toolbarItems('Close options pane'),
open: Pages.Dashboard.Toolbar.toolbarItems('Open options pane'),
select: 'Panel editor option pane select',
},
// not sure about the naming *DataPane*
DataPane: {
content: 'Panel editor data pane content',
},
},
PanelInspector: {
Data: {
content: 'Panel inspector Data content',
},
Stats: {
content: 'Panel inspector Stats content',
},
Json: {
content: 'Panel inspector Json content',
},
Query: {
content: 'Panel inspector Query content',
},
},
Tab: {
title: (title: string) => `Tab ${title}`,
active: () => '[class*="-activeTabStyle"]',
},
QueryTab: {
content: 'Query editor tab content',
queryInspectorButton: 'Query inspector button',
},
AlertTab: {
content: 'Alert editor tab content',
},
TransformTab: {
content: 'Transform editor tab content',
},
QueryEditorToolbarItem: {
button: (title: string) => `QueryEditor toolbar item button ${title}`,
},
BackButton: {
backArrow: 'Go Back button',
},
OptionsGroup: {
toggle: (title: string) => `Options group ${title}`,
},
PluginVisualization: {
item: (title: string) => `Plugin visualization item ${title}`,
current: () => '[class*="-currentVisualizationItem"]',
},
Select: {
option: 'Select option',
},
FieldConfigEditor: {
content: 'Field config editor content',
},
OverridesConfigEditor: {
content: 'Field overrides editor content',
},
};

View File

@ -0,0 +1,8 @@
import { Pages } from './pages';
import { Components } from './components';
import { E2ESelectors } from '../types';
export const selectors: { pages: E2ESelectors<typeof Pages>; components: E2ESelectors<typeof Components> } = {
pages: Pages,
components: Components,
};

View File

@ -0,0 +1,121 @@
export const Pages = {
Login: {
url: '/login',
username: 'Username input field',
password: 'Password input field',
submit: 'Login button',
skip: 'Skip change password button',
},
DataSource: {
name: 'Data source settings page name input field',
delete: 'Data source settings page Delete button',
saveAndTest: 'Data source settings page Save and Test button',
alert: 'Data source settings page Alert',
alertMessage: 'Data source settings page Alert message',
},
DataSources: {
url: '/datasources',
dataSources: (dataSourceName: string) => `Data source list item ${dataSourceName}`,
},
AddDataSource: {
url: '/datasources/new',
dataSourcePlugins: (pluginName: string) => `Data source plugin item ${pluginName}`,
},
ConfirmModal: {
delete: 'Confirm Modal Danger Button',
},
AddDashboard: {
url: '/dashboard/new',
addNewPanel: 'Add new panel',
},
Dashboard: {
url: (uid: string) => `/d/${uid}`,
Toolbar: {
toolbarItems: (button: string) => `Dashboard navigation bar button ${button}`,
navBar: () => '.navbar',
},
SubMenu: {
submenuItem: 'Dashboard template variables submenu item',
submenuItemLabels: (item: string) => `Dashboard template variables submenu Label ${item}`,
submenuItemValueDropDownValueLinkTexts: (item: string) =>
`Dashboard template variables Variable Value DropDown value link text ${item}`,
submenuItemValueDropDownDropDown: 'Dashboard template variables Variable Value DropDown DropDown',
submenuItemValueDropDownOptionTexts: (item: string) =>
`Dashboard template variables Variable Value DropDown option text ${item}`,
},
Settings: {
General: {
deleteDashBoard: 'Dashboard settings page delete dashboard button',
sectionItems: (item: string) => `Dashboard settings section item ${item}`,
saveDashBoard: 'Dashboard settings aside actions Save button',
saveAsDashBoard: 'Dashboard settings aside actions Save As button',
},
Variables: {
List: {
addVariableCTA: 'Call to action button Add variable',
newButton: 'Variable editor New variable button',
table: 'Variable editor Table',
tableRowNameFields: (variableName: string) => `Variable editor Table Name field ${variableName}`,
tableRowDefinitionFields: (variableName: string) => `Variable editor Table Definition field ${variableName}`,
tableRowArrowUpButtons: (variableName: string) => `Variable editor Table ArrowUp button ${variableName}`,
tableRowArrowDownButtons: (variableName: string) => `Variable editor Table ArrowDown button ${variableName}`,
tableRowDuplicateButtons: (variableName: string) => `Variable editor Table Duplicate button ${variableName}`,
tableRowRemoveButtons: (variableName: string) => `Variable editor Table Remove button ${variableName}`,
},
Edit: {
General: {
headerLink: 'Variable editor Header link',
modeLabelNew: 'Variable editor Header mode New',
modeLabelEdit: 'Variable editor Header mode Edit',
generalNameInput: 'Variable editor Form Name field',
generalTypeSelect: 'Variable editor Form Type select',
generalLabelInput: 'Variable editor Form Label field',
generalHideSelect: 'Variable editor Form Hide select',
selectionOptionsMultiSwitch: 'Variable editor Form Multi switch',
selectionOptionsIncludeAllSwitch: 'Variable editor Form IncludeAll switch',
selectionOptionsCustomAllInput: 'Variable editor Form IncludeAll field',
previewOfValuesOption: 'Variable editor Preview of Values option',
addButton: 'Variable editor Add button',
updateButton: 'Variable editor Update button',
},
QueryVariable: {
queryOptionsDataSourceSelect: 'Variable editor Form Query DataSource select',
queryOptionsRefreshSelect: 'Variable editor Form Query Refresh select',
queryOptionsRegExInput: 'Variable editor Form Query RegEx field',
queryOptionsSortSelect: 'Variable editor Form Query Sort select',
queryOptionsQueryInput: 'Variable editor Form Default Variable Query Editor textarea',
valueGroupsTagsEnabledSwitch: 'Variable editor Form Query UseTags switch',
valueGroupsTagsTagsQueryInput: 'Variable editor Form Query TagsQuery field',
valueGroupsTagsTagsValuesQueryInput: 'Variable editor Form Query TagsValuesQuery field',
},
ConstantVariable: {
constantOptionsQueryInput: 'Variable editor Form Constant Query field',
},
},
},
},
},
Dashboards: {
url: '/dashboards',
dashboards: (title: string) => `Dashboard search item ${title}`,
},
SaveDashboardAsModal: {
newName: 'Save dashboard title field',
save: 'Save dashboard button',
},
SaveDashboardModal: {
save: 'Dashboard settings Save Dashboard Modal Save button',
saveVariables: 'Dashboard settings Save Dashboard Modal Save variables checkbox',
saveTimerange: 'Dashboard settings Save Dashboard Modal Save timerange checkbox',
},
SharePanelModal: {
linkToRenderedImage: 'Link to rendered image',
},
Explore: {
url: '/explore',
General: {
container: 'Explore',
runButton: 'Run button',
},
},
};

View File

@ -0,0 +1 @@
export * from './selectors';

View File

@ -0,0 +1,15 @@
export type StringSelector = string;
export type FunctionSelector = (id: string) => string;
export type CssSelector = () => string;
export interface Selectors {
[key: string]: StringSelector | FunctionSelector | CssSelector | UrlSelector | Selectors;
}
export type E2ESelectors<S extends Selectors> = {
[P in keyof S]: S[P];
};
export interface UrlSelector extends Selectors {
url: string | FunctionSelector;
}

View File

@ -0,0 +1,4 @@
{
"exclude": ["dist", "node_modules", "**/*.test.ts*"],
"extends": "./tsconfig.json"
}

View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"declarationDir": "dist",
"outDir": "compiled",
"rootDirs": ["."],
"typeRoots": ["node_modules/@types"]
},
"exclude": ["dist", "node_modules"],
"extends": "@grafana/tsconfig",
"include": ["src/**/*.ts"]
}

View File

@ -31,7 +31,6 @@
},
"devDependencies": {
"@cypress/webpack-preprocessor": "4.1.3",
"@grafana/tsconfig": "^1.0.0-rc1",
"@rollup/plugin-commonjs": "11.0.2",
"@rollup/plugin-node-resolve": "7.1.1",
"@types/node": "13.7.7",
@ -43,19 +42,18 @@
"rollup-plugin-typescript2": "0.26.0",
"rollup-plugin-visualizer": "3.3.1",
"ts-loader": "6.2.1",
"typescript": "3.7.5",
"ts-node": "8.8.1"
},
"types": "src/index.ts",
"dependencies": {
"@cypress/webpack-preprocessor": "4.1.1",
"@grafana/tsconfig": "^1.0.0-rc1",
"@grafana/e2e-selectors": "7.0.0-pre.0",
"blink-diff": "1.0.13",
"commander": "5.0.0",
"cypress": "3.7.0",
"execa": "4.0.0",
"ts-loader": "6.2.1",
"typescript": "3.7.2",
"typescript": "3.7.5",
"yaml": "^1.8.3"
}
}

View File

@ -16,6 +16,7 @@ const buildCjsPackage = ({ env }) => ({
exports: 'named',
globals: {},
},
external: ['@grafana/e2e-selectors'],
plugins: [
copy({
flatten: false,

View File

@ -1,129 +0,0 @@
import { TestData } from '../pages/testdata';
import { Panel } from '../pages/panel';
import { Graph } from '../pages/graph';
import { componentFactory } from '../support';
import { Dashboard } from '../pages/dashboard';
export const Components = {
DataSource: {
TestData,
},
Panels: {
Panel,
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',
},
}),
},
PanelEditor: {
General: componentFactory({
selectors: {
content: 'Panel editor content',
},
}),
OptionsPane: componentFactory({
selectors: {
content: 'Panel editor option pane content',
close: Dashboard.selectors.toolbarItems('Close options pane'),
open: Dashboard.selectors.toolbarItems('Open options pane'),
select: 'Panel editor option pane select',
},
}),
// not sure about the naming *DataPane*
DataPane: componentFactory({
selectors: {
content: 'Panel editor data pane content',
},
}),
},
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"]',
},
}),
QueryTab: componentFactory({
selectors: {
content: 'Query editor tab content',
queryInspectorButton: 'Query inspector button',
},
}),
AlertTab: componentFactory({
selectors: {
content: 'Alert editor tab content',
},
}),
TransformTab: componentFactory({
selectors: {
content: 'Transform editor tab content',
},
}),
QueryEditorToolbarItem: componentFactory({
selectors: {
button: (title: string) => `QueryEditor toolbar item button ${title}`,
},
}),
BackButton: componentFactory({
selectors: {
backArrow: 'Go Back button',
},
}),
OptionsGroup: componentFactory({
selectors: {
toggle: (title: string) => `Options group ${title}`,
},
}),
PluginVisualization: componentFactory({
selectors: {
item: (title: string) => `Plugin visualization item ${title}`,
current: () => '[class*="-currentVisualizationItem"]',
},
}),
Select: componentFactory({
selectors: {
option: 'Select option',
},
}),
FieldConfigEditor: componentFactory({
selectors: {
content: 'Field config editor content',
},
}),
OverridesConfigEditor: componentFactory({
selectors: {
content: 'Field overrides editor content',
},
}),
};

View File

@ -14,9 +14,7 @@ const DEFAULT_ADD_PANEL_CONFIG: AddPanelConfig = {
export const addPanel = (config?: Partial<AddPanelConfig>) => {
const { dataSourceName, queriesForm } = { ...DEFAULT_ADD_PANEL_CONFIG, ...config };
// @todo remove `@ts-ignore` when possible
// @ts-ignore
getScenarioContext().then(({ lastAddedDashboardUid }) => {
getScenarioContext().then(({ lastAddedDashboardUid }: any) => {
e2e.flows.openDashboard(lastAddedDashboardUid);
e2e.pages.Dashboard.Toolbar.toolbarItems('Add panel').click();
e2e.pages.AddDashboard.addNewPanel().click();

View File

@ -1,4 +1,4 @@
import { e2e } from '../noTypeCheck';
import { e2e } from '../index';
export enum PanelMenuItems {
Edit = 'Edit',

View File

@ -3,4 +3,24 @@
*
* @packageDocumentation
*/
export { e2e } from './noTypeCheck';
import { e2eScenario, ScenarioArguments } from './support/scenario';
import { Flows } from './flows';
import { getScenarioContext, setScenarioContext } from './support/scenarioContext';
import { e2eFactory } from './support';
import { Pages } from '@grafana/e2e-selectors/src/selectors/pages';
import { Components } from '@grafana/e2e-selectors/src/selectors/components';
const e2eObject = {
env: (args: string) => Cypress.env(args),
config: () => Cypress.config(),
blobToBase64String: (blob: any) => Cypress.Blob.blobToBase64String(blob),
imgSrcToBlob: (url: string) => Cypress.Blob.imgSrcToBlob(url),
scenario: (args: ScenarioArguments) => e2eScenario(args),
pages: e2eFactory({ selectors: Pages }),
components: e2eFactory({ selectors: Components }),
flows: Flows,
getScenarioContext,
setScenarioContext,
};
export const e2e: (() => Cypress.cy) & typeof e2eObject = Object.assign(() => cy, e2eObject);

View File

@ -1,28 +0,0 @@
// @ts-nocheck
// importing the e2e package in Grafana will cause transpile errors because
// Cypress is an unknown type. Adding the Cypress types would overwrite all jest test types like
// 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 } from './pages';
import { Components } from './components';
import { Flows } from './flows';
import { getScenarioContext, setScenarioContext } from './support/scenarioContext';
export type SelectorFunction = (text?: string) => Cypress.Chainable<JQuery<HTMLElement>>;
export type VisitFunction = (args?: string) => Cypress.Chainable<Window>;
const e2eObject = {
env: (args: string) => Cypress.env(args),
config: () => Cypress.config(),
blobToBase64String: (blob: any) => Cypress.Blob.blobToBase64String(blob),
imgSrcToBlob: (url: string) => Cypress.Blob.imgSrcToBlob(url),
scenario: (args: ScenarioArguments) => e2eScenario(args),
pages: Pages,
components: Components,
flows: Flows,
getScenarioContext,
setScenarioContext,
};
export const e2e: (() => Cypress.cy) & typeof e2eObject = Object.assign(() => cy, e2eObject);

View File

@ -1,8 +0,0 @@
import { pageFactory } from '../support';
export const AddDashboard = pageFactory({
url: '/dashboard/new',
selectors: {
addNewPanel: 'Add new panel',
},
});

View File

@ -1,8 +0,0 @@
import { pageFactory } from '../support';
export const AddDataSource = pageFactory({
url: '/datasources/new',
selectors: {
dataSourcePlugins: (pluginName: string) => `Data source plugin item ${pluginName}`,
},
});

View File

@ -1,8 +0,0 @@
import { pageFactory } from '../support';
export const ConfirmModal = pageFactory({
url: '',
selectors: {
delete: 'Confirm Modal Danger Button',
},
});

View File

@ -1,9 +0,0 @@
import { pageFactory } from '../support';
export const Dashboard = pageFactory({
url: (uid: string) => `/d/${uid}`,
selectors: {
toolbarItems: (button: string) => `Dashboard navigation bar button ${button}`,
navBar: () => '.navbar',
},
});

View File

@ -1,11 +0,0 @@
import { pageFactory } from '../support';
export const DashboardSettings = pageFactory({
url: '',
selectors: {
deleteDashBoard: 'Dashboard settings page delete dashboard button',
sectionItems: (item: string) => `Dashboard settings section item ${item}`,
saveDashBoard: 'Dashboard settings aside actions Save button',
saveAsDashBoard: 'Dashboard settings aside actions Save As button',
},
});

View File

@ -1,8 +0,0 @@
import { pageFactory } from '../support';
export const Dashboards = pageFactory({
url: '/dashboards',
selectors: {
dashboards: (title: string) => `Dashboard search item ${title}`,
},
});

View File

@ -1,12 +0,0 @@
import { pageFactory } from '../support';
export const DataSource = pageFactory({
url: '',
selectors: {
name: 'Data source settings page name input field',
delete: 'Data source settings page Delete button',
saveAndTest: 'Data source settings page Save and Test button',
alert: 'Data source settings page Alert',
alertMessage: 'Data source settings page Alert message',
},
});

View File

@ -1,8 +0,0 @@
import { pageFactory } from '../support';
export const DataSources = pageFactory({
url: '/datasources',
selectors: {
dataSources: (dataSourceName: string) => `Data source list item ${dataSourceName}`,
},
});

View File

@ -1,9 +0,0 @@
import { pageFactory } from '../support';
export const Explore = pageFactory({
url: '/explore',
selectors: {
container: 'Explore',
runButton: 'Run button',
},
});

View File

@ -1,13 +0,0 @@
import { VisualizationTab } from './visualizationTab';
import { pageFactory } from '../../support';
export const Graph = {
VisualizationTab,
Legend: pageFactory({
url: '',
selectors: {
legendItemAlias: (name: string) => `gpl alias ${name}`,
showLegendSwitch: 'gpl show legend',
},
}),
};

View File

@ -1,11 +0,0 @@
import { pageFactory } from '../../support';
export const VisualizationTab = pageFactory({
url: '',
selectors: {
xAxisSection: 'X-Axis section',
axesSection: 'Axes section',
legendSection: 'Legend section',
displaySection: 'Display section',
},
});

View File

@ -1,47 +0,0 @@
import { Login } from './login';
import { AddDataSource } from './addDataSource';
import { DataSource } from './datasource';
import { DataSources } from './datasources';
import { ConfirmModal } from './confirmModal';
import { AddDashboard } from './addDashboard';
import { Dashboard } from './dashboard';
import { SaveDashboardAsModal } from './saveDashboardAsModal';
import { Dashboards } from './dashboards';
import { DashboardSettings } from './dashboardSettings';
import { Explore } from './explore';
import { SaveDashboardModal } from './saveDashboardModal';
import { SharePanelModal } from './sharePanelModal';
import { ConstantVariable, QueryVariable, VariableGeneral, Variables, VariablesSubMenu } from './variables';
export const Pages = {
Login,
DataSource,
DataSources,
AddDataSource,
ConfirmModal,
AddDashboard,
Dashboard: {
visit: (uid: string) => Dashboard.visit(uid),
Toolbar: Dashboard,
SubMenu: VariablesSubMenu,
Settings: {
General: DashboardSettings,
Variables: {
List: Variables,
Edit: {
General: VariableGeneral,
QueryVariable: QueryVariable,
ConstantVariable: ConstantVariable,
},
},
},
},
Dashboards,
SaveDashboardAsModal,
SaveDashboardModal,
SharePanelModal,
Explore: {
visit: () => Explore.visit(),
General: Explore,
},
};

View File

@ -1,11 +0,0 @@
import { pageFactory } from '../support';
export const Login = pageFactory({
url: '/login',
selectors: {
username: 'Username input field',
password: 'Password input field',
submit: 'Login button',
skip: 'Skip change password button',
},
});

View File

@ -1,9 +0,0 @@
import { pageFactory } from '../support';
export const Panel = pageFactory({
url: '',
selectors: {
title: (title: string) => `Panel header title item ${title}`,
headerItems: (item: string) => `Panel header item ${item}`,
},
});

View File

@ -1,9 +0,0 @@
import { pageFactory } from '../support';
export const SaveDashboardAsModal = pageFactory({
url: '',
selectors: {
newName: 'Save dashboard title field',
save: 'Save dashboard button',
},
});

View File

@ -1,10 +0,0 @@
import { pageFactory } from '../support';
export const SaveDashboardModal = pageFactory({
url: '',
selectors: {
save: 'Dashboard settings Save Dashboard Modal Save button',
saveVariables: 'Dashboard settings Save Dashboard Modal Save variables checkbox',
saveTimerange: 'Dashboard settings Save Dashboard Modal Save timerange checkbox',
},
});

View File

@ -1,8 +0,0 @@
import { pageFactory } from '../support';
export const SharePanelModal = pageFactory({
url: '',
selectors: {
linkToRenderedImage: 'Link to rendered image',
},
});

View File

@ -1,5 +0,0 @@
import { QueryTab } from './queryTab';
export const TestData = {
QueryTab,
};

View File

@ -1,13 +0,0 @@
import { componentFactory } from '../../support';
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',
},
});

View File

@ -1,69 +0,0 @@
import { pageFactory } from '../support';
export const Variables = pageFactory({
url: '',
selectors: {
addVariableCTA: 'Call to action button Add variable',
newButton: 'Variable editor New variable button',
table: 'Variable editor Table',
tableRowNameFields: (variableName: string) => `Variable editor Table Name field ${variableName}`,
tableRowDefinitionFields: (variableName: string) => `Variable editor Table Definition field ${variableName}`,
tableRowArrowUpButtons: (variableName: string) => `Variable editor Table ArrowUp button ${variableName}`,
tableRowArrowDownButtons: (variableName: string) => `Variable editor Table ArrowDown button ${variableName}`,
tableRowDuplicateButtons: (variableName: string) => `Variable editor Table Duplicate button ${variableName}`,
tableRowRemoveButtons: (variableName: string) => `Variable editor Table Remove button ${variableName}`,
},
});
export const VariablesSubMenu = pageFactory({
url: '',
selectors: {
submenuItem: 'Dashboard template variables submenu item',
submenuItemLabels: (item: string) => `Dashboard template variables submenu Label ${item}`,
submenuItemValueDropDownValueLinkTexts: (item: string) =>
`Dashboard template variables Variable Value DropDown value link text ${item}`,
submenuItemValueDropDownDropDown: 'Dashboard template variables Variable Value DropDown DropDown',
submenuItemValueDropDownOptionTexts: (item: string) =>
`Dashboard template variables Variable Value DropDown option text ${item}`,
},
});
export const VariableGeneral = pageFactory({
url: '',
selectors: {
headerLink: 'Variable editor Header link',
modeLabelNew: 'Variable editor Header mode New',
modeLabelEdit: 'Variable editor Header mode Edit',
generalNameInput: 'Variable editor Form Name field',
generalTypeSelect: 'Variable editor Form Type select',
generalLabelInput: 'Variable editor Form Label field',
generalHideSelect: 'Variable editor Form Hide select',
selectionOptionsMultiSwitch: 'Variable editor Form Multi switch',
selectionOptionsIncludeAllSwitch: 'Variable editor Form IncludeAll switch',
selectionOptionsCustomAllInput: 'Variable editor Form IncludeAll field',
previewOfValuesOption: 'Variable editor Preview of Values option',
addButton: 'Variable editor Add button',
updateButton: 'Variable editor Update button',
},
});
export const QueryVariable = pageFactory({
url: '',
selectors: {
queryOptionsDataSourceSelect: 'Variable editor Form Query DataSource select',
queryOptionsRefreshSelect: 'Variable editor Form Query Refresh select',
queryOptionsRegExInput: 'Variable editor Form Query RegEx field',
queryOptionsSortSelect: 'Variable editor Form Query Sort select',
queryOptionsQueryInput: 'Variable editor Form Default Variable Query Editor textarea',
valueGroupsTagsEnabledSwitch: 'Variable editor Form Query UseTags switch',
valueGroupsTagsTagsQueryInput: 'Variable editor Form Query TagsQuery field',
valueGroupsTagsTagsValuesQueryInput: 'Variable editor Form Query TagsValuesQuery field',
},
});
export const ConstantVariable = pageFactory({
url: '',
selectors: {
constantOptionsQueryInput: 'Variable editor Form Constant Query field',
},
});

View File

@ -18,20 +18,10 @@ 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(() => {
Flows.login('admin', 'admin');
if (addScenarioDataSource) {
@ -42,12 +32,8 @@ export const e2eScenario = ({
}
});
// @todo remove `@ts-ignore` when possible
// @ts-ignore
afterEach(() => {
// @todo remove `@ts-ignore` when possible
// @ts-ignore
getScenarioContext().then(({ lastAddedDashboardUid, lastAddedDataSource }) => {
getScenarioContext().then(({ lastAddedDashboardUid, lastAddedDataSource }: any) => {
if (lastAddedDataSource) {
Flows.deleteDataSource(lastAddedDataSource);
}
@ -58,8 +44,6 @@ export const e2eScenario = ({
});
});
// @todo remove `@ts-ignore` when possible
// @ts-ignore
it(itName, () => scenario());
}
});

View File

@ -1,76 +1,99 @@
import { CssSelector, FunctionSelector, Selectors, StringSelector, UrlSelector } from '@grafana/e2e-selectors';
import { e2e } from '../index';
import { Selector } from './selector';
import { fromBaseUrl } from './url';
import { e2e } from '../index';
import { SelectorFunction, VisitFunction } from '../noTypeCheck';
export type Selectors = Record<string, string | Function>;
export type SelectorFunctions<S> = { [P in keyof S]: SelectorFunction };
export type VisitFunction = (args?: string) => Cypress.Chainable<Window>;
export type E2EVisit = { visit: VisitFunction };
export type E2EFunction = (text?: string) => Cypress.Chainable<JQuery<HTMLElement>>;
export type Page<S> = SelectorFunctions<S> & {
selectors: S;
visit: VisitFunction;
export type TypeSelectors<S> = S extends StringSelector
? E2EFunction
: S extends FunctionSelector
? E2EFunction
: S extends CssSelector
? E2EFunction
: S extends UrlSelector
? E2EVisit & Omit<E2EFunctions<S>, 'url'>
: S extends Record<any, any>
? E2EFunctions<S>
: S;
export type E2EFunctions<S extends Selectors> = {
[P in keyof S]: TypeSelectors<S[P]>;
};
export interface PageFactoryArgs<S> {
selectors: S;
url?: string | Function;
}
export const pageFactory = <S extends Selectors>({ url, selectors }: PageFactoryArgs<S>): Page<S> => {
const visit = (args?: string) => {
if (!url) {
return e2e().visit('');
}
export type E2EObjects<S extends Selectors> = E2EFunctions<S>;
let parsedUrl = '';
if (typeof url === 'string') {
parsedUrl = fromBaseUrl(url);
}
export type E2EFactoryArgs<S extends Selectors> = { selectors: S };
if (typeof url === 'function' && args) {
parsedUrl = fromBaseUrl(url(args));
}
e2e().logToConsole('Visiting', parsedUrl);
return e2e().visit(parsedUrl);
};
const pageObjects: SelectorFunctions<S> = {} as SelectorFunctions<S>;
const processSelectors = <S extends Selectors>(e2eObjects: E2EFunctions<S>, selectors: S): E2EFunctions<S> => {
const logOutput = (data: any) => e2e().logToConsole('Retrieving Selector:', data);
const keys = Object.keys(selectors);
keys.forEach(key => {
for (let index = 0; index < keys.length; index++) {
const key = keys[index];
const value = selectors[key];
if (key === 'url') {
// @ts-ignore
e2eObjects['visit'] = (args?: string) => {
let parsedUrl = '';
if (typeof value === 'string') {
parsedUrl = fromBaseUrl(value);
}
if (typeof value === 'function' && args) {
parsedUrl = fromBaseUrl(value(args));
}
e2e().logToConsole('Visiting', parsedUrl);
return e2e().visit(parsedUrl);
};
continue;
}
if (typeof value === 'string') {
// @ts-ignore
pageObjects[key] = () => {
e2e().logToConsole('Retrieving Selector:', value);
e2eObjects[key] = () => {
logOutput(value);
return e2e().get(Selector.fromAriaLabel(value));
};
continue;
}
if (typeof value === 'function') {
// @ts-ignore
pageObjects[key] = (text?: string) => {
e2eObjects[key] = (text?: string) => {
if (!text) {
const selector = value();
e2e().logToConsole('Retrieving Selector:', selector);
const selector = value((undefined as unknown) as string);
logOutput(selector);
return e2e().get(selector);
}
const selector = value(text);
e2e().logToConsole('Retrieving Selector:', selector);
logOutput(selector);
return e2e().get(Selector.fromAriaLabel(selector));
};
continue;
}
});
return {
visit,
...pageObjects,
selectors,
};
if (typeof value === 'object') {
// @ts-ignore
e2eObjects[key] = processSelectors({}, value);
}
}
return e2eObjects;
};
type Component<S> = Omit<Page<S>, 'visit'>;
type ComponentFactoryArgs<S> = Omit<PageFactoryArgs<S>, 'url'>;
export const e2eFactory = <S extends Selectors>({ selectors }: E2EFactoryArgs<S>): E2EObjects<S> => {
const e2eObjects: E2EFunctions<S> = {} as E2EFunctions<S>;
processSelectors(e2eObjects, selectors);
export const componentFactory = <S extends Selectors>(args: ComponentFactoryArgs<S>): Component<S> => {
const { visit, ...rest } = pageFactory(args);
return rest;
return { ...e2eObjects };
};

View File

@ -16,7 +16,7 @@ import { closeMilestoneTask } from './tasks/closeMilestone';
import { pluginDevTask } from './tasks/plugin.dev';
import { githubPublishTask } from './tasks/plugin.utils';
import { pluginUpdateTask } from './tasks/plugin.update';
import { ciBuildPluginTask, ciBuildPluginDocsTask, ciPackagePluginTask, ciPluginReportTask } from './tasks/plugin.ci';
import { ciBuildPluginDocsTask, ciBuildPluginTask, ciPackagePluginTask, ciPluginReportTask } from './tasks/plugin.ci';
import { buildPackageTask } from './tasks/package.build';
import { pluginCreateTask } from './tasks/plugin.create';
import { bundleManagedTask } from './tasks/plugin/bundle.managed';
@ -41,7 +41,7 @@ export const run = (includeInternalScripts = false) => {
program
.command('package:build')
.option('-s, --scope <packages>', 'packages=[data|runtime|ui|toolkit]')
.option('-s, --scope <packages>', 'packages=[data|runtime|ui|toolkit|e2e|e2e-selectors]')
.description('Builds @grafana/* package to packages/grafana-*/dist')
.action(async cmd => {
await execTask(buildPackageTask)({

View File

@ -1,6 +1,6 @@
{
"extends": ["@grafana/eslint-config"],
"rules": {
"no-restricted-imports": [2, "^@grafana/runtime.*", "^@grafana/ui.*"]
"no-restricted-imports": [2, "^@grafana/runtime.*", "^@grafana/ui.*", "^@grafana/e2e.*"]
}
}

View File

@ -29,6 +29,7 @@
"dependencies": {
"@emotion/core": "^10.0.27",
"@grafana/data": "7.0.0-pre.0",
"@grafana/e2e-selectors": "7.0.0-pre.0",
"@grafana/slate-react": "0.22.9-grafana",
"@grafana/tsconfig": "^1.0.0-rc1",
"@iconscout/react-unicons": "^1.0.0",

View File

@ -24,7 +24,7 @@ const buildCjsPackage = ({ env }) => {
},
},
],
external: ['react', 'react-dom', '@grafana/data', 'moment'],
external: ['react', 'react-dom', '@grafana/data', 'moment', '@grafana/e2e-selectors'],
plugins: [
commonjs({
include: /node_modules/,

View File

@ -2,10 +2,11 @@ import React, { CSSProperties, FC, ReactNode, useState } from 'react';
import { GrafanaTheme } from '@grafana/data';
import RcDrawer from 'rc-drawer';
import { css } from 'emotion';
import { selectors } from '@grafana/e2e-selectors';
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;
@ -96,8 +97,8 @@ export const Drawer: FC<Props> = ({
className={drawerStyles.drawer}
aria-label={
typeof title === 'string'
? e2e.components.Drawer.General.selectors.title(title)
: e2e.components.Drawer.General.selectors.title('no title')
? selectors.components.Drawer.General.title(title)
: selectors.components.Drawer.General.title('no title')
}
>
{typeof title === 'string' && (
@ -109,7 +110,7 @@ export const Drawer: FC<Props> = ({
size="xl"
onClick={() => setIsExpanded(true)}
surface="header"
aria-label={e2e.components.Drawer.General.selectors.expand}
aria-label={selectors.components.Drawer.General.expand}
/>
)}
{expandable && isExpanded && (
@ -118,7 +119,7 @@ export const Drawer: FC<Props> = ({
size="xl"
onClick={() => setIsExpanded(false)}
surface="header"
aria-label={e2e.components.Drawer.General.selectors.contract}
aria-label={selectors.components.Drawer.General.contract}
/>
)}
<IconButton
@ -126,7 +127,7 @@ export const Drawer: FC<Props> = ({
size="xl"
onClick={onClose}
surface="header"
aria-label={e2e.components.Drawer.General.selectors.close}
aria-label={selectors.components.Drawer.General.close}
/>
</div>
<div className={drawerStyles.titleWrapper}>

View File

@ -1,11 +1,12 @@
import React, { FC } from 'react';
import { css, cx } from 'emotion';
import { GrafanaTheme } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
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;
@ -69,7 +70,7 @@ export const Tab: FC<TabProps> = ({ label, active, icon, onChangeTab, counter })
<li
className={cx(tabsStyles.tabItem, active && tabsStyles.activeStyle)}
onClick={onChangeTab}
aria-label={e2e.components.Tab.selectors.title(label)}
aria-label={selectors.components.Tab.title(label)}
>
{icon && <Icon name={icon} />}
{label}

View File

@ -1,6 +1,6 @@
import React, { ButtonHTMLAttributes } from 'react';
import { IconButton } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
export interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
surface: 'dashboard' | 'panel' | 'header';
@ -14,7 +14,7 @@ export const BackButton: React.FC<Props> = ({ surface, onClick }) => {
tooltipPlacement="bottom"
size="xxl"
surface={surface}
aria-label={e2e.components.BackButton.selectors.backArrow}
aria-label={selectors.components.BackButton.backArrow}
onClick={onClick}
/>
);

View File

@ -1,9 +1,9 @@
import React, { ChangeEvent, PureComponent, SyntheticEvent } from 'react';
import { Tooltip } from '@grafana/ui';
import { AppEvents } from '@grafana/data';
import { e2e } from '@grafana/e2e';
import appEvents from 'app/core/app_events';
import { selectors } from '@grafana/e2e-selectors';
interface Props {
onSubmit: (pw: string) => void;
@ -117,7 +117,7 @@ export class ChangePassword extends PureComponent<Props, State> {
placement="bottom"
content="If you skip you will be prompted to change password next time you login."
>
<a className="btn btn-link" onClick={this.onSkip} aria-label={e2e.pages.Login.selectors.skip}>
<a className="btn btn-link" onClick={this.onSkip} aria-label={selectors.pages.Login.skip}>
Skip
</a>
</Tooltip>

View File

@ -1,5 +1,5 @@
import React, { ChangeEvent, PureComponent, SyntheticEvent } from 'react';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import { FormModel } from './LoginCtrl';
@ -74,7 +74,7 @@ export class LoginForm extends PureComponent<Props, State> {
className="gf-form-input login-form-input"
required
placeholder={this.props.loginHint}
aria-label={e2e.pages.Login.selectors.username}
aria-label={selectors.pages.Login.username}
onChange={this.onChangeUsername}
/>
</div>
@ -87,7 +87,7 @@ export class LoginForm extends PureComponent<Props, State> {
ng-model="formModel.password"
id="inputPassword"
placeholder={this.props.passwordHint}
aria-label={e2e.pages.Login.selectors.password}
aria-label={selectors.pages.Login.password}
onChange={this.onChangePassword}
/>
</div>
@ -95,7 +95,7 @@ export class LoginForm extends PureComponent<Props, State> {
{!this.props.isLoggingIn ? (
<button
type="submit"
aria-label={e2e.pages.Login.selectors.submit}
aria-label={selectors.pages.Login.submit}
className={`btn btn-large p-x-2 ${this.state.valid ? 'btn-primary' : 'btn-inverse'}`}
onClick={this.onSubmit}
disabled={!this.state.valid}

View File

@ -5,7 +5,7 @@ import filter from 'lodash/filter';
import find from 'lodash/find';
import indexOf from 'lodash/indexOf';
import map from 'lodash/map';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import coreModule from '../core_module';
import { GrafanaRootScope } from 'app/routes/GrafanaCtrl';
@ -27,13 +27,13 @@ export class ValueSelectDropdownCtrl {
onUpdated: any;
queryHasSearchFilter: boolean;
debouncedQueryChanged: Function;
selectors: typeof e2e.pages.Dashboard.SubMenu.selectors;
selectors: typeof selectors.pages.Dashboard.SubMenu;
/** @ngInject */
constructor(private $scope: IScope) {
this.queryHasSearchFilter = this.variable ? containsSearchFilter(this.variable.query) : false;
this.debouncedQueryChanged = debounce(this.queryChanged.bind(this), 200);
this.selectors = e2e.pages.Dashboard.SubMenu.selectors;
this.selectors = selectors.pages.Dashboard.SubMenu;
}
show() {

View File

@ -1,6 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import coreModule from 'app/core/core_module';
import appEvents from 'app/core/app_events';
@ -103,7 +103,7 @@ export class UtilSrv {
scope.yesText = payload.yesText || 'Yes';
scope.noText = payload.noText || 'Cancel';
scope.confirmTextValid = scope.confirmText ? false : true;
scope.selectors = e2e.pages.ConfirmModal.selectors;
scope.selectors = selectors.pages.ConfirmModal;
appEvents.emit(CoreEvents.showModal, {
src: 'public/app/partials/confirm_modal.html',

View File

@ -2,8 +2,9 @@ import React, { PureComponent } from 'react';
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
import { css } from 'emotion';
import { Alert, Button, IconName } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import { AngularComponent, getAngularLoader, getDataSourceSrv } from '@grafana/runtime';
import appEvents from 'app/core/app_events';
import { getAlertingValidationMessage } from './getAlertingValidationMessage';
@ -18,7 +19,6 @@ import { TestRuleResult } from './TestRuleResult';
import { AppNotificationSeverity, CoreEvents, StoreState } from 'app/types';
import { updateLocation } from 'app/core/actions';
import { PanelEditorTabId } from '../dashboard/components/PanelEditor/types';
import { e2e } from '@grafana/e2e';
interface OwnProps {
dashboard: DashboardModel;
@ -206,7 +206,7 @@ class UnConnectedAlertTab extends PureComponent<Props, State> {
return (
<EditorTabBody toolbarItems={toolbarItems}>
<div aria-label={e2e.components.AlertTab.selectors.content}>
<div aria-label={selectors.components.AlertTab.content}>
{alert && hasTransformations && (
<Alert
severity={AppNotificationSeverity.Error}

View File

@ -3,6 +3,7 @@ import React, { useMemo } from 'react';
import _ from 'lodash';
import { LocationUpdate } from '@grafana/runtime';
import { Button, HorizontalGroup, IconButton, stylesFactory, useTheme } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import { connect, MapDispatchToProps } from 'react-redux';
// Utils
import config from 'app/core/config';
@ -13,7 +14,6 @@ import { addPanel } from 'app/features/dashboard/state/reducers';
// Types
import { DashboardModel, PanelModel } from '../../state';
import { LS_PANEL_COPY_KEY } from 'app/core/constants';
import { e2e } from '@grafana/e2e';
import { css, cx, keyframes } from 'emotion';
import { GrafanaTheme } from '@grafana/data';
import tinycolor from 'tinycolor2';
@ -168,7 +168,7 @@ const AddPanelWidgetCreate: React.FC<AddPanelWidgetCreateProps> = ({ onCreate, o
return (
<div className={styles.wrapper}>
<HorizontalGroup>
<Button icon="plus" size="md" onClick={onCreate} aria-label={e2e.pages.AddDashboard.selectors.addNewPanel}>
<Button icon="plus" size="md" onClick={onCreate} aria-label={selectors.pages.AddDashboard.addNewPanel}>
Add new panel
</Button>
{copiedPanelPlugins.length === 1 && (

View File

@ -1,8 +1,8 @@
// Libraries
import React, { FunctionComponent } from 'react';
// Components
import { Tooltip, Icon, IconName, IconType, IconSize } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { Icon, IconName, IconSize, IconType, Tooltip } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
interface Props {
icon?: IconName;
@ -31,7 +31,7 @@ export const DashNavButton: FunctionComponent<Props> = ({
<button
className={`btn navbar-button navbar-button--${classSuffix}`}
onClick={onClick}
aria-label={e2e.pages.Dashboard.Toolbar.selectors.toolbarItems(tooltip)}
aria-label={selectors.pages.Dashboard.Toolbar.toolbarItems(tooltip)}
>
{icon && <Icon name={icon} type={iconType} size={iconSize || 'lg'} />}
{children}

View File

@ -1,7 +1,7 @@
import $ from 'jquery';
import _ from 'lodash';
import angular, { ILocationService, IScope } from 'angular';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import { appEvents, contextSrv, coreModule } from 'app/core/core';
import { DashboardModel } from '../../state/DashboardModel';
@ -24,7 +24,7 @@ export class SettingsCtrl {
canDelete: boolean;
sections: any[];
hasUnsavedFolderChange: boolean;
selectors: typeof e2e.pages.Dashboard.Settings.General.selectors;
selectors: typeof selectors.pages.Dashboard.Settings.General;
useAngularTemplating: boolean;
/** @ngInject */
@ -59,7 +59,7 @@ export class SettingsCtrl {
appEvents.on(CoreEvents.dashboardSaved, this.onPostSave.bind(this), $scope);
this.selectors = e2e.pages.Dashboard.Settings.General.selectors;
this.selectors = selectors.pages.Dashboard.Settings.General;
this.useAngularTemplating = !getConfig().featureToggles.newVariables;
}

View File

@ -8,12 +8,13 @@ import {
transformDataFrame,
} from '@grafana/data';
import { Button, Field, Icon, Select, Table } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import AutoSizer from 'react-virtualized-auto-sizer';
import { getPanelInspectorStyles } from './styles';
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[];
@ -109,7 +110,7 @@ export class InspectDataTab extends PureComponent<Props, State> {
});
return (
<div className={styles.dataTabContent} aria-label={e2e.components.PanelInspector.Data.selectors.content}>
<div className={styles.dataTabContent} aria-label={selectors.components.PanelInspector.Data.content}>
<div className={styles.toolbar}>
<Field label="Transformer" className="flex-grow-1">
<Select options={transformationOptions} value={transformId} onChange={this.onTransformationChange} />

View File

@ -2,10 +2,11 @@ import React, { PureComponent } from 'react';
import { chain } from 'lodash';
import { AppEvents, PanelData, SelectableValue } from '@grafana/data';
import { Button, ClipboardButton, Field, JSONFormatter, Select, TextArea } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import { appEvents } from 'app/core/core';
import { DashboardModel, PanelModel } from '../../state';
import { getPanelInspectorStyles } from './styles';
import { e2e } from '@grafana/e2e';
enum ShowContent {
PanelJSON = 'panel',
@ -142,7 +143,7 @@ export class InspectJSONTab extends PureComponent<Props, State> {
return (
<>
<div className={styles.toolbar} aria-label={e2e.components.PanelInspector.Json.selectors.content}>
<div className={styles.toolbar} aria-label={selectors.components.PanelInspector.Json.content}>
<Field label="Select source" className="flex-grow-1">
<Select options={options} value={selected} onChange={this.onSelectChanged} />
</Field>

View File

@ -7,6 +7,7 @@ import { QueryInspector } from './QueryInspector';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { CustomScrollbar, Drawer, JSONFormatter, TabContent } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import { getDataSourceSrv, getLocationSrv } from '@grafana/runtime';
import {
DataFrame,
@ -25,7 +26,6 @@ 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;
@ -223,7 +223,7 @@ export class PanelInspectorUnconnected extends PureComponent<Props, State> {
}
return (
<div aria-label={e2e.components.PanelInspector.Stats.selectors.content}>
<div aria-label={selectors.components.PanelInspector.Stats.content}>
{this.renderStatsTable('Stats', stats)}
{this.renderStatsTable('Data source stats', dataStats)}
</div>

View File

@ -1,12 +1,13 @@
import React, { PureComponent } from 'react';
import { Button, JSONFormatter, LoadingPlaceholder } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import { AppEvents, PanelEvents } from '@grafana/data';
import appEvents from 'app/core/app_events';
import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard';
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;
@ -189,7 +190,7 @@ export class QueryInspector extends PureComponent<Props, State> {
return (
<>
<div aria-label={e2e.components.PanelInspector.Query.selectors.content}>
<div aria-label={selectors.components.PanelInspector.Query.content}>
<h3 className="section-heading">Query inspector</h3>
<p className="small muted">
Query inspector allows you to view raw request and response. To collect this data Grafana needs to issue a

View File

@ -13,7 +13,7 @@ import { getDataLinksVariableSuggestions } from '../../../panel/panellinks/link_
import { OverrideEditor } from './OverrideEditor';
import groupBy from 'lodash/groupBy';
import { OptionsGroup } from './OptionsGroup';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
interface Props {
plugin: PanelPlugin;
@ -103,7 +103,7 @@ export const OverrideFieldConfigEditor: React.FC<Props> = props => {
};
return (
<div aria-label={e2e.components.OverridesConfigEditor.selectors.content}>
<div aria-label={selectors.components.OverridesConfigEditor.content}>
{renderOverrides()}
{renderAddOverride()}
</div>
@ -183,7 +183,7 @@ export const DefaultFieldConfigEditor: React.FC<Props> = ({ data, onChange, conf
const groupedConfigs = groupBy(plugin.fieldConfigRegistry.list(), i => i.category && i.category[0]);
return (
<div aria-label={e2e.components.FieldConfigEditor.selectors.content}>
<div aria-label={selectors.components.FieldConfigEditor.content}>
{Object.keys(groupedConfigs).map((k, i) => {
const groupItemsCounter = countGroupItems(groupedConfigs[k], config);

View File

@ -4,7 +4,7 @@ import { GrafanaTheme } from '@grafana/data';
import { Icon, stylesFactory, useTheme } from '@grafana/ui';
import { PANEL_EDITOR_UI_STATE_STORAGE_KEY } from './state/reducers';
import { useLocalStorage } from 'react-use';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
export interface OptionsGroupProps {
id: string;
@ -101,7 +101,7 @@ const CollapsibleSection: FC<Omit<OptionsGroupProps, 'persistMe'>> = ({
<div
className={styles.header}
onClick={() => toggleExpand(!isExpanded)}
aria-label={e2e.components.OptionsGroup.selectors.toggle(id)}
aria-label={selectors.components.OptionsGroup.toggle(id)}
>
<div className={cx(styles.toggle, 'editor-options-group-toggle')}>
<Icon name={isExpanded ? 'angle-down' : 'angle-right'} />

View File

@ -8,7 +8,7 @@ import { css } from 'emotion';
import { PanelOptionsTab } from './PanelOptionsTab';
import { DashNavButton } from 'app/features/dashboard/components/DashNav/DashNavButton';
import { usePanelLatestData } from './usePanelLatestData';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
interface Props {
plugin: PanelPlugin;
@ -81,7 +81,7 @@ export const OptionsPaneContent: React.FC<Props> = ({
const showMainTab = activeTab === 'options' || plugin.meta.skipDataQuery;
return (
<div className={styles.panelOptionsPane} aria-label={e2e.components.PanelEditor.OptionsPane.selectors.content}>
<div className={styles.panelOptionsPane} aria-label={selectors.components.PanelEditor.OptionsPane.content}>
{plugin && (
<div className={styles.wrapper}>
<TabsBar className={styles.tabsBar}>
@ -185,7 +185,7 @@ export const TabsBarContent: React.FC<{
return (
<>
{width < 352 ? (
<div className="flex-grow-1" aria-label={e2e.components.PanelEditor.OptionsPane.selectors.select}>
<div className="flex-grow-1" aria-label={selectors.components.PanelEditor.OptionsPane.select}>
<Select
options={tabs}
value={active}

View File

@ -31,7 +31,7 @@ import { SubMenuItems } from 'app/features/dashboard/components/SubMenu/SubMenuI
import { BackButton } from 'app/core/components/BackButton/BackButton';
import { appEvents } from 'app/core/core';
import { SaveDashboardModalProxy } from '../SaveDashboard/SaveDashboardModalProxy';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
interface OwnProps {
dashboard: DashboardModel;
@ -197,7 +197,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
onDragFinished={size => this.onDragFinished(Pane.Top, size)}
>
{this.renderPanel(styles)}
<div className={styles.tabsWrapper} aria-label={e2e.components.PanelEditor.DataPane.selectors.content}>
<div className={styles.tabsWrapper} aria-label={selectors.components.PanelEditor.DataPane.content}>
<PanelEditorTabs panel={panel} dashboard={dashboard} tabs={tabs} onChangeTab={this.onChangeTab} data={data} />
</div>
</SplitPane>
@ -332,7 +332,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
}
return (
<div className={styles.wrapper} aria-label={e2e.components.PanelEditor.General.selectors.content}>
<div className={styles.wrapper} aria-label={selectors.components.PanelEditor.General.content}>
{this.editorToolbar(styles)}
<div className={styles.verticalSplitPanesWrapper}>
{uiState.isPanelOptionsVisible ? this.renderWithOptionsPane(styles) : this.renderHorizontalSplit(styles)}

View File

@ -1,7 +1,8 @@
import React, { useMemo } from 'react';
import { Button, HorizontalGroup, TextArea, Form, Checkbox } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { Button, Checkbox, Form, HorizontalGroup, TextArea } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import { SaveDashboardFormProps } from '../types';
interface SaveDashboardFormDTO {
@ -37,7 +38,7 @@ export const SaveDashboardForm: React.FC<SaveDashboardFormProps> = ({ dashboard,
label="Save current time range as dashboard default"
name="saveTimerange"
ref={register}
aria-label={e2e.pages.SaveDashboardModal.selectors.saveTimerange}
aria-label={selectors.pages.SaveDashboardModal.saveTimerange}
/>
)}
{hasVariableChanged && (
@ -45,7 +46,7 @@ export const SaveDashboardForm: React.FC<SaveDashboardFormProps> = ({ dashboard,
label="Save current variable values as dashboard default"
name="saveVariables"
ref={register}
aria-label={e2e.pages.SaveDashboardModal.selectors.saveVariables}
aria-label={selectors.pages.SaveDashboardModal.saveVariables}
/>
)}
{(hasVariableChanged || hasTimeChanged) && <div className="gf-form-group" />}
@ -54,7 +55,7 @@ export const SaveDashboardForm: React.FC<SaveDashboardFormProps> = ({ dashboard,
</div>
<HorizontalGroup>
<Button type="submit" aria-label={e2e.pages.SaveDashboardModal.selectors.save}>
<Button type="submit" aria-label={selectors.pages.SaveDashboardModal.save}>
Save
</Button>
<Button variant="secondary" onClick={onCancel}>

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react';
import { e2e } from '@grafana/e2e';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
import { LegacyForms, ClipboardButton, Icon, InfoBox } from '@grafana/ui';
const { Select, Switch } = LegacyForms;
import { SelectableValue, PanelModel, AppEvents } from '@grafana/data';
@ -86,7 +86,7 @@ export class ShareLink extends PureComponent<Props, State> {
render() {
const { panel } = this.props;
const { useCurrentTimeRange, includeTemplateVars, selectedTheme, shareUrl, imageUrl } = this.state;
const selectors = e2e.pages.SharePanelModal.selectors;
const selectors = e2eSelectors.pages.SharePanelModal;
return (
<div className="share-modal-body">

View File

@ -1,6 +1,6 @@
import angular, { ILocationService } from 'angular';
import _ from 'lodash';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import { VariableSrv } from 'app/features/templating/all';
import { CoreEvents } from '../../../../types';
@ -9,7 +9,7 @@ export class SubMenuCtrl {
variables: any;
dashboard: any;
submenuEnabled: boolean;
selectors: typeof e2e.pages.Dashboard.SubMenu.selectors;
selectors: typeof selectors.pages.Dashboard.SubMenu;
/** @ngInject */
constructor(private variableSrv: VariableSrv, private $location: ILocationService) {
@ -19,7 +19,7 @@ export class SubMenuCtrl {
this.dashboard.events.on(CoreEvents.submenuVisibilityChanged, (enabled: boolean) => {
this.submenuEnabled = enabled;
});
this.selectors = e2e.pages.Dashboard.SubMenu.selectors;
this.selectors = selectors.pages.Dashboard.SubMenu;
}
annotationStateChanged() {

View File

@ -1,6 +1,6 @@
import React, { FunctionComponent, useEffect, useState } from 'react';
import { VariableHide, VariableModel } from '../../../templating/types';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import { PickerRenderer } from '../../../variables/pickers/PickerRenderer';
interface Props {
@ -24,7 +24,7 @@ export const SubMenuItems: FunctionComponent<Props> = ({ variables }) => {
<div
key={variable.id}
className="submenu-item gf-form-inline"
aria-label={e2e.pages.Dashboard.SubMenu.selectors.submenuItem}
aria-label={selectors.pages.Dashboard.SubMenu.submenuItem}
>
<PickerRenderer variable={variable} />
</div>

View File

@ -11,7 +11,7 @@ import {
import { TransformationOperationRow } from './TransformationOperationRow';
import { Card, CardProps } from '../../../../core/components/Card/Card';
import { css } from 'emotion';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
interface Props {
onChange: (transformations: DataTransformerConfig[]) => void;
@ -145,7 +145,7 @@ export class TransformationsEditor extends React.PureComponent<Props> {
return (
<CustomScrollbar autoHeightMin="100%">
<Container padding="md">
<div aria-label={e2e.components.TransformTab.selectors.content}>
<div aria-label={selectors.components.TransformTab.content}>
{!hasTransformationsConfigured && this.renderNoAddedTransformsState()}
{hasTransformationsConfigured && this.renderTransformationEditors()}
{hasTransformationsConfigured && this.renderTransformationSelector()}

View File

@ -1,10 +1,10 @@
import React, { Component } from 'react';
import classNames from 'classnames';
import { isEqual } from 'lodash';
import { DataLink, ScopedVars, PanelMenuItem, PanelData, LoadingState, QueryResultMetaNotice } from '@grafana/data';
import { DataLink, LoadingState, PanelData, PanelMenuItem, QueryResultMetaNotice, ScopedVars } from '@grafana/data';
import { AngularComponent } from '@grafana/runtime';
import { ClickOutsideWrapper, Tooltip, Icon } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { ClickOutsideWrapper, Icon, Tooltip } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import PanelHeaderCorner from './PanelHeaderCorner';
import { PanelHeaderMenu } from './PanelHeaderMenu';
@ -159,7 +159,7 @@ export class PanelHeader extends Component<Props, State> {
className="panel-title-container"
onClick={this.onMenuToggle}
onMouseDown={this.onMouseDown}
aria-label={e2e.components.Panels.Panel.selectors.title(title)}
aria-label={selectors.components.Panels.Panel.title(title)}
>
<div className="panel-title">
{Object.values(notices).map(this.renderNotice)}

View File

@ -2,7 +2,7 @@ import React, { FC } from 'react';
import { css } from 'emotion';
import { PanelMenuItem } from '@grafana/data';
import { Icon, IconName, useTheme } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
interface Props {
children: any;
@ -30,7 +30,7 @@ export const PanelHeaderMenuItem: FC<Props & PanelMenuItem> = props => {
<li className={isSubMenu ? 'dropdown-submenu' : undefined}>
<a onClick={props.onClick} href={props.href}>
{props.iconClassName && <Icon name={props.iconClassName as IconName} className={menuIconClassName} />}
<span className="dropdown-item-text" aria-label={e2e.components.Panels.Panel.selectors.headerItems(props.text)}>
<span className="dropdown-item-text" aria-label={selectors.components.Panels.Panel.headerItems(props.text)}>
{props.text}
{isSubMenu && <Icon name="angle-right" className={shortcutIconClassName} />}
</span>

View File

@ -1,9 +1,8 @@
// Libraries
import React, { PureComponent } from 'react';
// Components
import { CustomScrollbar, Icon, IconName, PanelOptionsGroup } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import { FadeIn } from 'app/core/components/Animations/FadeIn';
import { e2e } from '@grafana/e2e';
interface Props {
children: JSX.Element;
@ -92,7 +91,7 @@ export class EditorTabBody extends PureComponent<Props, State> {
className="btn navbar-button"
onClick={onClick}
disabled={view.disabled}
aria-label={e2e.components.QueryEditorToolbarItem.selectors.button(view.title)}
aria-label={selectors.components.QueryEditorToolbarItem.button(view.title)}
>
{view.icon && <Icon name={view.icon as IconName} />} {view.title}
</button>

View File

@ -3,7 +3,7 @@ import React, { PureComponent } from 'react';
// Components
import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
import { QueryOptions } from './QueryOptions';
import { CustomScrollbar, stylesFactory, Button, HorizontalGroup, Modal } from '@grafana/ui';
import { Button, CustomScrollbar, HorizontalGroup, Modal, stylesFactory } from '@grafana/ui';
import { getLocationSrv } from '@grafana/runtime';
import { QueryEditorRows } from './QueryEditorRows';
// Services
@ -20,7 +20,7 @@ import { Unsubscribable } from 'rxjs';
import { DashboardQueryEditor, isSharedDashboardQuery } from 'app/plugins/datasource/dashboard';
import { expressionDatasource, ExpressionDatasourceID } from 'app/features/expressions/ExpressionDatasource';
import { css } from 'emotion';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
interface Props {
panel: PanelModel;
@ -178,7 +178,7 @@ export class QueriesTab extends PureComponent<Props, State> {
<Button
variant="secondary"
onClick={this.openQueryInspector}
aria-label={e2e.components.QueryTab.selectors.queryInspectorButton}
aria-label={selectors.components.QueryTab.queryInspectorButton}
>
Query inspector
</Button>
@ -245,7 +245,7 @@ export class QueriesTab extends PureComponent<Props, State> {
}
return (
<div aria-label={e2e.components.QueryTab.selectors.content}>
<div aria-label={selectors.components.QueryTab.content}>
<QueryEditorRows
queries={panel.targets}
datasource={currentDS}

View File

@ -2,7 +2,7 @@ import React from 'react';
import { GrafanaTheme, PanelPluginMeta } from '@grafana/data';
import { stylesFactory, useTheme, styleMixins } from '@grafana/ui';
import { css, cx } from 'emotion';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import { PanelPluginBadge } from '../../plugins/PluginSignatureBadge';
interface Props {
@ -22,7 +22,7 @@ const VizTypePickerPlugin: React.FC<Props> = ({ isCurrent, plugin, onClick, disa
});
return (
<div className={styles.wrapper} aria-label={e2e.components.PluginVisualization.selectors.item(plugin.name)}>
<div className={styles.wrapper} aria-label={selectors.components.PluginVisualization.item(plugin.name)}>
<div className={cssClass} onClick={disabled ? () => {} : onClick} title={plugin.name}>
<div className={styles.bg} />
<div className={styles.itemContent}>

View File

@ -1,6 +1,6 @@
import React, { PureComponent } from 'react';
import { DataSourceSettings } from '@grafana/data';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
export interface Props {
dataSource: DataSourceSettings;
@ -20,7 +20,7 @@ export class DataSourcesListItem extends PureComponent<Props> {
<img src={dataSource.typeLogoUrl} alt={dataSource.name} />
</figure>
<div className="card-item-details">
<div className="card-item-name" aria-label={e2e.pages.DataSources.selectors.dataSources(dataSource.name)}>
<div className="card-item-name" aria-label={selectors.pages.DataSources.dataSources(dataSource.name)}>
{dataSource.name}
{dataSource.isDefault && <span className="btn btn-secondary btn-small card-item-label">default</span>}
</div>

View File

@ -2,8 +2,8 @@ import React, { FC, PureComponent } from 'react';
import { connect } from 'react-redux';
import { hot } from 'react-hot-loader';
import { DataSourcePluginMeta, NavModel } from '@grafana/data';
import { List, LinkButton, Button } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { Button, LinkButton, List } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import Page from 'app/core/components/Page/Page';
import { DataSourcePluginCategory, StoreState } from 'app/types';
@ -126,7 +126,7 @@ const DataSourceTypeCard: FC<DataSourceTypeCardProps> = props => {
<Card
title={plugin.name}
description={plugin.info.description}
ariaLabel={e2e.pages.AddDataSource.selectors.dataSourcePlugins(plugin.name)}
ariaLabel={selectors.pages.AddDataSource.dataSourcePlugins(plugin.name)}
logoUrl={plugin.info.logos.small}
actions={
<>
@ -154,7 +154,7 @@ const DataSourceTypeCard: FC<DataSourceTypeCardProps> = props => {
}
className={isPhantom && 'add-data-source-item--phantom'}
onClick={onClick}
aria-label={e2e.pages.AddDataSource.selectors.dataSourcePlugins(plugin.name)}
aria-label={selectors.pages.AddDataSource.dataSourcePlugins(plugin.name)}
/>
);
};

View File

@ -1,7 +1,8 @@
import React, { FC } from 'react';
import { InlineFormLabel, LegacyForms } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
const { Input, Switch } = LegacyForms;
import { e2e } from '@grafana/e2e';
export interface Props {
dataSourceName: string;
@ -30,7 +31,7 @@ const BasicSettings: FC<Props> = ({ dataSourceName, isDefault, onDefaultChange,
placeholder="Name"
onChange={event => onNameChange(event.target.value)}
required
aria-label={e2e.pages.DataSource.selectors.name}
aria-label={selectors.pages.DataSource.name}
/>
</div>
<Switch

View File

@ -1,5 +1,5 @@
import React, { FC } from 'react';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import config from 'app/core/config';
@ -19,7 +19,7 @@ const ButtonRow: FC<Props> = ({ isReadOnly, onDelete, onSubmit, onTest }) => {
className="btn btn-primary"
disabled={isReadOnly}
onClick={event => onSubmit(event)}
aria-label={e2e.pages.DataSource.selectors.saveAndTest}
aria-label={selectors.pages.DataSource.saveAndTest}
>
Save &amp; Test
</button>
@ -34,7 +34,7 @@ const ButtonRow: FC<Props> = ({ isReadOnly, onDelete, onSubmit, onTest }) => {
className="btn btn-danger"
disabled={isReadOnly}
onClick={onDelete}
aria-label={e2e.pages.DataSource.selectors.delete}
aria-label={selectors.pages.DataSource.delete}
>
Delete
</button>

View File

@ -2,7 +2,8 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import isString from 'lodash/isString';
import { e2e } from '@grafana/e2e';
import { Icon } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
// Components
import Page from 'app/core/components/Page/Page';
import { GenericDataSourcePlugin, PluginSettings } from './PluginSettings';
@ -24,7 +25,6 @@ import { getRouteParamsId } from 'app/core/selectors/location';
// Types
import { CoreEvents, StoreState } from 'app/types/';
import { DataSourcePluginMeta, DataSourceSettings, NavModel, UrlQueryMap } from '@grafana/data';
import { Icon } from '@grafana/ui';
import { getDataSourceLoadingNav } from '../state/navModel';
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
import { dataSourceLoaded, setDataSourceName, setIsDefault } from '../state/reducers';
@ -204,12 +204,12 @@ export class DataSourceSettingsPage extends PureComponent<Props> {
<div className="gf-form-group">
{testingStatus && testingStatus.message && (
<div className={`alert-${testingStatus.status} alert`} aria-label={e2e.pages.DataSource.selectors.alert}>
<div className={`alert-${testingStatus.status} alert`} aria-label={selectors.pages.DataSource.alert}>
<div className="alert-icon">
{testingStatus.status === 'error' ? <Icon name="exclamation-triangle" /> : <Icon name="check" />}
</div>
<div className="alert-body">
<div className="alert-title" aria-label={e2e.pages.DataSource.selectors.alertMessage}>
<div className="alert-title" aria-label={selectors.pages.DataSource.alertMessage}>
{testingStatus.message}
</div>
</div>

View File

@ -1,23 +1,34 @@
// Libraries
import React from 'react';
import { hot } from 'react-hot-loader';
import { css, cx } from 'emotion';
import { compose } from 'redux';
import { connect } from 'react-redux';
import AutoSizer from 'react-virtualized-auto-sizer';
import memoizeOne from 'memoize-one';
import { selectors } from '@grafana/e2e-selectors';
import { ErrorBoundaryAlert, stylesFactory, withTheme } from '@grafana/ui';
import {
AbsoluteTimeRange,
DataQuery,
DataSourceApi,
ExploreMode,
GrafanaTheme,
GraphSeriesXY,
LoadingState,
PanelData,
RawTimeRange,
TimeRange,
TimeZone,
} from '@grafana/data';
// Services & Utils
import store from 'app/core/store';
import config from 'app/core/config';
// Components
import { ErrorBoundaryAlert, stylesFactory, withTheme } from '@grafana/ui';
import LogsContainer from './LogsContainer';
import QueryRows from './QueryRows';
import TableContainer from './TableContainer';
import RichHistoryContainer from './RichHistory/RichHistoryContainer';
// Actions
import {
addQueryRow,
changeSize,
initializeExplore,
modifyQueries,
@ -25,23 +36,8 @@ import {
scanStart,
setQueries,
toggleGraph,
addQueryRow,
updateTimeRange,
} from './state/actions';
// Types
import {
AbsoluteTimeRange,
DataQuery,
DataSourceApi,
GraphSeriesXY,
PanelData,
RawTimeRange,
TimeRange,
TimeZone,
LoadingState,
ExploreMode,
GrafanaTheme,
} from '@grafana/data';
import { ExploreId, ExploreItemState, ExploreUIState, ExploreUpdateState, ExploreUrlState } from 'app/types/explore';
import { StoreState } from 'app/types';
@ -49,10 +45,10 @@ import {
DEFAULT_RANGE,
DEFAULT_UI_STATE,
ensureQueries,
getTimeRangeFromUrl,
getTimeRange,
lastUsedDatasourceKeyForOrgId,
getFirstNonQueryRowSpecificError,
getTimeRange,
getTimeRangeFromUrl,
lastUsedDatasourceKeyForOrgId,
} from 'app/core/utils/explore';
import { Emitter } from 'app/core/utils/emitter';
import { ExploreToolbar } from './ExploreToolbar';
@ -63,8 +59,6 @@ import { scanStopAction } from './state/actionTypes';
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 {
@ -320,7 +314,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
const queryError = getFirstNonQueryRowSpecificError(queryErrors);
return (
<div className={exploreClass} ref={this.getRef} aria-label={e2e.pages.Explore.General.selectors.container}>
<div className={exploreClass} ref={this.getRef} aria-label={selectors.pages.Explore.General.container}>
<ExploreToolbar exploreId={exploreId} onChangeTime={this.onChangeTime} />
{datasourceMissing ? this.renderEmptyState() : null}
{datasourceInstance && (

View File

@ -1,9 +1,9 @@
import React from 'react';
import { RefreshPicker } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import memoizeOne from 'memoize-one';
import { css } from 'emotion';
import classNames from 'classnames';
import { e2e } from '@grafana/e2e';
import { ResponsiveButton } from './ResponsiveButton';
@ -43,7 +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}
aria-label={selectors.pages.Explore.General.runButton}
/>
);

View File

@ -1,7 +1,7 @@
import React, { FC, useCallback, CSSProperties } from 'react';
import { css, cx } from 'emotion';
import { GrafanaTheme } from '@grafana/data';
import { e2e } from '@grafana/e2e';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
import { useTheme, TagList, styleMixins, stylesFactory } from '@grafana/ui';
import { DashboardSectionItem, OnToggleChecked } from '../types';
import { SearchCheckbox } from './SearchCheckbox';
@ -15,7 +15,7 @@ export interface Props {
style?: CSSProperties;
}
const { selectors } = e2e.pages.Dashboards;
const selectors = e2eSelectors.pages.Dashboards;
export const SearchItem: FC<Props> = ({ item, editable, onToggleChecked, onTagSelected, style }) => {
const theme = useTheme();

View File

@ -1,6 +1,6 @@
import React, { PureComponent } from 'react';
import { VariableQueryProps } from 'app/types/plugins';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
export default class DefaultVariableQueryEditor extends PureComponent<VariableQueryProps, any> {
constructor(props: VariableQueryProps) {
@ -38,7 +38,7 @@ export default class DefaultVariableQueryEditor extends PureComponent<VariableQu
onBlur={this.onBlur}
placeholder="metric name or tags query"
required
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.selectors.queryOptionsQueryInput}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsQueryInput}
/>
</div>
);

View File

@ -1,6 +1,6 @@
import _ from 'lodash';
import { AppEvents } from '@grafana/data';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import coreModule from 'app/core/core_module';
import { variableTypes } from './types';
@ -60,10 +60,10 @@ export class VariableEditorCtrl {
];
$scope.selectors = {
...e2e.pages.Dashboard.Settings.Variables.List.selectors,
...e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors,
...e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.selectors,
...e2e.pages.Dashboard.Settings.Variables.Edit.ConstantVariable.selectors,
...selectors.pages.Dashboard.Settings.Variables.List,
...selectors.pages.Dashboard.Settings.Variables.Edit.General,
...selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable,
...selectors.pages.Dashboard.Settings.Variables.Edit.ConstantVariable,
};
$scope.init = () => {

View File

@ -1,5 +1,5 @@
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import { ConstantVariableModel } from '../../templating/types';
import { VariableEditorProps } from '../editor/types';
@ -36,9 +36,7 @@ export class ConstantVariableEditor extends PureComponent<Props> {
onChange={this.onChange}
onBlur={this.onBlur}
placeholder="your metric prefix"
aria-label={
e2e.pages.Dashboard.Settings.Variables.Edit.ConstantVariable.selectors.constantOptionsQueryInput
}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.ConstantVariable.constantOptionsQueryInput}
/>
</div>
</div>

View File

@ -1,11 +1,12 @@
import React, { FunctionComponent, useCallback } from 'react';
import { LegacyForms } from '@grafana/ui';
const { Switch } = LegacyForms;
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import { VariableWithMultiSupport } from '../../templating/types';
import { VariableEditorProps } from './types';
import { VariableIdentifier, toVariableIdentifier } from '../state/types';
import { toVariableIdentifier, VariableIdentifier } from '../state/types';
const { Switch } = LegacyForms;
export interface SelectionOptionsEditorProps<Model extends VariableWithMultiSupport = VariableWithMultiSupport>
extends VariableEditorProps<Model> {
@ -37,7 +38,7 @@ export const SelectionOptionsEditor: FunctionComponent<SelectionOptionsEditorPro
<div className="section gf-form-group">
<h5 className="section-heading">Selection Options</h5>
<div className="section">
<div aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors.selectionOptionsMultiSwitch}>
<div aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsMultiSwitch}>
<Switch
label="Multi-value"
labelClass="width-10"
@ -46,9 +47,7 @@ export const SelectionOptionsEditor: FunctionComponent<SelectionOptionsEditorPro
tooltip={'Enables multiple values to be selected at the same time'}
/>
</div>
<div
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors.selectionOptionsIncludeAllSwitch}
>
<div aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsIncludeAllSwitch}>
<Switch
label="Include All option"
labelClass="width-10"
@ -67,7 +66,7 @@ export const SelectionOptionsEditor: FunctionComponent<SelectionOptionsEditorPro
value={props.variable.allValue ?? ''}
onChange={onAllValueChanged}
placeholder="blank = auto"
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors.selectionOptionsCustomAllInput}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput}
/>
</div>
)}

View File

@ -1,8 +1,9 @@
import React, { MouseEvent, PureComponent } from 'react';
import { Icon } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import { NEW_VARIABLE_ID, toVariableIdentifier, toVariablePayload, VariableIdentifier } from '../state/types';
import { StoreState } from '../../../types';
import { e2e } from '@grafana/e2e';
import { Icon } from '@grafana/ui';
import { VariableEditorList } from './VariableEditorList';
import { VariableEditorEditor } from './VariableEditorEditor';
import { MapDispatchToProps, MapStateToProps } from 'react-redux';
@ -69,7 +70,7 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
<h3 className="dashboard-settings__header">
<a
onClick={this.onChangeToListMode}
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors.headerLink}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.headerLink}
>
Variables
</a>
@ -77,7 +78,7 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
<span>
<Icon
name="angle-right"
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors.modeLabelNew}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.modeLabelNew}
/>
New
</span>
@ -86,7 +87,7 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
<span>
<Icon
name="angle-right"
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors.modeLabelEdit}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.modeLabelEdit}
/>
Edit
</span>
@ -99,7 +100,7 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
type="button"
className="btn btn-primary"
onClick={this.onNewVariable}
aria-label={e2e.pages.Dashboard.Settings.Variables.List.selectors.newButton}
aria-label={selectors.pages.Dashboard.Settings.Variables.List.newButton}
>
New
</a>

View File

@ -2,7 +2,8 @@ import React, { ChangeEvent, FormEvent, PureComponent } from 'react';
import isEqual from 'lodash/isEqual';
import { AppEvents, VariableType } from '@grafana/data';
import { InlineFormLabel } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import { variableAdapters } from '../adapters';
import { NEW_VARIABLE_ID, toVariablePayload, VariableIdentifier } from '../state/types';
import { VariableHide, VariableModel } from '../../templating/types';
@ -129,7 +130,7 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props> {
required
value={this.props.editor.name}
onChange={this.onNameChange}
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors.generalNameInput}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInput}
/>
</div>
<div className="gf-form max-width-19">
@ -141,7 +142,7 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props> {
className="gf-form-input"
value={this.props.variable.type}
onChange={this.onTypeChange}
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors.generalTypeSelect}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelect}
>
{variableAdapters.list().map(({ id, name }) => (
<option key={id} label={name} value={id}>
@ -168,7 +169,7 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props> {
value={this.props.variable.label ?? ''}
onChange={this.onLabelChange}
placeholder="optional display name"
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors.generalLabelInput}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInput}
/>
</div>
<div className="gf-form max-width-19">
@ -178,7 +179,7 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props> {
className="gf-form-input"
value={this.props.variable.hide}
onChange={this.onHideChange}
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors.generalHideSelect}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalHideSelect}
>
<option label="" value={VariableHide.dontHide}>
{''}
@ -204,7 +205,7 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props> {
<button
type="submit"
className="btn btn-primary"
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors.updateButton}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.updateButton}
>
Update
</button>
@ -213,7 +214,7 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props> {
<button
type="submit"
className="btn btn-primary"
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors.addButton}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.addButton}
>
Add
</button>

View File

@ -1,9 +1,10 @@
import React, { MouseEvent, PureComponent } from 'react';
import { e2e } from '@grafana/e2e';
import { Icon } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import EmptyListCTA from '../../../core/components/EmptyListCTA/EmptyListCTA';
import { QueryVariableModel, VariableModel } from '../../templating/types';
import { toVariableIdentifier, VariableIdentifier } from '../state/types';
import { Icon } from '@grafana/ui';
export interface Props {
variables: VariableModel[];
@ -72,7 +73,7 @@ export class VariableEditorList extends PureComponent<Props> {
<div>
<table
className="filter-table filter-table--hover"
aria-label={e2e.pages.Dashboard.Settings.Variables.List.selectors.table}
aria-label={selectors.pages.Dashboard.Settings.Variables.List.table}
>
<thead>
<tr>
@ -90,7 +91,7 @@ export class VariableEditorList extends PureComponent<Props> {
<span
onClick={event => this.onEditClick(event, toVariableIdentifier(variable))}
className="pointer template-variable"
aria-label={e2e.pages.Dashboard.Settings.Variables.List.selectors.tableRowNameFields(
aria-label={selectors.pages.Dashboard.Settings.Variables.List.tableRowNameFields(
variable.name
)}
>
@ -101,7 +102,7 @@ export class VariableEditorList extends PureComponent<Props> {
style={{ maxWidth: '200px' }}
onClick={event => this.onEditClick(event, toVariableIdentifier(variable))}
className="pointer max-width"
aria-label={e2e.pages.Dashboard.Settings.Variables.List.selectors.tableRowDefinitionFields(
aria-label={selectors.pages.Dashboard.Settings.Variables.List.tableRowDefinitionFields(
variable.name
)}
>
@ -113,7 +114,7 @@ export class VariableEditorList extends PureComponent<Props> {
<Icon
onClick={event => this.onChangeVariableOrder(event, variable, MoveType.up)}
name="arrow-up"
aria-label={e2e.pages.Dashboard.Settings.Variables.List.selectors.tableRowArrowUpButtons(
aria-label={selectors.pages.Dashboard.Settings.Variables.List.tableRowArrowUpButtons(
variable.name
)}
/>
@ -124,7 +125,7 @@ export class VariableEditorList extends PureComponent<Props> {
<Icon
onClick={event => this.onChangeVariableOrder(event, variable, MoveType.down)}
name="arrow-down"
aria-label={e2e.pages.Dashboard.Settings.Variables.List.selectors.tableRowArrowDownButtons(
aria-label={selectors.pages.Dashboard.Settings.Variables.List.tableRowArrowDownButtons(
variable.name
)}
/>
@ -134,7 +135,7 @@ export class VariableEditorList extends PureComponent<Props> {
<a
onClick={event => this.onDuplicateVariable(event, toVariableIdentifier(variable))}
className="btn btn-inverse btn-small"
aria-label={e2e.pages.Dashboard.Settings.Variables.List.selectors.tableRowDuplicateButtons(
aria-label={selectors.pages.Dashboard.Settings.Variables.List.tableRowDuplicateButtons(
variable.name
)}
>
@ -145,7 +146,7 @@ export class VariableEditorList extends PureComponent<Props> {
<a
onClick={event => this.onRemoveVariable(event, toVariableIdentifier(variable))}
className="btn btn-danger btn-small"
aria-label={e2e.pages.Dashboard.Settings.Variables.List.selectors.tableRowRemoveButtons(
aria-label={selectors.pages.Dashboard.Settings.Variables.List.tableRowRemoveButtons(
variable.name
)}
>

View File

@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useState } from 'react';
import { VariableModel, VariableOption, VariableWithOptions } from '../../templating/types';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
export interface VariableValuesPreviewProps {
variable: VariableModel;
@ -30,7 +30,7 @@ export const VariableValuesPreview: React.FunctionComponent<VariableValuesPrevie
<div className="gf-form" key={`${o.value}-${index}`}>
<span
className="gf-form-label"
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.General.selectors.previewOfValuesOption}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption}
>
{o.text}
</span>

View File

@ -1,6 +1,6 @@
import React, { FunctionComponent, useMemo } from 'react';
import { VariableHide, VariableModel } from '../../templating/types';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import { variableAdapters } from '../adapters';
interface Props {
@ -20,7 +20,7 @@ export const PickerRenderer: FunctionComponent<Props> = props => {
{props.variable.hide === VariableHide.dontHide && (
<label
className="gf-form-label gf-form-label--variable"
aria-label={e2e.pages.Dashboard.SubMenu.selectors.submenuItemLabels(labelOrName)}
aria-label={selectors.pages.Dashboard.SubMenu.submenuItemLabels(labelOrName)}
>
{labelOrName}
</label>

View File

@ -1,6 +1,7 @@
import React, { PureComponent } from 'react';
import { getTagColorsFromName, Icon } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
import { VariableTag } from '../../../templating/types';
interface Props {
@ -22,7 +23,7 @@ export class VariableLink extends PureComponent<Props> {
<a
onClick={this.onClick}
className="variable-value-link"
aria-label={e2e.pages.Dashboard.SubMenu.selectors.submenuItemValueDropDownValueLinkTexts(`${text}`)}
aria-label={selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts(`${text}`)}
>
{text}
{tags.map(tag => {

View File

@ -1,6 +1,7 @@
import React, { PureComponent } from 'react';
import { getTagColorsFromName, Tooltip, Icon } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { getTagColorsFromName, Icon, Tooltip } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import { VariableOption, VariableTag } from '../../../templating/types';
export interface Props {
@ -42,7 +43,7 @@ export class VariableOptions extends PureComponent<Props> {
return (
<div
className={`${multi ? 'variable-value-dropdown multi' : 'variable-value-dropdown single'}`}
aria-label={e2e.pages.Dashboard.SubMenu.selectors.submenuItemValueDropDownDropDown}
aria-label={selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownDropDown}
>
<div className="variable-options-wrapper">
<div className="variable-options-column">
@ -95,7 +96,7 @@ export class VariableOptions extends PureComponent<Props> {
return (
<a key={`${option.value}`} className={highlightClass} onClick={this.onToggle(option)}>
<span className="variable-option-icon"></span>
<span aria-label={e2e.pages.Dashboard.SubMenu.selectors.submenuItemValueDropDownOptionTexts(`${option.text}`)}>
<span aria-label={selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts(`${option.text}`)}>
{option.text}
</span>
</a>

View File

@ -1,7 +1,6 @@
import React, { ChangeEvent, PureComponent } from 'react';
import { e2e } from '@grafana/e2e';
import { InlineFormLabel, LegacyForms } from '@grafana/ui';
const { Switch } = LegacyForms;
import { selectors } from '@grafana/e2e-selectors';
import templateSrv from '../../templating/template_srv';
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
@ -16,6 +15,8 @@ import { connectWithStore } from '../../../core/utils/connectWithReduxStore';
import { toVariableIdentifier } from '../state/types';
import { changeVariableMultiValue } from '../state/actions';
const { Switch } = LegacyForms;
export interface OwnProps extends VariableEditorProps<QueryVariableModel> {}
interface ConnectedProps {
@ -131,7 +132,7 @@ export class QueryVariableEditorUnConnected extends PureComponent<Props, State>
onChange={this.onDataSourceChange}
required
aria-label={
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.selectors.queryOptionsDataSourceSelect
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsDataSourceSelect
}
>
{this.props.editor.extended?.dataSources.length &&
@ -153,9 +154,7 @@ export class QueryVariableEditorUnConnected extends PureComponent<Props, State>
className="gf-form-input"
value={this.props.variable.refresh}
onChange={this.onRefreshChange}
aria-label={
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.selectors.queryOptionsRefreshSelect
}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRefreshSelect}
>
<option label="Never" value={VariableRefresh.never}>
Never
@ -194,7 +193,7 @@ export class QueryVariableEditorUnConnected extends PureComponent<Props, State>
value={this.state.regex ?? this.props.variable.regex}
onChange={this.onRegExChange}
onBlur={this.onRegExBlur}
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.selectors.queryOptionsRegExInput}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInput}
/>
</div>
<div className="gf-form max-width-21">
@ -206,7 +205,7 @@ export class QueryVariableEditorUnConnected extends PureComponent<Props, State>
className="gf-form-input"
value={this.props.variable.sort}
onChange={this.onSortChange}
aria-label={e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.selectors.queryOptionsSortSelect}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsSortSelect}
>
<option label="Disabled" value={VariableSort.disabled}>
Disabled
@ -249,9 +248,7 @@ export class QueryVariableEditorUnConnected extends PureComponent<Props, State>
<div className="gf-form-group">
<h5>Value groups/tags (Experimental feature)</h5>
<div
aria-label={
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.selectors.valueGroupsTagsEnabledSwitch
}
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.valueGroupsTagsEnabledSwitch}
>
<Switch
label="Enabled"
@ -272,7 +269,7 @@ export class QueryVariableEditorUnConnected extends PureComponent<Props, State>
onChange={this.onTagsQueryChange}
onBlur={this.onTagsQueryBlur}
aria-label={
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.selectors.valueGroupsTagsTagsQueryInput
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.valueGroupsTagsTagsQueryInput
}
/>
</div>
@ -286,8 +283,7 @@ export class QueryVariableEditorUnConnected extends PureComponent<Props, State>
onChange={this.onTagValuesQueryChange}
onBlur={this.onTagValuesQueryBlur}
aria-label={
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.selectors
.valueGroupsTagsTagsValuesQueryInput
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.valueGroupsTagsTagsValuesQueryInput
}
/>
</div>

Some files were not shown because too many files have changed in this diff Show More