Adding fleet cypress a11y tests (#138192)

* Updated test to use uiSettings.

* Added initial files for adding Fleet A11y tests.

* Added setup for a11y test.

* Still working with combobox

* Got combobox to work.

* Added tests for Agents tag.

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* More tests.

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* Fixed typo.

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* Added more tests and stuff.

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* Added more tests and stuff.

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* Finished the fleet screens.

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* Added exception for cypres-real-events devDependency.

* Skipped test with a11y violation.

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* Ignoring dev dependencies.

* Look for input before trying to type into it.

* Updated cypress readme about a11y checks.

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
John Dorlus 2022-08-12 22:36:27 -04:00 committed by GitHub
parent d260286da6
commit 47bc3592c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 227 additions and 11 deletions

View file

@ -178,6 +178,7 @@ const DEV_PATTERNS = [
'src/dev/**/*',
'x-pack/{dev-tools,tasks,scripts,test,build_chromium}/**/*',
'x-pack/plugins/*/server/scripts/**/*',
'x-pack/plugins/fleet/cypress',
];
/** Restricted imports with suggested alternatives */

View file

@ -160,6 +160,12 @@ taken into consideration until another solution is implemented:
Remember that minimizing the number of times the web page is loaded, we minimize as well the execution time.
### Accessibility
The `checkA11y({ skipFailures: false });` call uses [axe-core](https://github.com/dequelabs/axe-core) to perform a full page check for accessibility violations.
See [axe-core](https://github.com/dequelabs/axe-core)'s documentation for details on what is checked for.
## Linting
Optional linting rules for Cypress and linting setup can be found [here](https://github.com/cypress-io/eslint-plugin-cypress#usage)

View file

@ -0,0 +1,141 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
/* eslint-disable-next-line import/no-extraneous-dependencies */
import 'cypress-real-events/support';
import { checkA11y } from '../../support/commands';
import { FLEET, navigateTo } from '../../tasks/navigation';
import {
GENERATE_FLEET_SERVER_POLICY_BUTTON,
AGENTS_QUICK_START_TAB_BUTTON,
PLATFORM_TYPE_LINUX_BUTTON,
AGENTS_ADVANCED_TAB_BUTTON,
ADVANCED_FLEET_SERVER_ADD_HOST_BUTTON,
ADVANCED_FLEET_SERVER_GENERATE_SERVICE_TOKEN_BUTTON,
AGENT_POLICIES_TAB,
AGENT_POLICIES_CREATE_AGENT_POLICY_BUTTON,
AGENT_POLICIES_CREATE_AGENT_POLICY_FLYOUT_TITLE,
AGENT_POLICY_CREATE_AGENT_POLICY_NAME_FIELD,
AGENT_POLICIES_FLYOUT_ADVANCED_DEFAULT_NAMESPACE_HEADER,
AGENT_POLICY_FLYOUT_CREATE_BUTTON,
ENROLLMENT_TOKENS_TAB,
ENROLLMENT_TOKENS_CREATE_TOKEN_BUTTON,
ENROLLMENT_TOKENS_CREATE_TOKEN_NAME_FIELD,
DATA_STREAMS_TAB,
SETTINGS_TAB,
SETTINGS_FLEET_SERVER_HOST_HEADING,
} from '../../screens/fleet';
import { AGENT_POLICY_NAME_LINK } from '../../screens/integrations';
import { cleanupAgentPolicies, unenrollAgent } from '../../tasks/cleanup';
describe('Home page', () => {
before(() => {
navigateTo(FLEET);
cy.getBySel(AGENTS_QUICK_START_TAB_BUTTON, { timeout: 15000 }).should('be.visible');
});
describe('Agents', () => {
const fleetServerHost = 'https://localhost:8220';
describe('Quick Start', () => {
it('Get started with fleet', () => {
checkA11y({ skipFailures: false });
});
it('Install Fleet Server', () => {
cy.getBySel('fleetServerHostInput', { timeout: 15000 }).should('be.visible');
cy.getBySel('fleetServerHostInput').getBySel('comboBoxSearchInput').type(fleetServerHost);
cy.getBySel(GENERATE_FLEET_SERVER_POLICY_BUTTON).click();
cy.getBySel(PLATFORM_TYPE_LINUX_BUTTON, { timeout: 15000 }).should('be.visible');
checkA11y({ skipFailures: false });
});
});
describe('Advanced', () => {
before(() => {
cy.getBySel(AGENTS_ADVANCED_TAB_BUTTON).click();
});
it('Select policy for fleet', () => {
checkA11y({ skipFailures: false });
});
it('Add your fleet sever host', () => {
cy.getBySel(ADVANCED_FLEET_SERVER_ADD_HOST_BUTTON).click();
checkA11y({ skipFailures: false });
});
it('Generate service token', () => {
cy.getBySel(ADVANCED_FLEET_SERVER_ADD_HOST_BUTTON, { timeout: 15000 }).should('be.visible');
cy.getBySel(ADVANCED_FLEET_SERVER_GENERATE_SERVICE_TOKEN_BUTTON).click();
cy.getBySel(PLATFORM_TYPE_LINUX_BUTTON, { timeout: 15000 }).should('be.visible');
checkA11y({ skipFailures: false });
});
});
});
describe('Agent Policies', () => {
before(() => {
cy.getBySel(AGENT_POLICIES_TAB).click();
cy.getBySel(AGENT_POLICIES_CREATE_AGENT_POLICY_BUTTON, { timeout: 15000 }).should(
'be.visible'
);
});
it('Agent Table', () => {
checkA11y({ skipFailures: false });
});
it('Create Policy Flyout', () => {
cy.getBySel(AGENT_POLICIES_CREATE_AGENT_POLICY_BUTTON).click();
cy.getBySel(AGENT_POLICIES_CREATE_AGENT_POLICY_FLYOUT_TITLE, { timeout: 15000 }).should(
'be.visible'
);
cy.getBySel(AGENT_POLICY_CREATE_AGENT_POLICY_NAME_FIELD).type('testName');
cy.get('.ingest-active-button').click();
cy.getBySel(AGENT_POLICIES_FLYOUT_ADVANCED_DEFAULT_NAMESPACE_HEADER, {
timeout: 15000,
}).should('be.visible');
checkA11y({ skipFailures: false });
});
it('Agent Table After Adding Another Agent', () => {
cy.getBySel(AGENT_POLICY_FLYOUT_CREATE_BUTTON).click();
cy.getBySel(AGENT_POLICY_NAME_LINK, { timeout: 15000 }).should('be.visible');
checkA11y({ skipFailures: true });
});
});
describe('Enrollment Tokens', () => {
before(() => {
cy.getBySel(ENROLLMENT_TOKENS_TAB).click();
});
it('Enrollment Tokens Table', () => {
cy.getBySel('tableHeaderCell_name_0', { timeout: 15000 }).should('be.visible');
checkA11y({ skipFailures: false });
});
it('Create Enrollment Token Modal', () => {
cy.getBySel(ENROLLMENT_TOKENS_CREATE_TOKEN_BUTTON).click();
cy.getBySel(ENROLLMENT_TOKENS_CREATE_TOKEN_NAME_FIELD, { timeout: 15000 }).should(
'be.visible'
);
checkA11y({ skipFailures: false });
});
});
describe('Data Streams', () => {
before(() => {
cy.getBySel('confirmModalCancelButton').click();
cy.getBySel(DATA_STREAMS_TAB, { timeout: 15000 }).should('be.visible');
cy.getBySel(DATA_STREAMS_TAB).click();
});
it('Datastreams Empty Table', () => {
cy.getBySel('tableHeaderSortButton', { timeout: 15000 }).should('be.visible');
checkA11y({ skipFailures: false });
});
});
describe.skip('Settings', () => {
// A11y Violation https://github.com/elastic/kibana/issues/138474
before(() => {
cy.getBySel(SETTINGS_TAB).click();
});
it('Settings Form', () => {
cy.getBySel(SETTINGS_FLEET_SERVER_HOST_HEADING, { timeout: 15000 }).should('be.visible');
checkA11y({ skipFailures: false });
});
});
after(() => {
unenrollAgent();
cleanupAgentPolicies();
});
});

View file

@ -14,6 +14,7 @@ export const AGENT_POLICY_CODE_BLOCK = 'agentPolicyCodeBlock';
export const AGENTS_TAB = 'fleet-agents-tab';
export const AGENT_POLICIES_TAB = 'fleet-agent-policies-tab';
export const ENROLLMENT_TOKENS_TAB = 'fleet-enrollment-tokens-tab';
export const DATA_STREAMS_TAB = 'fleet-datastreams-tab';
export const SETTINGS_TAB = 'fleet-settings-tab';
export const STANDALONE_TAB = 'standaloneTab';
export const MISSING_PRIVILEGES_TITLE = 'missingPrivilegesPromptTitle';
@ -23,6 +24,22 @@ export const FLEET_SERVER_MISSING_PRIVILEGES_TITLE = 'fleetServerMissingPrivileg
export const AGENT_POLICY_SAVE_INTEGRATION = 'saveIntegration';
export const PACKAGE_POLICY_TABLE_LINK = 'PackagePoliciesTableLink';
export const ADD_PACKAGE_POLICY_BTN = 'addPackagePolicyButton';
export const GENERATE_FLEET_SERVER_POLICY_BUTTON = 'generateFleetServerPolicyButton';
export const ADD_FLEET_SERVER_HEADER = 'addFleetServerHeader';
export const AGENTS_QUICK_START_TAB_BUTTON = 'fleetServerFlyoutTab-quickStart';
export const AGENTS_ADVANCED_TAB_BUTTON = 'fleetServerFlyoutTab-advanced';
export const PLATFORM_TYPE_LINUX_BUTTON = 'platformTypeLinux';
export const ADVANCED_FLEET_SERVER_ADD_HOST_BUTTON = 'fleetServerAddHostBtn';
export const ADVANCED_FLEET_SERVER_GENERATE_SERVICE_TOKEN_BUTTON =
'fleetServerGenerateServiceTokenBtn';
export const AGENT_POLICIES_CREATE_AGENT_POLICY_BUTTON = 'createAgentPolicyButton';
export const AGENT_POLICIES_CREATE_AGENT_POLICY_FLYOUT_TITLE = 'createAgentPolicyFlyoutTitle';
export const AGENT_POLICY_CREATE_AGENT_POLICY_NAME_FIELD = 'createAgentPolicyNameField';
export const AGENT_POLICIES_FLYOUT_ADVANCED_DEFAULT_NAMESPACE_HEADER = 'defaultNamespaceHeader';
export const AGENT_POLICY_FLYOUT_CREATE_BUTTON = 'createAgentPolicyFlyoutBtn';
export const ENROLLMENT_TOKENS_CREATE_TOKEN_BUTTON = 'createEnrollmentTokenButton';
export const ENROLLMENT_TOKENS_CREATE_TOKEN_NAME_FIELD = 'createEnrollmentTokenNameField';
export const SETTINGS_FLEET_SERVER_HOST_HEADING = 'fleetServerHostHeader';
export const AGENT_BINARY_SOURCES_TABLE = 'AgentDownloadSourcesTable';
export const AGENT_BINARY_SOURCES_TABLE_ACTIONS = {

View file

@ -15,3 +15,27 @@
// https://on.cypress.io/custom-commands
// ***********************************************
//
/* eslint-disable-next-line import/no-extraneous-dependencies */
import 'cypress-axe';
/* eslint-disable-next-line import/no-extraneous-dependencies */
import { AXE_CONFIG, AXE_OPTIONS } from '@kbn/axe-config';
const axeConfig = {
...AXE_CONFIG,
};
const axeOptions = {
...AXE_OPTIONS,
runOnly: [...AXE_OPTIONS.runOnly, 'best-practice'],
};
export const checkA11y = ({ skipFailures }: { skipFailures: boolean }) => {
// https://github.com/component-driven/cypress-axe#cychecka11y
cy.injectAxe();
cy.configureAxe(axeConfig);
const context = '.kbnAppWrapper'; // Scopes a11y checks to only our app
/**
* We can get rid of the last two params when we don't need to add skipFailures
* params = (context, options, violationCallback, skipFailures)
*/
cy.checkA11y(context, axeOptions, undefined, skipFailures);
};

View file

@ -10,7 +10,8 @@
"outDir": "target/types",
"types": [
"cypress",
"node"
"node",
"cypress-real-events"
],
"resolveJsonModule": true,
"target": "ES2019",

View file

@ -69,7 +69,7 @@ const Header: React.FunctionComponent<{
return (
<>
<EuiTitle size="m">
<h2>
<h2 data-test-subj="addFleetServerHeader">
<FormattedMessage
id="xpack.fleet.fleetServerFlyout.title"
defaultMessage="Add a Fleet Server"

View file

@ -116,7 +116,7 @@ const ServiceTokenStepContent: React.FunctionComponent<{
<EuiSpacer size="m" />
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem grow={false}>
<strong>
<strong data-test-subject="serviceTokenSaveReminderHeader">
<FormattedMessage
id="xpack.fleet.fleetServerSetup.serviceTokenLabel"
defaultMessage="Service token"

View file

@ -109,7 +109,7 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =
</EuiDescribedFormGroup>
<EuiDescribedFormGroup
title={
<h4>
<h4 data-test-subj="defaultNamespaceHeader">
<FormattedMessage
id="xpack.fleet.agentPolicyForm.namespaceFieldLabel"
defaultMessage="Default namespace"

View file

@ -174,7 +174,8 @@ export const AgentPolicyCreateInlineForm: React.FunctionComponent<Props> = ({
<>
<EuiSpacer size="s" />
<StyledEuiAccordion
id="advancedOptions"
id="advancedOptionsJustChanged"
data-test-subj="advancedOptionsButton"
buttonContent={
<FormattedMessage
id="xpack.fleet.agentPolicyForm.advancedOptionsToggleLabel"

View file

@ -43,6 +43,7 @@ export const AgentPolicyGeneralFields: React.FunctionComponent<Props> = ({
isInvalid={Boolean(touchedFields.name && validation.name)}
>
<EuiFieldText
data-test-subj="createAgentPolicyNameField"
disabled={agentPolicy.is_managed === true}
fullWidth
value={agentPolicy.name}

View file

@ -95,7 +95,6 @@ export const AgentPolicyIntegrationForm: React.FunctionComponent<Props> = ({
/>
}
buttonClassName="ingest-active-button"
data-test-subj="advancedOptionsBtn"
>
<EuiSpacer size="l" />
<AgentPolicyAdvancedOptionsContent

View file

@ -68,7 +68,7 @@ export const CreateAgentPolicyFlyout: React.FunctionComponent<Props> = ({
const header = (
<EuiFlyoutHeader hasBorder aria-labelledby="CreateAgentPolicyFlyoutTitle">
<EuiTitle size="m">
<h2 id="CreateAgentPolicyFlyoutTitle">
<h2 id="CreateAgentPolicyFlyoutTitle" data-test-subj="createAgentPolicyFlyoutTitle">
<FormattedMessage
id="xpack.fleet.createAgentPolicy.flyoutTitle"
defaultMessage="Create agent policy"

View file

@ -181,6 +181,25 @@ export const AgentPolicyListPage: React.FunctionComponent<{}> = () => {
iconType="plusInCircle"
isDisabled={!hasFleetAllPrivileges}
onClick={() => setIsCreateAgentPolicyFlyoutOpen(true)}
data-test-subj="createAgentPolicyButton"
>
<FormattedMessage
id="xpack.fleet.agentPolicyList.addButton"
defaultMessage="Create agent policy"
/>
</EuiButton>
),
[hasFleetAllPrivileges, setIsCreateAgentPolicyFlyoutOpen]
);
const emptyStateCreateAgentPolicyButton = useMemo(
() => (
<EuiButton
fill
iconType="plusInCircle"
isDisabled={!hasFleetAllPrivileges}
onClick={() => setIsCreateAgentPolicyFlyoutOpen(true)}
data-test-subj="emptyPromptCreateAgentPolicyButton"
>
<FormattedMessage
id="xpack.fleet.agentPolicyList.addButton"
@ -202,10 +221,10 @@ export const AgentPolicyListPage: React.FunctionComponent<{}> = () => {
/>
</h2>
}
actions={createAgentPolicyButton}
actions={emptyStateCreateAgentPolicyButton}
/>
),
[createAgentPolicyButton]
[emptyStateCreateAgentPolicyButton]
);
const onTableChange = (criteria: CriteriaWithPagination<AgentPolicy>) => {

View file

@ -305,7 +305,12 @@ export const EnrollmentTokenListPage: React.FunctionComponent<{}> = () => {
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton fill iconType="plusInCircle" onClick={() => setModalOpen(true)}>
<EuiButton
data-test-subj="createEnrollmentTokenButton"
fill
iconType="plusInCircle"
onClick={() => setModalOpen(true)}
>
<FormattedMessage
id="xpack.fleet.enrollmentTokensList.newKeyButton"
defaultMessage="Create enrollment token"

View file

@ -64,7 +64,7 @@ export const SettingsSection: React.FunctionComponent<SettingsSectionProps> = ({
return (
<>
<EuiTitle size="s">
<h4>
<h4 data-test-subj="fleetServerHostHeader">
<FormattedMessage
id="xpack.fleet.settings.fleetServerHostSectionTitle"
defaultMessage="Fleet server hosts"

View file

@ -115,6 +115,7 @@ export const NewEnrollmentTokenModal: React.FunctionComponent<Props> = ({
})}
>
<EuiFieldText
data-test-subj="createEnrollmentTokenNameField"
name="name"
autoComplete="off"
placeholder={i18n.translate('xpack.fleet.newEnrollmentKey.placeholder', {