Chore: Reduce pa11y-ci errors (#41787)

* Chore: moves alert to test-id

* Chore: moves Dashboard navigation to test-id

* Chore: moves Bar gauge value to data-testid

* Chore move Folder picker select container to data-testid

* Chore: moves Time zone picker select container to data-testid

* Chore: moves Choose starting day of the week to data-testid

* Chore: change tabIndex on search input

* Chore: moves various search related aria-lables to data-testid

* Chore: connects label to select input on alerting page

* Chore: connects TimeZonePicker and WeekStartPicker with labels

* Chore: moves CallToActionButton to data-testid

* Chore: move user home preferences select to data-testid

* Chore: lower all thresholds
This commit is contained in:
Hugo Häggmark
2021-11-17 14:45:45 +01:00
committed by GitHub
parent 4f9c096599
commit 5498203507
37 changed files with 221 additions and 116 deletions

View File

@@ -25,37 +25,37 @@ var config = {
"click element button[aria-label='Login button']",
"wait for element [aria-label='Skip change password button'] to be visible",
],
threshold: 14,
threshold: 13,
rootElement: '.main-view',
},
{
url: '${HOST}/?orgId=1',
wait: 500,
threshold: 1,
threshold: 0,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge',
wait: 500,
rootElement: '.main-view',
threshold: 51,
threshold: 0,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=settings',
wait: 500,
rootElement: '.main-view',
threshold: 54,
threshold: 0,
},
{
url: '${HOST}/?orgId=1&search=open',
wait: 500,
rootElement: '.main-view',
threshold: 8,
threshold: 0,
},
{
url: '${HOST}/alerting/list',
wait: 500,
rootElement: '.main-view',
threshold: 1,
threshold: 0,
},
{
url: '${HOST}/datasources',
@@ -85,19 +85,19 @@ var config = {
url: '${HOST}/org',
wait: 500,
rootElement: '.main-view',
threshold: 3,
threshold: 0,
},
{
url: '${HOST}/org/apikeys',
wait: 500,
rootElement: '.main-view',
threshold: 1,
threshold: 0,
},
{
url: '${HOST}/dashboards',
wait: 500,
rootElement: '.main-view',
threshold: 2,
threshold: 0,
},
],
};

View File

@@ -12,7 +12,7 @@ e2e.scenario({
e2e.flows.openDashboard({ uid: 'O6f11TZWk' });
e2e()
.get(`[data-panelid=6] [aria-label^="${selectors.components.Panels.Visualization.BarGauge.value}"]`)
.get(`[data-panelid=6] [data-testid^="${selectors.components.Panels.Visualization.BarGauge.valueV2}"]`)
.should('have.css', 'color', 'rgb(242, 73, 92)')
.contains('100');
},

View File

@@ -50,7 +50,7 @@ e2e.scenario({
e2e.components.PageToolbar.item('Dashboard settings').click();
e2e.components.TimeZonePicker.container()
e2e.components.TimeZonePicker.containerV2()
.should('be.visible')
.within(() => {
e2e.components.Select.singleValue().should('be.visible').should('have.text', 'Coordinated Universal Time');

View File

@@ -10,7 +10,7 @@ e2e.scenario({
e2e.flows.openDashboard({ uid: '5SdHCadmz' });
e2e.components.PageToolbar.item('Dashboard settings').click();
e2e.components.FolderPicker.container()
e2e.components.FolderPicker.containerV2()
.should('be.visible')
.within(() => {
e2e().get('#dashboard-folder-input').should('be.visible').click();
@@ -18,7 +18,7 @@ e2e.scenario({
e2e.components.Select.option().should('be.visible').first().click();
e2e.components.FolderPicker.container()
e2e.components.FolderPicker.containerV2()
.should('be.visible')
.within(() => {
e2e().get('#dashboard-folder-input').should('exist').should('have.focus');
@@ -26,7 +26,7 @@ e2e.scenario({
e2e.pages.Dashboard.Settings.General.title().click();
e2e.components.FolderPicker.container()
e2e.components.FolderPicker.containerV2()
.should('be.visible')
.within(() => {
e2e().get('#dashboard-folder-input').should('exist').should('not.have.focus');

View File

@@ -72,7 +72,11 @@ export const Components = {
},
},
BarGauge: {
/**
* @deprecated use valueV2 from Grafana 8.3 instead
*/
value: 'Bar gauge value',
valueV2: 'data-testid Bar gauge value',
},
PieChart: {
svgSlice: 'Pie Chart Slice',
@@ -164,7 +168,11 @@ export const Components = {
content: 'Alert editor tab content',
},
Alert: {
/**
* @deprecated use alertV2 from Grafana 8.3 instead
*/
alert: (severity: string) => `Alert ${severity}`,
alertV2: (severity: string) => `data-testid Alert ${severity}`,
},
TransformTab: {
content: 'Transform editor tab content',
@@ -210,7 +218,11 @@ export const Components = {
content: 'Field overrides editor content',
},
FolderPicker: {
/**
* @deprecated use containerV2 from Grafana 8.3 instead
*/
container: 'Folder picker select container',
containerV2: 'data-testid Folder picker select container',
input: 'Select a folder',
},
ReadonlyFolderPicker: {
@@ -225,10 +237,19 @@ export const Components = {
inputV2: 'Select a data source',
},
TimeZonePicker: {
/**
* @deprecated use TimeZonePicker.containerV2 from Grafana 8.3 instead
*/
container: 'Time zone picker select container',
containerV2: 'data-testid Time zone picker select container',
},
WeekStartPicker: {
/**
* @deprecated use WeekStartPicker.containerV2 from Grafana 8.3 instead
*/
container: 'Choose starting day of the week',
containerV2: 'data-testid Choose starting day of the week',
placeholder: 'Choose starting day of the week',
},
TraceViewer: {
spanBar: () => '[data-test-id="SpanBar--wrapper"]',
@@ -239,8 +260,20 @@ export const Components = {
select: (name: string) => `Value picker select ${name}`,
},
Search: {
/**
* @deprecated use sectionV2 from Grafana 8.3 instead
*/
section: 'Search section',
sectionV2: 'data-testid Search section',
/**
* @deprecated use itemsV2 from Grafana 8.3 instead
*/
items: 'Search items',
itemsV2: 'data-testid Search items',
collapseFolder: (sectionId: string) => `data-testid Collapse folder ${sectionId}`,
expandFolder: (sectionId: string) => `data-testid Expand folder ${sectionId}`,
dashboardItem: (item: string) => `${Components.Search.dashboardItems} ${item}`,
dashboardItems: 'data-testid Dashboard search item',
},
DashboardLinks: {
container: 'data-testid Dashboard link container',
@@ -251,7 +284,11 @@ export const Components = {
icon: 'Loading indicator',
},
CallToActionCard: {
/**
* @deprecated use buttonV2 from Grafana 8.3 instead
*/
button: (name: string) => `Call to action button ${name}`,
buttonV2: (name: string) => `data-testid Call to action button ${name}`,
},
DataLinksContextMenu: {
singleLink: 'Data link',

View File

@@ -43,7 +43,11 @@ export const Pages = {
Dashboard: {
url: (uid: string) => `/d/${uid}`,
DashNav: {
/**
* @deprecated use navV2 from Grafana 8.3 instead
*/
nav: 'Dashboard navigation',
navV2: 'data-testid Dashboard navigation',
},
SubMenu: {
submenu: 'Dashboard submenu',
@@ -61,12 +65,19 @@ export const Pages = {
sectionItems: (item: string) => `Dashboard settings section item ${item}`,
saveDashBoard: 'Dashboard settings aside actions Save button',
saveAsDashBoard: 'Dashboard settings aside actions Save As button',
/**
* @deprecated use components.TimeZonePicker.containerV2 from Grafana 8.3 instead
*/
timezone: 'Time zone picker select container',
title: 'Dashboard settings page title',
},
Annotations: {
List: {
/**
* @deprecated use addAnnotationCTAV2 from Grafana 8.3 instead
*/
addAnnotationCTA: Components.CallToActionCard.button('Add annotation query'),
addAnnotationCTAV2: Components.CallToActionCard.buttonV2('Add annotation query'),
},
Settings: {
name: 'Annotations settings name input',
@@ -74,7 +85,11 @@ export const Pages = {
},
Variables: {
List: {
/**
* @deprecated use addVariableCTAV2 from Grafana 8.3 instead
*/
addVariableCTA: Components.CallToActionCard.button('Add variable'),
addVariableCTAV2: Components.CallToActionCard.buttonV2('Add variable'),
newButton: 'Variable editor New variable button',
table: 'Variable editor Table',
tableRowNameFields: (variableName: string) => `Variable editor Table Name field ${variableName}`,
@@ -124,6 +139,9 @@ export const Pages = {
},
Dashboards: {
url: '/dashboards',
/**
* @deprecated use components.Search.dashboardItem from Grafana 8.3 instead
*/
dashboards: (title: string) => `Dashboard search item ${title}`,
},
SaveDashboardAsModal: {

View File

@@ -101,7 +101,11 @@ export const addDashboard = (config?: Partial<AddDashboardConfig>) => {
const addAnnotation = (config: AddAnnotationConfig, isFirst: boolean) => {
if (isFirst) {
e2e.pages.Dashboard.Settings.Annotations.List.addAnnotationCTA().click();
if (e2e.pages.Dashboard.Settings.Annotations.List.addAnnotationCTAV2) {
e2e.pages.Dashboard.Settings.Annotations.List.addAnnotationCTAV2().click();
} else {
e2e.pages.Dashboard.Settings.Annotations.List.addAnnotationCTA().click();
}
} else {
cy.contains('New query').click();
}
@@ -145,7 +149,11 @@ const addVariable = (config: PartialAddVariableConfig, isFirst: boolean): AddVar
};
if (isFirst) {
e2e.pages.Dashboard.Settings.Variables.List.addVariableCTA().click();
if (e2e.pages.Dashboard.Settings.Variables.List.addVariableCTAV2) {
e2e.pages.Dashboard.Settings.Variables.List.addVariableCTAV2().click();
} else {
e2e.pages.Dashboard.Settings.Variables.List.addVariableCTA().click();
}
} else {
e2e.pages.Dashboard.Settings.Variables.List.newButton().click();
}

View File

@@ -1,5 +1,9 @@
import { e2e } from '../index';
export const assertSuccessNotification = () => {
e2e().get('[aria-label^="Alert success"]').should('exist');
if (e2e.components.Alert.alertV2) {
e2e.components.Alert.alertV2('success').should('exist');
} else {
e2e.components.Alert.alert('success').should('exist');
}
};

View File

@@ -41,7 +41,11 @@ const uiDelete = (uid: string, title: string) => {
e2e.pages.Dashboards.visit();
// @todo replace `e2e.pages.Dashboards.dashboards` with this when argument is empty
e2e()
.get('[aria-label^="Dashboard search item "]')
.each((item) => e2e().wrap(item).should('not.contain', title));
if (e2e.components.Search.dashboardItems) {
e2e.components.Search.dashboardItems().each((item) => e2e().wrap(item).should('not.contain', title));
} else {
e2e()
.get('[aria-label^="Dashboard search item "]')
.each((item) => e2e().wrap(item).should('not.contain', title));
}
};

View File

@@ -13,11 +13,19 @@ export const setTimeRange = ({ from, to, zone }: TimeRangeConfig) => {
if (zone) {
e2e().contains('button', 'Change time settings').click();
selectOption({
clickToOpen: true,
container: e2e.components.TimeZonePicker.container(),
optionText: zone,
});
if (e2e.components.TimeZonePicker.containerV2) {
selectOption({
clickToOpen: true,
container: e2e.components.TimeZonePicker.containerV2(),
optionText: zone,
});
} else {
selectOption({
clickToOpen: true,
container: e2e.components.TimeZonePicker.container(),
optionText: zone,
});
}
}
// For smaller screens

View File

@@ -47,7 +47,7 @@ export const Alert = React.forwardRef<HTMLDivElement, Props>(
<div
ref={ref}
className={cx(styles.alert, className)}
aria-label={selectors.components.Alert.alert(severity)}
data-testid={selectors.components.Alert.alertV2(severity)}
{...restProps}
>
<div className={styles.icon}>

View File

@@ -1,26 +1,26 @@
// Library
import React, { PureComponent, CSSProperties, ReactNode } from 'react';
import React, { CSSProperties, PureComponent, ReactNode } from 'react';
import tinycolor from 'tinycolor2';
import {
TimeSeriesValue,
DisplayProcessor,
DisplayValue,
formattedValueToString,
DisplayValueAlignmentFactors,
FALLBACK_COLOR,
FieldColorModeId,
FieldConfig,
FormattedValue,
formattedValueToString,
GAUGE_DEFAULT_MAXIMUM,
GAUGE_DEFAULT_MINIMUM,
DisplayValueAlignmentFactors,
ThresholdsMode,
DisplayProcessor,
FieldConfig,
FieldColorModeId,
getFieldColorMode,
FALLBACK_COLOR,
TextDisplayOptions,
ThresholdsMode,
TimeSeriesValue,
VizOrientation,
} from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { FormattedValueDisplay } from '../FormattedValueDisplay/FormattedValueDisplay';
import { measureText, calculateFontSize } from '../../utils/measureText';
import { calculateFontSize, measureText } from '../../utils/measureText';
import { Themeable2 } from '../../types';
const MIN_VALUE_HEIGHT = 18;
@@ -114,7 +114,7 @@ export class BarGauge extends PureComponent<Props> {
return (
<div style={styles.wrapper}>
<FormattedValueDisplay
aria-label={selectors.components.Panels.Visualization.BarGauge.value}
data-testid={selectors.components.Panels.Visualization.BarGauge.valueV2}
value={value}
style={styles.value}
/>
@@ -195,7 +195,7 @@ export class BarGauge extends PureComponent<Props> {
<div style={containerStyles}>
{cells}
<FormattedValueDisplay
aria-label={selectors.components.Panels.Visualization.BarGauge.value}
data-testid={selectors.components.Panels.Visualization.BarGauge.valueV2}
value={value}
style={valueStyles}
/>

View File

@@ -23,7 +23,7 @@ exports[`BarGauge Render with basic options should render 1`] = `
}
>
<FormattedDisplayValue
aria-label="Bar gauge value"
data-testid="data-testid Bar gauge value"
style={
Object {
"alignItems": "center",

View File

@@ -1,6 +1,6 @@
import React, { FC, useState, useCallback } from 'react';
import React, { FC, useCallback, useState } from 'react';
import { css, cx } from '@emotion/css';
import { TimeZone, GrafanaTheme2, getTimeZoneInfo } from '@grafana/data';
import { getTimeZoneInfo, GrafanaTheme2, TimeZone } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../../themes';
import { TimeZoneTitle } from '../TimeZonePicker/TimeZoneTitle';
import { TimeZoneDescription } from '../TimeZonePicker/TimeZoneDescription';
@@ -84,7 +84,7 @@ export const TimePickerFooter: FC<Props> = (props) => {
</div>
{editMode === 'tz' ? (
<section
aria-label={selectors.components.TimeZonePicker.container}
data-testid={selectors.components.TimeZonePicker.containerV2}
className={cx(style.timeZoneContainer, style.timeSettingContainer)}
>
<TimeZonePicker
@@ -101,7 +101,7 @@ export const TimePickerFooter: FC<Props> = (props) => {
</section>
) : (
<section
aria-label={selectors.components.TimeZonePicker.container}
aria-label={selectors.components.TimeZonePicker.containerV2}
className={cx(style.timeZoneContainer, style.timeSettingContainer)}
>
<Field className={style.fiscalYearField} label={'Fiscal year start month'}>

View File

@@ -37,7 +37,7 @@ export const WeekStartPicker: React.FC<Props> = (props) => {
<Select
inputId={inputId}
value={weekStarts.find((item) => item.value === value)?.value}
placeholder="Choose starting day of the week"
placeholder={selectors.components.WeekStartPicker.placeholder}
autoFocus={autoFocus}
openMenuOnFocus={true}
width={width}
@@ -45,7 +45,6 @@ export const WeekStartPicker: React.FC<Props> = (props) => {
onChange={onChangeWeekStart}
onBlur={onBlur}
disabled={disabled}
aria-label={selectors.components.WeekStartPicker.container}
menuShouldPortal={true}
/>
);

View File

@@ -82,7 +82,7 @@ const EmptyListCTA: React.FunctionComponent<Props> = ({
href={buttonLink}
icon={buttonIcon}
className={ctaElementClassName}
aria-label={selectors.components.CallToActionCard.button(buttonTitle)}
data-testid={selectors.components.CallToActionCard.buttonV2(buttonTitle)}
disabled={buttonDisabled}
>
{buttonTitle}

View File

@@ -174,7 +174,7 @@ export class FolderPicker extends PureComponent<Props, State> {
const { enableCreateNew, inputId } = this.props;
return (
<div aria-label={selectors.components.FolderPicker.container}>
<div data-testid={selectors.components.FolderPicker.containerV2}>
<AsyncSelect
inputId={inputId}
aria-label={selectors.components.FolderPicker.input}

View File

@@ -2,7 +2,7 @@
exports[`FolderPicker should render 1`] = `
<div
aria-label="Folder picker select container"
data-testid="data-testid Folder picker select container"
>
<AsyncSelect
allowCustomValue={false}

View File

@@ -12,8 +12,8 @@ import {
Select,
stylesFactory,
TimeZonePicker,
WeekStartPicker,
Tooltip,
WeekStartPicker,
} from '@grafana/ui';
import { SelectableValue } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
@@ -150,7 +150,7 @@ export class SharedPreferences extends PureComponent<Props, State> {
</Tooltip>
</Label>
}
aria-label="User preferences home dashboard drop down"
data-testid="User preferences home dashboard drop down"
>
<Select
menuShouldPortal
@@ -166,12 +166,21 @@ export class SharedPreferences extends PureComponent<Props, State> {
/>
</Field>
<Field label="Timezone" aria-label={selectors.components.TimeZonePicker.container}>
<TimeZonePicker includeInternal={true} value={timezone} onChange={this.onTimeZoneChanged} />
<Field label="Timezone" data-testid={selectors.components.TimeZonePicker.containerV2}>
<TimeZonePicker
includeInternal={true}
value={timezone}
onChange={this.onTimeZoneChanged}
inputId={'shared-preferences-timezone-picker'}
/>
</Field>
<Field label="Week start" aria-label={selectors.components.WeekStartPicker.container}>
<WeekStartPicker value={weekStart} onChange={this.onWeekStartChanged} />
<Field label="Week start" data-testid={selectors.components.WeekStartPicker.containerV2}>
<WeekStartPicker
value={weekStart}
onChange={this.onWeekStartChanged}
inputId={'shared-preferences-week-start-picker'}
/>
</Field>
<div className="gf-form-button-row">
<Button variant="primary" aria-label="User preferences save button">

View File

@@ -10,7 +10,7 @@ import { getAlertRuleItems, getSearchQuery } from './state/selectors';
import { SelectableValue } from '@grafana/data';
import { config, locationService } from '@grafana/runtime';
import { setSearchQuery } from './state/reducers';
import { Button, LinkButton, Select, VerticalGroup, FilterInput } from '@grafana/ui';
import { Button, FilterInput, LinkButton, Select, VerticalGroup } from '@grafana/ui';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
import { ShowModalReactEvent } from '../../types/events';
import { AlertHowToModal } from './AlertHowToModal';
@@ -100,10 +100,13 @@ export class AlertRuleListUnconnected extends PureComponent<Props> {
<FilterInput placeholder="Search alerts" value={search} onChange={this.onSearchQueryChange} />
</div>
<div className="gf-form">
<label className="gf-form-label">States</label>
<label className="gf-form-label" htmlFor="alert-state-filter">
States
</label>
<div className="width-13">
<Select
inputId={'alert-state-filter'}
menuShouldPortal
options={this.stateFilters}
onChange={this.onStateFilterChanged}

View File

@@ -61,7 +61,7 @@ describe('ApiKeysPage', () => {
describe('when there are no API keys', () => {
it('then it should render CTA', () => {
setup({ apiKeys: getMultipleMockKeys(0), apiKeysCount: 0, hasFetched: true });
expect(screen.getByLabelText(selectors.components.CallToActionCard.button('New API key'))).toBeInTheDocument();
expect(screen.getByTestId(selectors.components.CallToActionCard.buttonV2('New API key'))).toBeInTheDocument();
});
});
@@ -150,7 +150,7 @@ describe('ApiKeysPage', () => {
const { addApiKeyMock } = setup({ apiKeys, apiKeysCount: apiKeys.length, hasFetched: true });
addApiKeyMock.mockClear();
userEvent.click(screen.getByLabelText(selectors.components.CallToActionCard.button('New API key')));
userEvent.click(screen.getByTestId(selectors.components.CallToActionCard.buttonV2('New API key')));
await addAndVerifyApiKey(addApiKeyMock, false);
});
});

View File

@@ -116,7 +116,7 @@ describe('AnnotationsSettings', () => {
expect(screen.queryByRole('table')).toBeInTheDocument();
expect(screen.getByRole('row', { name: /annotations & alerts \(built\-in\) grafana/i })).toBeInTheDocument();
expect(
screen.queryByLabelText(selectors.components.CallToActionCard.button('Add annotation query'))
screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query'))
).toBeInTheDocument();
expect(screen.queryByRole('link', { name: /annotations documentation/i })).toBeInTheDocument();
@@ -140,7 +140,7 @@ describe('AnnotationsSettings', () => {
expect(screen.getByRole('table')).toBeInTheDocument();
expect(screen.getByRole('row', { name: /my annotation \(built\-in\) grafana/i })).toBeInTheDocument();
expect(
screen.queryByLabelText(selectors.components.CallToActionCard.button('Add annotation query'))
screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query'))
).toBeInTheDocument();
expect(screen.queryByRole('button', { name: /new query/i })).not.toBeInTheDocument();
@@ -149,7 +149,7 @@ describe('AnnotationsSettings', () => {
expect(screen.queryAllByRole('row').length).toBe(0);
expect(
screen.queryByLabelText(selectors.components.CallToActionCard.button('Add annotation query'))
screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query'))
).toBeInTheDocument();
});
@@ -210,7 +210,7 @@ describe('AnnotationsSettings', () => {
test('it renders a form for adding/editing annotations', () => {
render(<AnnotationsSettings dashboard={dashboard} />);
userEvent.click(screen.getByLabelText(selectors.components.CallToActionCard.button('Add annotation query')));
userEvent.click(screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query')));
const heading = screen.getByRole('heading', {
name: /annotations edit/i,
@@ -237,7 +237,7 @@ describe('AnnotationsSettings', () => {
expect(screen.queryByRole('row', { name: /my prometheus annotation prometheus/i })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: /new query/i })).toBeInTheDocument();
expect(
screen.queryByLabelText(selectors.components.CallToActionCard.button('Add annotation query'))
screen.queryByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query'))
).not.toBeInTheDocument();
userEvent.click(screen.getByRole('button', { name: /new query/i }));

View File

@@ -6,6 +6,7 @@ import { selectOptionInTest } from '@grafana/ui';
import { byRole } from 'testing-library-selector';
import { GeneralSettingsUnconnected as GeneralSettings, Props } from './GeneralSettings';
import { DashboardModel } from '../../state';
import { selectors } from '@grafana/e2e-selectors';
const setupTestContext = (options: Partial<Props>) => {
const defaults: Props = {
@@ -38,7 +39,7 @@ describe('General Settings', () => {
setupTestContext({});
screen.getByDisplayValue('test dashboard title');
screen.getByDisplayValue('test dashboard description');
expect(screen.getByLabelText('Time zone picker select container')).toHaveTextContent(
expect(screen.getByTestId(selectors.components.TimeZonePicker.containerV2)).toHaveTextContent(
'Coordinated Universal Time'
);
});
@@ -47,8 +48,8 @@ describe('General Settings', () => {
describe('when timezone is changed', () => {
it('should call update function', async () => {
const { props } = setupTestContext({});
userEvent.click(screen.getByLabelText('Time zone picker select container'));
const timeZonePicker = screen.getByLabelText('Time zone picker select container');
userEvent.click(screen.getByTestId(selectors.components.TimeZonePicker.containerV2));
const timeZonePicker = screen.getByTestId(selectors.components.TimeZonePicker.containerV2);
userEvent.click(byRole('textbox').get(timeZonePicker));
await selectOptionInTest(timeZonePicker, 'Browser Time');
expect(props.updateTimeZone).toHaveBeenCalledWith('browser');

View File

@@ -69,7 +69,7 @@ describe('LinksSettings', () => {
expect(screen.getByRole('heading', { name: 'Dashboard links' })).toBeInTheDocument();
expect(
screen.getByLabelText(selectors.components.CallToActionCard.button('Add dashboard link'))
screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add dashboard link'))
).toBeInTheDocument();
expect(screen.queryByRole('table')).not.toBeInTheDocument();
});
@@ -80,7 +80,7 @@ describe('LinksSettings', () => {
expect(getTableBodyRows().length).toBe(links.length);
expect(
screen.queryByLabelText(selectors.components.CallToActionCard.button('Add dashboard link'))
screen.queryByTestId(selectors.components.CallToActionCard.buttonV2('Add dashboard link'))
).not.toBeInTheDocument();
});

View File

@@ -1,5 +1,5 @@
import React, { PureComponent } from 'react';
import { Input, TimeZonePicker, Field, Switch, CollapsableSection, WeekStartPicker } from '@grafana/ui';
import { CollapsableSection, Field, Input, Switch, TimeZonePicker, WeekStartPicker } from '@grafana/ui';
import { rangeUtil, TimeZone } from '@grafana/data';
import { isEmpty } from 'lodash';
import { selectors } from '@grafana/e2e-selectors';
@@ -65,7 +65,7 @@ export class TimePickerSettings extends PureComponent<Props, State> {
render() {
return (
<CollapsableSection label="Time options" isOpen={true}>
<Field label="Timezone" aria-label={selectors.components.TimeZonePicker.container}>
<Field label="Timezone" data-testid={selectors.components.TimeZonePicker.containerV2}>
<TimeZonePicker
inputId="time-options-input"
includeInternal={true}
@@ -74,7 +74,7 @@ export class TimePickerSettings extends PureComponent<Props, State> {
width={40}
/>
</Field>
<Field label="Week start" aria-label={selectors.components.WeekStartPicker.container}>
<Field label="Week start" data-testid={selectors.components.WeekStartPicker.containerV2}>
<WeekStartPicker
inputId="week-start-input"
width={40}

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { Provider } from 'react-redux';
import { render, screen } from '@testing-library/react';
import { UnthemedDashboardPage, Props } from './DashboardPage';
import { Props, UnthemedDashboardPage } from './DashboardPage';
import { Router } from 'react-router-dom';
import { locationService } from '@grafana/runtime';
import { DashboardModel } from '../state';
@@ -266,7 +266,7 @@ describe('DashboardPage', () => {
});
it('should render dashboard page toolbar and submenu', () => {
expect(screen.queryAllByLabelText(selectors.pages.Dashboard.DashNav.nav)).toHaveLength(1);
expect(screen.queryAllByTestId(selectors.pages.Dashboard.DashNav.navV2)).toHaveLength(1);
expect(screen.queryAllByLabelText(selectors.pages.Dashboard.SubMenu.submenu)).toHaveLength(1);
});
});
@@ -281,7 +281,7 @@ describe('DashboardPage', () => {
});
it('should not render page toolbar and submenu', () => {
expect(screen.queryAllByLabelText(selectors.pages.Dashboard.DashNav.nav)).toHaveLength(0);
expect(screen.queryAllByTestId(selectors.pages.Dashboard.DashNav.navV2)).toHaveLength(0);
expect(screen.queryAllByLabelText(selectors.pages.Dashboard.SubMenu.submenu)).toHaveLength(0);
});
});

View File

@@ -343,7 +343,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
return (
<div className={containerClassNames}>
{kioskMode !== KioskMode.Full && (
<header aria-label={selectors.pages.Dashboard.DashNav.nav}>
<header data-testid={selectors.pages.Dashboard.DashNav.navV2}>
<DashNav
dashboard={dashboard}
title={dashboard.title}

View File

@@ -1,10 +1,12 @@
import React from 'react';
import { configureStore } from '../../store/configureStore';
import { ResponseErrorContainer } from './ResponseErrorContainer';
import { Provider } from 'react-redux';
import { render, screen } from '@testing-library/react';
import { ExploreId } from '../../types';
import { DataQueryError, LoadingState } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { configureStore } from '../../store/configureStore';
import { ResponseErrorContainer } from './ResponseErrorContainer';
import { ExploreId } from '../../types';
describe('ResponseErrorContainer', () => {
it('shows error message if it does not contain refId', async () => {
@@ -12,7 +14,7 @@ describe('ResponseErrorContainer', () => {
setup({
message: errorMessage,
});
const errorEl = screen.getByLabelText('Alert error');
const errorEl = screen.getByTestId(selectors.components.Alert.alertV2('error'));
expect(errorEl).toBeInTheDocument();
expect(errorEl).toHaveTextContent(errorMessage);
});
@@ -23,7 +25,7 @@ describe('ResponseErrorContainer', () => {
refId: 'someId',
message: errorMessage,
});
const errorEl = screen.getByLabelText('Alert error');
const errorEl = screen.getByTestId(selectors.components.Alert.alertV2('error'));
expect(errorEl).toBeInTheDocument();
expect(errorEl).toHaveTextContent(errorMessage);
});
@@ -35,7 +37,7 @@ describe('ResponseErrorContainer', () => {
message: 'test error',
},
});
const errorEl = screen.getByLabelText('Alert error');
const errorEl = screen.getByTestId(selectors.components.Alert.alertV2('error'));
expect(errorEl).toBeInTheDocument();
expect(errorEl).toHaveTextContent(errorMessage);
});

View File

@@ -87,8 +87,8 @@ const defaultProps: Props = {
};
function getSelectors() {
const dashboardSelect = () => screen.getByLabelText(/user preferences home dashboard drop down/i);
const timepickerSelect = () => screen.getByLabelText(selectors.components.TimeZonePicker.container);
const dashboardSelect = () => screen.getByTestId('User preferences home dashboard drop down');
const timepickerSelect = () => screen.getByTestId(selectors.components.TimeZonePicker.containerV2);
const teamsTable = () => screen.getByRole('table', { name: /user teams table/i });
const orgsTable = () => screen.getByRole('table', { name: /user organizations table/i });
const sessionsTable = () => screen.getByRole('table', { name: /user sessions table/i });

View File

@@ -1,12 +1,14 @@
import React from 'react';
import { render, fireEvent, screen, waitFor, act } from '@testing-library/react';
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
import { locationService } from '@grafana/runtime';
import { selectOptionInTest } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import * as SearchSrv from 'app/core/services/search_srv';
import * as MockSearchSrv from 'app/core/services/__mocks__/search_srv';
import { DashboardSearch, Props } from './DashboardSearch';
import { searchResults } from '../testData';
import { SearchLayout } from '../types';
import { locationService } from '@grafana/runtime';
jest.mock('app/core/services/search_srv');
// Typecast the mock search so the mock import is correctly recognised by TS
@@ -93,9 +95,9 @@ describe('DashboardSearch', () => {
locationService.push('/');
setup();
const section = await screen.findAllByLabelText('Search section');
const section = await screen.findAllByTestId(selectors.components.Search.sectionV2);
expect(section).toHaveLength(2);
expect(screen.getAllByLabelText('Search items')).toHaveLength(1);
expect(screen.getAllByTestId(selectors.components.Search.itemsV2)).toHaveLength(1);
});
it('should call search with selected tags', async () => {

View File

@@ -68,7 +68,7 @@ export const SearchField: FC<SearchFieldProps> = ({ query, onChange, size, clear
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
onChange(event.currentTarget.value);
}}
tabIndex={1}
tabIndex={0}
spellCheck={false}
className={styles.input}
{...inputProps}

View File

@@ -1,6 +1,8 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { SearchItem, Props } from './SearchItem';
import { fireEvent, render, screen } from '@testing-library/react';
import { selectors } from '@grafana/e2e-selectors';
import { Props, SearchItem } from './SearchItem';
import { DashboardSearchItemType } from '../types';
beforeEach(() => {
@@ -35,7 +37,7 @@ const setup = (propOverrides?: Partial<Props>) => {
describe('SearchItem', () => {
it('should render the item', () => {
setup();
expect(screen.getAllByLabelText('Dashboard search item Test 1')).toHaveLength(1);
expect(screen.getAllByTestId(selectors.components.Search.dashboardItem('Test 1'))).toHaveLength(1);
expect(screen.getAllByText('Test 1')).toHaveLength(1);
});

View File

@@ -1,7 +1,7 @@
import React, { FC, useCallback } from 'react';
import { css } from '@emotion/css';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
import { TagList, Card, Icon, IconName, useStyles2 } from '@grafana/ui';
import { Card, Icon, IconName, TagList, useStyles2 } from '@grafana/ui';
import { GrafanaTheme2 } from '@grafana/data';
import { DashboardSectionItem, OnToggleChecked } from '../types';
import { SearchCheckbox } from './SearchCheckbox';
@@ -14,7 +14,7 @@ export interface Props {
onToggleChecked?: OnToggleChecked;
}
const selectors = e2eSelectors.pages.Dashboards;
const selectors = e2eSelectors.components.Search;
const getIconFromMeta = (meta = ''): IconName => {
const metaIconMap = new Map<string, IconName>([
@@ -49,7 +49,7 @@ export const SearchItem: FC<Props> = ({ item, editable, onToggleChecked, onTagSe
const folderTitle = item.folderTitle || 'General';
return (
<Card
aria-label={selectors.dashboards(item.title)}
data-testid={selectors.dashboardItem(item.title)}
heading={item.title}
href={item.url}
style={{ minHeight: SEARCH_ITEM_HEIGHT }}

View File

@@ -1,7 +1,9 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { SearchResults, Props } from './SearchResults';
import { searchResults, generalFolder } from '../testData';
import { fireEvent, render, screen } from '@testing-library/react';
import { selectors } from '@grafana/e2e-selectors';
import { Props, SearchResults } from './SearchResults';
import { generalFolder, searchResults } from '../testData';
import { SearchLayout } from '../types';
beforeEach(() => {
@@ -25,14 +27,15 @@ const setup = (propOverrides?: Partial<Props>) => {
describe('SearchResults', () => {
it('should render result items', () => {
setup();
expect(screen.getAllByLabelText('Search section')).toHaveLength(2);
expect(screen.getAllByTestId(selectors.components.Search.sectionV2)).toHaveLength(2);
});
it('should render section items for expanded section', () => {
setup();
expect(screen.getAllByLabelText(/collapse folder/i)).toHaveLength(1);
expect(screen.getAllByLabelText('Search items')).toHaveLength(1);
expect(screen.getAllByLabelText(/dashboard search item/i)).toHaveLength(2);
expect(screen.getAllByTestId(selectors.components.Search.collapseFolder('0'))).toHaveLength(1);
expect(screen.getAllByTestId(selectors.components.Search.itemsV2)).toHaveLength(1);
expect(screen.getAllByTestId(selectors.components.Search.dashboardItem('Test 1'))).toHaveLength(1);
expect(screen.getAllByTestId(selectors.components.Search.dashboardItem('Test 2'))).toHaveLength(1);
});
it('should not render checkboxes for non-editable results', () => {
@@ -49,7 +52,7 @@ describe('SearchResults', () => {
const mockOnToggleSection = jest.fn();
setup({ onToggleSection: mockOnToggleSection });
fireEvent.click(screen.getByLabelText('Collapse folder 0'));
fireEvent.click(screen.getByTestId(selectors.components.Search.collapseFolder('0')));
expect(mockOnToggleSection).toHaveBeenCalledTimes(1);
expect(mockOnToggleSection).toHaveBeenCalledWith(generalFolder);
});

View File

@@ -3,7 +3,7 @@ import { css } from '@emotion/css';
import { FixedSizeList } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer';
import { GrafanaTheme } from '@grafana/data';
import { stylesFactory, useTheme, Spinner } from '@grafana/ui';
import { Spinner, stylesFactory, useTheme } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import { DashboardSection, OnToggleChecked, SearchLayout } from '../types';
import { SEARCH_ITEM_HEIGHT, SEARCH_ITEM_MARGIN } from '../constants';
@@ -20,7 +20,7 @@ export interface Props {
layout?: string;
}
const { section: sectionLabel, items: itemsLabel } = selectors.components.Search;
const { sectionV2: sectionLabel, itemsV2: itemsLabel } = selectors.components.Search;
export const SearchResults: FC<Props> = memo(
({ editable, loading, onTagSelected, onToggleChecked, onToggleSection, results, layout }) => {
@@ -32,12 +32,12 @@ export const SearchResults: FC<Props> = memo(
<div className={styles.wrapper}>
{results.map((section) => {
return (
<div aria-label={sectionLabel} className={styles.section} key={section.id || section.title}>
<div data-testid={sectionLabel} className={styles.section} key={section.id || section.title}>
{section.title && (
<SectionHeader onSectionClick={onToggleSection} {...{ onToggleChecked, editable, section }} />
)}
{section.expanded && (
<div aria-label={itemsLabel} className={styles.sectionItems}>
<div data-testid={itemsLabel} className={styles.sectionItems}>
{section.items.map((item) => (
<SearchItem key={item.id} {...itemProps} item={item} />
))}
@@ -56,7 +56,6 @@ export const SearchResults: FC<Props> = memo(
<AutoSizer disableWidth>
{({ height }) => (
<FixedSizeList
aria-label="Search items"
className={styles.wrapper}
innerElementType="ul"
itemSize={SEARCH_ITEM_HEIGHT + SEARCH_ITEM_MARGIN}

View File

@@ -2,7 +2,9 @@ import React, { FC, useCallback } from 'react';
import { css, cx } from '@emotion/css';
import { useLocalStorage } from 'react-use';
import { GrafanaTheme } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { Icon, Spinner, stylesFactory, useTheme } from '@grafana/ui';
import { DashboardSection, OnToggleChecked } from '../types';
import { SearchCheckbox } from './SearchCheckbox';
import { getSectionIcon, getSectionStorageKey } from '../utils';
@@ -46,7 +48,11 @@ export const SectionHeader: FC<SectionHeaderProps> = ({
<div
className={styles.wrapper}
onClick={onSectionExpand}
aria-label={section.expanded ? `Collapse folder ${section.id}` : `Expand folder ${section.id}`}
data-testid={
section.expanded
? selectors.components.Search.collapseFolder(section.id?.toString())
: selectors.components.Search.expandFolder(section.id?.toString())
}
>
<SearchCheckbox
className={styles.checkbox}

View File

@@ -1,15 +1,15 @@
import React from 'react';
import { mount, ReactWrapper } from 'enzyme';
import {
PanelData,
dateMath,
TimeRange,
VizOrientation,
PanelProps,
LoadingState,
dateTime,
FieldConfigSource,
LoadingState,
PanelData,
PanelProps,
TimeRange,
toDataFrame,
VizOrientation,
} from '@grafana/data';
import { BarGaugeDisplayMode } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
@@ -17,7 +17,7 @@ import { selectors } from '@grafana/e2e-selectors';
import { BarGaugePanel } from './BarGaugePanel';
import { BarGaugeOptions } from './types';
const valueSelector = selectors.components.Panels.Visualization.BarGauge.value;
const valueSelector = selectors.components.Panels.Visualization.BarGauge.valueV2;
describe('BarGaugePanel', () => {
describe('when empty result is rendered', () => {
@@ -28,7 +28,7 @@ describe('BarGaugePanel', () => {
});
it('should render with title "No data"', () => {
const displayValue = wrapper.find(`div[aria-label="${valueSelector}"]`).text();
const displayValue = wrapper.find(`div[data-testid="${valueSelector}"]`).text();
expect(displayValue).toBe('No data');
});
});
@@ -49,7 +49,7 @@ describe('BarGaugePanel', () => {
});
it('should render with title "No data"', () => {
const displayValue = wrapper.find(`div[aria-label="${valueSelector}"]`).text();
const displayValue = wrapper.find(`div[data-testid="${valueSelector}"]`).text();
expect(displayValue).toBe('100');
});
});