Skip to content

Commit

Permalink
Splitting Cypress tests for component function and axe (a11y) checks (e…
Browse files Browse the repository at this point in the history
…lastic#6331)

* First commit for splitting Cypress tests.

* Renaming config variables and a11y test type.

* Refactoring to simpler commands, one config file.

* Factoring out a11y tests for two more components.

* Adding a comment to specPattern for future selves.

* Updating Cypress wiki doc for new functionality.
  • Loading branch information
1Copenut authored Nov 2, 2022
1 parent 60e59d5 commit 5e32012
Show file tree
Hide file tree
Showing 12 changed files with 238 additions and 63 deletions.
2 changes: 1 addition & 1 deletion cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default defineConfig({

return config;
},
specPattern: './src/**/*.spec.tsx',
specPattern: ['./src/**/*.spec.tsx', './src/**/*.a11y.tsx'], // scripts/cypress.js splits this using the CLI --spec argument
video: false,
},
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"test-staged": "yarn lint && node scripts/test-staged.js",
"test-cypress": "node ./scripts/cypress",
"test-cypress-dev": "node ./scripts/cypress --dev",
"test-cypress-a11y": "node ./scripts/cypress --a11y",
"combine-test-coverage": "sh ./scripts/combine-coverage.sh",
"start-test-server": "BABEL_MODULES=false NODE_ENV=puppeteer NODE_OPTIONS=--max-old-space-size=4096 webpack-dev-server --config src-docs/webpack.config.js --port 9999",
"yo-component": "yo ./generator-eui/app/component.js",
Expand Down
12 changes: 11 additions & 1 deletion scripts/cypress.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ const argv = yargs(hideBin(process.argv))
'skip-css': { type: 'boolean' },
dev: { type: 'boolean' },
theme: { type: 'string', default: 'light', choices: ['light', 'dark'] },
a11y: { type: 'boolean' },
}).argv;

const isDev = argv.hasOwnProperty('dev');
const isA11y = argv.hasOwnProperty('a11y');
const skipScss = argv.hasOwnProperty('skip-css');
const theme = argv.theme;

Expand All @@ -39,12 +41,20 @@ if (!skipScss) {
console.log(info('Not compiling SCSS, disabled by --skip-css'));
}

// compile dev and a11y options for how to run tests (headless, local UI)
// and whether to run component tests or axe checks.
const testParams = isDev
? 'open --component'
: `run --component --browser chrome ${
isA11y ? '--spec="./src/**/*.a11y.tsx"' : '--spec="./src/**/*.spec.tsx"'
}`;

const cypressCommandParts = [
'cross-env', // windows support
`THEME=${theme}`, // pass the theme
'BABEL_MODULES=false', // let webpack receive ES Module code
'NODE_ENV=cypress_test', // enable code coverage checks
`cypress ${isDev ? 'open --component' : 'run --component --browser chrome'}`,
`cypress ${testParams}`,
...argv._, // pass any extra options given to this script
];
const cypressCommand = cypressCommandParts.join(' ');
Expand Down
40 changes: 40 additions & 0 deletions src/components/accordion/accordion.a11y.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

/// <reference types="../../../cypress/support"/>

import React from 'react';
import { EuiAccordion, EuiAccordionProps } from './index';
import { EuiPanel } from '../../components/panel';
import { htmlIdGenerator } from '../../services';

const baseProps: EuiAccordionProps = {
buttonContent: 'Click me to toggle',
id: htmlIdGenerator()(),
initialIsOpen: false,
};

const noArrow = { arrowDisplay: 'none' };
const noArrowProps: EuiAccordionProps = Object.assign(baseProps, noArrow);

describe('EuiAccordion', () => {
describe('Automated accessibility check', () => {
it('has zero violations when expanded', () => {
cy.mount(
<EuiAccordion {...noArrowProps}>
<EuiPanel color="subdued">
Any content inside of <strong>EuiAccordion</strong> will appear
here. We will include <a href="#">a link</a> to confirm focus.
</EuiPanel>
</EuiAccordion>
);
cy.get('button.euiAccordion__button').click();
cy.checkAxe();
});
});
});
15 changes: 0 additions & 15 deletions src/components/accordion/accordion.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,19 +133,4 @@ describe('EuiAccordion', () => {
cy.focused().contains('a link');
});
});

describe('Automated accessibility check', () => {
it('has zero violations when expanded', () => {
cy.mount(
<EuiAccordion {...noArrowProps}>
<EuiPanel color="subdued">
Any content inside of <strong>EuiAccordion</strong> will appear
here. We will include <a href="#">a link</a> to confirm focus.
</EuiPanel>
</EuiAccordion>
);
cy.get('button.euiAccordion__button').click();
cy.checkAxe();
});
});
});
41 changes: 41 additions & 0 deletions src/components/context_menu/context_menu_panel.a11y.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

/// <reference types="../../../cypress/support"/>

import React from 'react';

import { EuiContextMenuItem } from './context_menu_item';
import { EuiContextMenuPanel } from './context_menu_panel';

const items = [
<EuiContextMenuItem key="A" data-test-subj="itemA">
Option A
</EuiContextMenuItem>,
<EuiContextMenuItem key="B" data-test-subj="itemB">
Option B
</EuiContextMenuItem>,
<EuiContextMenuItem key="C" data-test-subj="itemC">
Option C
</EuiContextMenuItem>,
];

describe('EuiContextMenuPanel', () => {
describe('Automated accessibility check', () => {
it('has zero violations', () => {
const showNextPanelHandler = cy.stub();
cy.mount(
<EuiContextMenuPanel
items={items}
showNextPanel={showNextPanelHandler}
/>
);
cy.checkAxe();
});
});
});
13 changes: 0 additions & 13 deletions src/components/context_menu/context_menu_panel.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -389,17 +389,4 @@ describe('EuiContextMenuPanel', () => {
});
});
});

describe('Automated accessibility check', () => {
it('has zero violations', () => {
const showNextPanelHandler = cy.stub();
cy.mount(
<EuiContextMenuPanel
items={items}
showNextPanel={showNextPanelHandler}
/>
);
cy.checkAxe();
});
});
});
120 changes: 120 additions & 0 deletions src/components/selectable/selectable.a11y.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

/// <reference types="../../../cypress/support"/>

import React, { useState } from 'react';

import { EuiButton } from '../button';
import { EuiPopover } from '../popover';
import { EuiSelectable, EuiSelectableProps } from './selectable';

const options: EuiSelectableProps['options'] = [
{
label: 'Titan',
'data-test-subj': 'titanOption',
},
{
label: 'Enceladus',
},
{
label:
"Pandora is one of Saturn's moons, named for a Titaness of Greek mythology",
},
];

const EuiSelectableListboxOnly = (args) => {
return (
<EuiSelectable options={options} {...args}>
{(list) => <>{list}</>}
</EuiSelectable>
);
};

const EuiSelectableWithSearchInput = (args) => {
return (
<EuiSelectable searchable options={options} {...args}>
{(list, search) => (
<>
{search}
{list}
</>
)}
</EuiSelectable>
);
};

describe('EuiSelectable', () => {
describe('with a `searchable` configuration', () => {
it('has no accessibility errors', () => {
const onChange = cy.stub();
cy.realMount(<EuiSelectableWithSearchInput onChange={onChange} />);
cy.checkAxe();
});
});

describe('without a `searchable` configuration', () => {
it('has no accessibility errors', () => {
const onChange = cy.stub();
cy.realMount(
<EuiSelectableListboxOnly
aria-label="No search box"
onChange={onChange}
/>
);
cy.checkAxe();
});
});

describe('nested in `EuiPopover` component', () => {
const EuiSelectableNested = () => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);

const onChange = () => {};
const onClosePopover = () => {};
const onButtonClick = () => {
setIsPopoverOpen(!isPopoverOpen);
};

const button = (
<EuiButton
iconType="arrowDown"
iconSide="right"
onClick={onButtonClick}
>
Show popover
</EuiButton>
);

return (
<EuiPopover
id="data-cy-popover-1"
panelPaddingSize="s"
button={button}
isOpen={isPopoverOpen}
closePopover={onClosePopover}
>
<EuiSelectableWithSearchInput
aria-label="With popover"
options={options}
onChange={onChange}
>
{(list) => <>{list}</>}
</EuiSelectableWithSearchInput>
</EuiPopover>
);
};

it('has no accessibility errors', () => {
cy.realMount(<EuiSelectableNested />);
cy.get('button').realClick();
cy.get('li[role=option]').first(); // Make sure the EuiSelectable is rendered before a11y check
cy.checkAxe();
});
});
});
24 changes: 0 additions & 24 deletions src/components/selectable/selectable.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,6 @@ describe('EuiSelectable', () => {
]);
});
});

it('has no accessibility errors', () => {
const onChange = cy.stub();
cy.realMount(<EuiSelectableWithSearchInput onChange={onChange} />);
cy.checkAxe();
});
});

describe('without a `searchable` configuration', () => {
Expand Down Expand Up @@ -232,17 +226,6 @@ describe('EuiSelectable', () => {
]);
});
});

it('has no accessibility errors', () => {
const onChange = cy.stub();
cy.realMount(
<EuiSelectableListboxOnly
aria-label="No search box"
onChange={onChange}
/>
);
cy.checkAxe();
});
});

describe('nested in `EuiPopover` component', () => {
Expand Down Expand Up @@ -290,12 +273,5 @@ describe('EuiSelectable', () => {
cy.realPress('Enter');
expect(cy.get('ul[role=listbox]')).to.exist;
});

it('has no accessibility errors', () => {
cy.realMount(<EuiSelectableNested />);
cy.get('button').realClick();
cy.get('li[role=option]').first(); // Make sure the EuiSelectable is rendered before a11y check
cy.checkAxe();
});
});
});
1 change: 1 addition & 0 deletions tsconfig-cypress.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"extends": "./tsconfig.json",
"include": [
"./src/**/*.spec.tsx",
"./src/**/*.a11y.tsx",
],
"compilerOptions": {
"types": ["cypress", "cypress-real-events", "cypress-axe"]
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,5 @@
"./src/**/*",
"./src-docs/**/*"
],
"exclude": ["node_modules", "**/*/*.spec.tsx"]
"exclude": ["node_modules", "**/*/*.spec.tsx", "**/*/*.a11y.tsx"]
}
Loading

0 comments on commit 5e32012

Please sign in to comment.