Dashboard a11y tests (#58122) (#59131)

* adding comprehensive dashboard tests

* fixing delete and adding dima changes

* Fixing some of the a11y test failures

* Fixing i18n issue

* Extracting exit fullscreen logic in a separate function

* Fixing typo

* Upgrading axe

* Fixing failing jest tests

* Removing main tag as it was causing a test to fail

* Adding focusable=false to a range control as well

* Update test/accessibility/apps/dashboard.ts

Co-Authored-By: Michail Yasonik <michail.yasonik@elastic.co>

* Fixing linting error

* Update src/legacy/core_plugins/input_control_vis/public/components/vis/list_control.tsx

Co-Authored-By: Michail Yasonik <michail.yasonik@elastic.co>

* Add comments

Co-authored-by: Bhavya RM <bhavya@elastic.co>
Co-authored-by: Michail Yasonik <michail@yasonik.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

Co-authored-by: Bhavya RM <bhavya@elastic.co>
Co-authored-by: Michail Yasonik <michail@yasonik.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Maja Grubic 2020-03-03 17:14:48 +00:00 committed by GitHub
parent 0fdb6e520a
commit 59a12ab69e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 171 additions and 14 deletions

View file

@ -388,7 +388,7 @@
"@typescript-eslint/parser": "^2.15.0",
"angular-mocks": "^1.7.9",
"archiver": "^3.1.1",
"axe-core": "^3.3.2",
"axe-core": "^3.4.1",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0",
"babel-plugin-dynamic-import-node": "^2.2.0",

View file

@ -25,6 +25,7 @@ exports[`renders ListControl 1`] = `
compressed={false}
data-test-subj="listControlSelect0"
fullWidth={false}
inputRef={[Function]}
isClearable={true}
isLoading={false}
onChange={[Function]}

View file

@ -58,8 +58,17 @@ class ListControlUi extends PureComponent<ListControlUiProps, ListControlUiState
state = {
isLoading: false,
};
private textInput: HTMLElement | null;
constructor(props: ListControlUiProps) {
super(props);
this.textInput = null;
}
componentDidMount = () => {
if (this.textInput) {
this.textInput.setAttribute('focusable', 'false'); // remove when #59039 is fixed
}
this.isMounted = true;
};
@ -67,6 +76,10 @@ class ListControlUi extends PureComponent<ListControlUiProps, ListControlUiState
this.isMounted = false;
};
setTextInputRef = (ref: HTMLElement) => {
this.textInput = ref;
};
handleOnChange = (selectedOptions: any[]) => {
const selectedValues = selectedOptions.map(({ value }) => {
return value;
@ -143,6 +156,7 @@ class ListControlUi extends PureComponent<ListControlUiProps, ListControlUiState
onChange={this.handleOnChange}
singleSelection={!this.props.multiselect}
data-test-subj={`listControlSelect${this.props.controlIndex}`}
inputRef={this.setTextInputRef}
/>
);
}

View file

@ -28,6 +28,7 @@ exports[`renders DashboardCloneModal 1`] = `
</EuiText>
<EuiSpacer />
<EuiFieldText
aria-label="Cloned Dashboard Title"
autoFocus={true}
data-test-subj="clonedDashboardTitle"
isInvalid={false}

View file

@ -178,6 +178,9 @@ export class DashboardCloneModal extends React.Component<Props, State> {
<EuiFieldText
autoFocus
aria-label={i18n.translate('kbn.dashboard.cloneModal.cloneDashboardTitleAriaLabel', {
defaultMessage: 'Cloned Dashboard Title',
})}
data-test-subj="clonedDashboardTitle"
value={this.state.newDashboardName}
onChange={this.onInputChange}

View file

@ -5,6 +5,7 @@ exports[`it renders ToolBarPagerButtons 1`] = `
className="kuiButtonGroup"
>
<button
aria-label="Previous page in table"
className="kuiButton kuiButton--basic kuiButton--icon"
data-test-subj="btnPrevPage"
disabled={false}
@ -15,6 +16,7 @@ exports[`it renders ToolBarPagerButtons 1`] = `
/>
</button>
<button
aria-label="Next page in table"
className="kuiButton kuiButton--basic kuiButton--icon"
data-test-subj="btnNextPage"
disabled={false}

View file

@ -17,6 +17,7 @@
* under the License.
*/
import React from 'react';
import { i18n } from '@kbn/i18n';
interface Props {
hasPreviousPage: boolean;
@ -33,6 +34,12 @@ export function ToolBarPagerButtons(props: Props) {
onClick={() => props.onPagePrevious()}
disabled={!props.hasPreviousPage}
data-test-subj="btnPrevPage"
aria-label={i18n.translate(
'kbn.discover.docTable.pager.toolbarPagerButtons.previousButtonAriaLabel',
{
defaultMessage: 'Previous page in table',
}
)}
>
<span className="kuiButton__icon kuiIcon fa-chevron-left" />
</button>
@ -41,6 +48,12 @@ export function ToolBarPagerButtons(props: Props) {
onClick={() => props.onPageNext()}
disabled={!props.hasNextPage}
data-test-subj="btnNextPage"
aria-label={i18n.translate(
'kbn.ddiscover.docTable.pager.toolbarPagerButtons.nextButtonAriaLabel',
{
defaultMessage: 'Next page in table',
}
)}
>
<span className="kuiButton__icon kuiIcon fa-chevron-right" />
</button>

View file

@ -92,6 +92,7 @@ export class ValidatedDualRange extends Component {
fullWidth={fullWidth}
value={this.state.value}
onChange={this._onChange}
focusable={false} // remove when #59039 is fixed
{...rest}
/>
</EuiFormRow>

View file

@ -68,7 +68,7 @@ export function QueryLanguageSwitcher(props: Props) {
return (
<EuiPopover
id="popover"
id="queryLanguageSwitcherPopover"
anchorClassName="euiFormControlLayout__append"
ownFocus
anchorPosition={props.anchorPosition || 'downRight'}

View file

@ -20,27 +20,36 @@
import { FtrProviderContext } from '../ftr_provider_context';
export default function({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'dashboard', 'header']);
const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'home', 'settings']);
const a11y = getService('a11y');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const dashboardAddPanel = getService('dashboardAddPanel');
const testSubjects = getService('testSubjects');
const listingTable = getService('listingTable');
describe('Dashboard', () => {
const dashboardName = 'Dashboard Listing A11y';
const clonedDashboardName = 'Dashboard Listing A11y Copy';
before(async () => {
await esArchiver.loadIfNeeded('logstash_functional');
await kibanaServer.uiSettings.update({
defaultIndex: 'logstash-*',
});
await PageObjects.common.navigateToUrl('home', 'tutorial_directory/sampleData');
await PageObjects.home.addSampleDataSet('flights');
});
after(async () => {
await PageObjects.common.navigateToApp('dashboard');
await listingTable.searchForItemWithName(dashboardName);
await listingTable.checkListingSelectAllCheckbox();
await listingTable.clickDeleteSelected();
await PageObjects.common.clickConfirmOnModal();
});
it('dashboard', async () => {
await PageObjects.common.navigateToApp('dashboard');
await a11y.testAppSnapshot();
});
it('create dashboard button', async () => {
await PageObjects.dashboard.clickCreateDashboardPrompt();
await PageObjects.dashboard.clickNewDashboard();
await a11y.testAppSnapshot();
});
@ -49,9 +58,115 @@ export default function({ getService, getPageObjects }: FtrProviderContext) {
await a11y.testAppSnapshot();
});
it('Open Edit mode', async () => {
await PageObjects.dashboard.switchToEditMode();
await a11y.testAppSnapshot();
});
it('Open add panel', async () => {
await dashboardAddPanel.clickOpenAddPanel();
await a11y.testAppSnapshot();
});
it('add a visualization', async () => {
await testSubjects.click('savedObjectTitle[Flights]-Delay-Buckets');
await a11y.testAppSnapshot();
});
it('add a saved search', async () => {
await dashboardAddPanel.addSavedSearch('[Flights] Flight Log');
await a11y.testAppSnapshot();
});
it('save the dashboard', async () => {
await PageObjects.dashboard.saveDashboard(dashboardName);
await a11y.testAppSnapshot();
});
it('Open Edit mode', async () => {
await PageObjects.dashboard.switchToEditMode();
await a11y.testAppSnapshot();
});
it('open options menu', async () => {
await PageObjects.dashboard.openOptions();
await a11y.testAppSnapshot();
});
it('Should be able to hide panel titles', async () => {
await testSubjects.click('dashboardPanelTitlesCheckbox');
await a11y.testAppSnapshot();
});
it('Should be able display panels without margins', async () => {
await testSubjects.click('dashboardMarginsCheckbox');
await a11y.testAppSnapshot();
});
it('Open add panel', async () => {
await dashboardAddPanel.clickOpenAddPanel();
await a11y.testAppSnapshot();
});
it('Add one more saved object to cancel it', async () => {
await testSubjects.click('savedObjectTitle[Flights]-Average-Ticket-Price');
await a11y.testAppSnapshot();
});
it('Close add panel', async () => {
await dashboardAddPanel.closeAddPanel();
await a11y.testAppSnapshot();
});
it('Exit out of edit mode', async () => {
await PageObjects.dashboard.clickCancelOutOfEditMode();
await a11y.testAppSnapshot();
});
it('Discard changes', async () => {
await PageObjects.common.clickConfirmOnModal();
await PageObjects.dashboard.getIsInViewMode();
await a11y.testAppSnapshot();
});
it('Test full screen', async () => {
await PageObjects.dashboard.clickFullScreenMode();
await a11y.testAppSnapshot();
});
it('Exit out of full screen mode', async () => {
await PageObjects.dashboard.exitFullScreenMode();
await a11y.testAppSnapshot();
});
it('Make a clone of the dashboard', async () => {
await PageObjects.dashboard.clickClone();
await a11y.testAppSnapshot();
});
it('Confirm clone with *copy* appended', async () => {
await PageObjects.dashboard.confirmClone();
await a11y.testAppSnapshot();
});
it('Dashboard listing table', async () => {
await PageObjects.dashboard.gotoDashboardLandingPage();
await a11y.testAppSnapshot();
});
it('Delete a11y clone dashboard', async () => {
await listingTable.searchForItemWithName(clonedDashboardName);
await listingTable.checkListingSelectAllCheckbox();
await listingTable.clickDeleteSelected();
await a11y.testAppSnapshot();
await PageObjects.common.clickConfirmOnModal();
await listingTable.searchForItemWithName('');
});
// Blocked by https://github.com/elastic/kibana/issues/38980
it.skip('Open flight dashboard', async () => {
await testSubjects.click('dashboardListingTitleLink-[Flights]-Global-Flight-Dashboard');
await a11y.testAppSnapshot();
});
});
}

View file

@ -65,6 +65,13 @@ export function DashboardPageProvider({ getService, getPageObjects }: FtrProvide
await this.waitForRenderComplete();
}
public async exitFullScreenMode() {
log.debug(`exitFullScreenMode`);
const logoButton = await this.getExitFullScreenLogoButton();
await logoButton.moveMouseTo();
await this.clickExitFullScreenTextButton();
}
public async fullScreenModeMenuItemExists() {
return await testSubjects.exists('dashboardFullScreenMode');
}

View file

@ -7948,10 +7948,10 @@ aws4@^1.6.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
integrity sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=
axe-core@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.3.2.tgz#7baf3c55a5cf1621534a2c38735f5a1bf2f7e1a8"
integrity sha512-lRdxsRt7yNhqpcXQk1ao1BL73OZDzmFCWOG0mC4tGR/r14ohH2payjHwCMQjHGbBKm924eDlmG7utAGHiX/A6g==
axe-core@^3.4.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.1.tgz#d8d5aaef73f003e8b766ea28bb078343f3622201"
integrity sha512-mwpDgPwWB+5kMHyLjlxh4w25ClJfqSxi+c6LQ4ix349TdCUctMwJNPTkhPD1qP9SYIjFgjeVpVZWCvK9oBGwCg==
axios@^0.18.0, axios@^0.18.1:
version "0.18.1"