Skip to content

Commit e020b5f

Browse files
authored
Accessibility tests (#12900)
* WIP: Accessibility e2e test suite and support * Add e2e job for accessibility tests * Fix a11y tests * Remove test * Fix lint issues * Add report upload * Upload a11y screenshots * Remove failure gate on a11y screenshots * Test debugging * Fix lint * Fix setup issue * Tweak tests * Create a11y report * Fix screenshot folder * Build out more tests to cover a11y
1 parent 46aa285 commit e020b5f

File tree

13 files changed

+593
-43
lines changed

13 files changed

+593
-43
lines changed

.github/workflows/test.yaml

+58
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,65 @@ jobs:
142142
name: ${{github.run_number}}-${{github.run_attempt}}-screenshots-${{ matrix.role.tag }}+${{ matrix.features[0] }}
143143
path: cypress/screenshots
144144

145+
a11y-test:
146+
if: "!contains( github.event.pull_request.labels.*.name, 'ci/skip-e2e')"
147+
needs: e2e-ui-build
148+
runs-on: ubuntu-latest
149+
steps:
150+
- uses: actions/checkout@v4
151+
with:
152+
fetch-depth: 1
153+
- name: Setup env
154+
uses: ./.github/actions/setup
145155

156+
# Installing fixed version of Chrome since latest version does not work (128 didn't work)
157+
# Leaving this here again in case we need to pin to a specific Chrome version in the future
158+
- name: Install Chrome 127
159+
run: |
160+
sudo apt-get install -y wget libu2f-udev
161+
cd /tmp
162+
wget -q http://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_127.0.6533.72-1_amd64.deb
163+
sudo dpkg -i google-chrome-stable_127.0.6533.72-1_amd64.deb
164+
sudo apt-get install -y --allow-downgrades ./google-chrome-stable_127.0.6533.72-1_amd64.deb
165+
google-chrome --version
166+
167+
- name: Download e2e build
168+
uses: actions/download-artifact@v4
169+
with:
170+
name: ${{ env.E2E_BUILD_DIST_NAME }}
171+
path: ${{ env.E2E_BUILD_DIST_DIR }}
172+
- name: Download e2e build ember
173+
uses: actions/download-artifact@v4
174+
with:
175+
name: ${{ env.E2E_BUILD_DIST_EMBER_NAME }}
176+
path: ${{ env.E2E_BUILD_DIST_EMBER_DIR }}
177+
178+
- name: Run Rancher
179+
run: yarn e2e:docker
180+
181+
- name: Setup Rancher and user
182+
run: |
183+
yarn e2e:prod
184+
env:
185+
GREP_TAGS: "@adminUserSetup+@accessibility --@jenkins"
186+
TEST_USERNAME: admin
187+
TEST_ONLY: setup
188+
- name: Run user tests
189+
run: |
190+
yarn e2e:prod
191+
[ "$BUILD_DASHBOARD" != "false" ] || exit 0
192+
env:
193+
TEST_SKIP: setup
194+
GREP_TAGS: "@adminUser+@accessibility --@jenkins"
195+
TEST_USERNAME: admin
196+
TEST_A11Y: true
197+
198+
- name: Upload report
199+
uses: actions/upload-artifact@v4
200+
with:
201+
name: accessibility-report
202+
path: cypress/accessibility
203+
146204
unit-test:
147205
runs-on: ubuntu-latest
148206
steps:

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ sw.*
9090
# Cypress e2e testing
9191
cypress/videos
9292
cypress/screenshots
93+
cypress/accessibility
94+
setupTestEnv.sh
9395

9496
# Storybook
9597
storybook-static/

cypress.config.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { defineConfig } from 'cypress';
33
import { removeDirectory } from 'cypress-delete-downloads-folder';
44
import { getSpecPattern } from '@/scripts/cypress';
55
import websocketTasks from './cypress/support/utils/webSocket-utils';
6+
import path from 'path';
67

78
// Required for env vars to be available in cypress
89
require('dotenv').config();
@@ -11,13 +12,17 @@ require('dotenv').config();
1112
* VARIABLES
1213
*/
1314
const hasCoverage = (process.env.TEST_INSTRUMENT === 'true') || false; // Add coverage if instrumented
14-
const testDirs = ['priority', 'components', 'setup', 'pages', 'navigation', 'global-ui', 'features', 'extensions'];
15+
let testDirs = ['priority', 'components', 'setup', 'pages', 'navigation', 'global-ui', 'features', 'extensions'];
1516
const skipSetup = process.env.TEST_SKIP?.includes('setup');
1617
const baseUrl = (process.env.TEST_BASE_URL || 'https://localhost:8005').replace(/\/$/, '');
1718
const DEFAULT_USERNAME = 'admin';
1819
const username = process.env.TEST_USERNAME || DEFAULT_USERNAME;
1920
const apiUrl = process.env.API || (baseUrl.endsWith('/dashboard') ? baseUrl.split('/').slice(0, -1).join('/') : baseUrl);
2021

22+
if (process.env.TEST_A11Y) {
23+
testDirs = ['accessibility'];
24+
}
25+
2126
/**
2227
* LOGS:
2328
* Summary of the environment variables that we have detected (or are going ot use)
@@ -97,6 +102,8 @@ export default defineConfig({
97102
azureClientSecret: process.env.AZURE_CLIENT_SECRET,
98103
customNodeIp: process.env.CUSTOM_NODE_IP,
99104
customNodeKey: process.env.CUSTOM_NODE_KEY,
105+
accessibility: !!process.env.TEST_A11Y, // Are we running accessibility tests?
106+
a11yFolder: path.join('.', 'cypress', 'accessibility'),
100107
gkeServiceAccount: process.env.GKE_SERVICE_ACCOUNT,
101108
},
102109
e2e: {
@@ -106,6 +113,12 @@ export default defineConfig({
106113
require('@cypress/code-coverage/task')(on, config);
107114
require('@cypress/grep/src/plugin')(config);
108115
// For more info: https://www.npmjs.com/package/cypress-delete-downloads-folder
116+
117+
// Load Accessibility plugin if configured
118+
if (process.env.TEST_A11Y) {
119+
require('./cypress/support/plugins/accessibility').default(on, config);
120+
}
121+
109122
on('task', { removeDirectory });
110123
websocketTasks(on, config);
111124

cypress/e2e/po/pages/account-api-keys.po.ts

+4
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ export default class AccountPagePo extends PagePo {
5656
return this.applyButton().click();
5757
}
5858

59+
cancel(): Cypress.Chainable {
60+
return this.self().get('button[type="reset"]').click();
61+
}
62+
5963
currentPassword(): PasswordPo {
6064
return new PasswordPo('[data-testid="account__current_password"]');
6165
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { LoginPagePo } from '@/cypress/e2e/po/pages/login-page.po';
2+
import HomePagePo from '@/cypress/e2e/po/pages/home.po';
3+
import AboutPagePo from '@/cypress/e2e/po/pages/about.po';
4+
import PreferencesPagePo from '@/cypress/e2e/po/pages/preferences.po';
5+
import UserMenuPo from '@/cypress/e2e/po/side-bars/user-menu.po';
6+
import AccountPagePo from '@/cypress/e2e/po/pages/account-api-keys.po';
7+
import CreateKeyPagePo from '@/cypress/e2e/po/pages/account-api-keys-create_key.po';
8+
9+
describe('Shell a11y testing', { tags: ['@adminUser', '@accessibility'] }, () => {
10+
it('login page', () => {
11+
const loginPage = new LoginPagePo();
12+
13+
loginPage.goTo();
14+
loginPage.waitForPage();
15+
cy.injectAxe();
16+
loginPage.username().set('test user');
17+
18+
cy.checkPageAccessibility();
19+
});
20+
21+
it('locale selector', () => {
22+
const loginPage = new LoginPagePo();
23+
24+
loginPage.goTo();
25+
loginPage.waitForPage();
26+
cy.injectAxe();
27+
cy.get('[data-testid="locale-selector"]').click();
28+
cy.checkPageAccessibility();
29+
});
30+
31+
describe('Logged in', { testIsolation: 'off' }, () => {
32+
const aboutPage = new AboutPagePo();
33+
const prefPage = new PreferencesPagePo();
34+
const userMenu = new UserMenuPo();
35+
36+
before(() => {
37+
cy.login();
38+
});
39+
40+
it('home page', () => {
41+
HomePagePo.goToAndWaitForGet();
42+
cy.injectAxe();
43+
44+
cy.checkPageAccessibility();
45+
});
46+
47+
it('about page', () => {
48+
AboutPagePo.navTo();
49+
aboutPage.waitForPage();
50+
51+
cy.checkPageAccessibility();
52+
});
53+
54+
it('preferences page', () => {
55+
userMenu.clickMenuItem('Preferences');
56+
userMenu.isClosed();
57+
prefPage.waitForPage();
58+
prefPage.checkIsCurrentPage();
59+
prefPage.title();
60+
cy.injectAxe();
61+
62+
cy.checkPageAccessibility();
63+
});
64+
65+
describe('account', () => {
66+
const accountPage = new AccountPagePo();
67+
const createKeyPage = new CreateKeyPagePo();
68+
69+
it('account page', () => {
70+
userMenu.clickMenuItem('Account & API Keys');
71+
accountPage.waitForPage();
72+
cy.injectAxe();
73+
accountPage.checkIsCurrentPage();
74+
75+
cy.checkPageAccessibility();
76+
});
77+
78+
it('change password dialog', () => {
79+
accountPage.changePassword();
80+
81+
cy.checkElementAccessibility('.change-password-modal');
82+
83+
accountPage.cancel();
84+
});
85+
86+
it('create API key', () => {
87+
accountPage.create();
88+
createKeyPage.waitForPage();
89+
createKeyPage.isCurrentPage();
90+
91+
cy.checkPageAccessibility();
92+
});
93+
});
94+
});
95+
});

cypress/e2e/tests/setup/rancher-setup.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { serverUrlLocalhostCases, urlWithTrailingForwardSlash, httpUrl, nonUrlCa
66

77
// Cypress or the GrepTags avoid to run multiples times the same test for each tag used.
88
// This is a temporary solution till initialization is not handled as a test
9-
describe('Rancher setup', { tags: ['@adminUserSetup', '@standardUserSetup', '@setup', '@components', '@navigation', '@charts', '@explorer', '@explorer2', '@extensions', '@fleet', '@generic', '@globalSettings', '@manager', '@userMenu', '@usersAndAuths', '@elemental', '@vai', '@virtualizationMgmt'] }, () => {
9+
describe('Rancher setup', { tags: ['@adminUserSetup', '@standardUserSetup', '@setup', '@components', '@navigation', '@charts', '@explorer', '@explorer2', '@extensions', '@fleet', '@generic', '@globalSettings', '@manager', '@userMenu', '@usersAndAuths', '@elemental', '@vai', '@virtualizationMgmt', '@accessibility'] }, () => {
1010
const rancherSetupLoginPage = new RancherSetupLoginPagePo();
1111
const rancherSetupConfigurePage = new RancherSetupConfigurePage();
1212
const homePage = new HomePagePo();

cypress/globals.d.ts

+10
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,16 @@ declare global {
175175
* Check if the vai FF is enabled
176176
*/
177177
isVaiCacheEnabled(): Chainable<boolean>;
178+
179+
/**
180+
* Run an accessibility check on the current page or the specified element
181+
*/
182+
checkPageAccessibility(description?: string);
183+
184+
/**
185+
* Run an accessibility check on the specified element
186+
*/
187+
checkElementAccessibility(selector: any, description?: string);
178188
}
179189
}
180190
}

0 commit comments

Comments
 (0)