From ec1ae4505b7ae111fed40f87d614a1b0121cc399 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Thu, 5 Oct 2023 14:55:22 +0200 Subject: [PATCH] tests: Add accessibility tests for CSS color variables Signed-off-by: Ferdinand Thiessen --- apps/theming/__tests__/accessibility.cy.ts | 116 +++++++++++++++++++++ cypress/support/component.ts | 2 + package-lock.json | 68 +++++++++++- package.json | 4 +- tsconfig.json | 2 +- 5 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 apps/theming/__tests__/accessibility.cy.ts diff --git a/apps/theming/__tests__/accessibility.cy.ts b/apps/theming/__tests__/accessibility.cy.ts new file mode 100644 index 00000000000..bea8e5febde --- /dev/null +++ b/apps/theming/__tests__/accessibility.cy.ts @@ -0,0 +1,116 @@ +// eslint-disable-next-line import/no-webpack-loader-syntax, import/no-unresolved +import style from '!raw-loader!../css/default.css' + +const testCases = { + 'Generic combinations': { + foregroundColors: [ + 'color-main-text', + // 'color-text-light', deprecated + // 'color-text-lighter', deprecated + 'color-text-maxcontrast', + 'color-text-maxcontrast-default', + 'color-error-text', + 'color-warning-text', + 'color-success-text', + 'color-info-text', + ], + backgroundColors: [ + 'color-background-main', + 'color-background-hover', + 'color-background-dark', + 'color-background-darker', + ], + }, + Primary: { + foregroundColors: [ + 'color-primary-text', + ], + backgroundColors: [ + 'color-primary-default', + 'color-primary', + 'color-primary-hover', + ], + }, + 'Primary light': { + foregroundColors: [ + 'color-primary-light-text', + ], + backgroundColors: [ + 'color-primary-light', + 'color-primary-light-hover', + ], + }, + 'Primary element': { + foregroundColors: [ + 'color-primary-element-text', + 'color-primary-element-text-dark', + ], + backgroundColors: [ + 'color-primary-element', + 'color-primary-element-hover', + ], + }, + 'Primary element light': { + foregroundColors: [ + 'color-primary-element-light-text', + ], + backgroundColors: [ + 'color-primary-element-light', + 'color-primary-element-light-hover', + ], + }, +} + +before(() => { + cy.injectAxe() + + const el = document.createElement('style') + el.innerText = style + document.head.appendChild(el) +}) + +/** + * Create a wrapper element with color and background set + * + * @param foreground The foreground color (css variable without leading --) + * @param background The background color + */ +function createTestCase(foreground: string, background: string) { + const wrapper = document.createElement('div') + wrapper.innerText = `${foreground} ${background}` + wrapper.style.color = `var(--${foreground})` + wrapper.style.backgroundColor = `var(--${background})` + wrapper.style.padding = '4px' + wrapper.setAttribute('data-cy-testcase', '') + return wrapper +} + +for (const [name, { backgroundColors, foregroundColors }] of Object.entries(testCases)) { + describe(`Accessibility of CSS color variables for ${name}`, () => { + afterEach(() => { + cy.document().then(doc => { + const root = doc.querySelector('[data-cy-root]') + if (root === null) { + throw new Error('No test root found') + } + for (const child of root.children) { + root.removeChild(child) + } + }) + }) + + for (const foreground of foregroundColors) { + for (const background of backgroundColors) { + it(`color contrast of ${foreground} on ${background}`, () => { + const element = createTestCase(foreground, background) + cy.document().then(doc => { + const root = doc.querySelector('[data-cy-root]') + console.warn(root) + root?.appendChild(element) + cy.checkA11y('[data-cy-testcase]') + }) + }) + } + } + }) +} diff --git a/cypress/support/component.ts b/cypress/support/component.ts index b1e0a1b2c0f..da56f124826 100644 --- a/cypress/support/component.ts +++ b/cypress/support/component.ts @@ -19,6 +19,8 @@ * along with this program. If not, see . * */ +import 'cypress-axe' + /* eslint-disable */ import { mount } from '@cypress/vue2' diff --git a/package-lock.json b/package-lock.json index 62286b993e3..e4748226ba7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,6 @@ "camelcase": "^8.0.0", "cancelable-promise": "^4.3.1", "clipboard": "^2.0.11", - "colord": "^2.9.3", "core-js": "^3.33.0", "davclient.js": "github:owncloud/davclient.js.git#0.2.1", "debounce": "^1.2.1", @@ -113,8 +112,10 @@ "babel-jest": "^29.6.4", "babel-loader": "^9.1.0", "babel-loader-exclude-node-modules-except": "^1.2.1", + "colord": "^2.9.3", "css-loader": "^6.8.1", "cypress": "^13.3.0", + "cypress-axe": "^1.5.0", "cypress-if": "^1.10.5", "cypress-split": "^1.15.3", "cypress-wait-until": "^2.0.1", @@ -141,6 +142,7 @@ "karma-viewport": "^1.0.9", "node-polyfill-webpack-plugin": "^2.0.1", "puppeteer": "^21.0.3", + "raw-loader": "^4.0.2", "regextras": "^0.8.0", "sass": "^1.66.1", "sass-loader": "^13.2.2", @@ -7729,6 +7731,16 @@ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", "dev": true }, + "node_modules/axe-core": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.8.2.tgz", + "integrity": "sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, "node_modules/axios": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", @@ -9222,7 +9234,8 @@ "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true }, "node_modules/colorette": { "version": "2.0.20", @@ -10004,6 +10017,19 @@ "node": "^16.0.0 || ^18.0.0 || >=20.0.0" } }, + "node_modules/cypress-axe": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/cypress-axe/-/cypress-axe-1.5.0.tgz", + "integrity": "sha512-Hy/owCjfj+25KMsecvDgo4fC/781ccL+e8p+UUYoadGVM2ogZF9XIKbiM6KI8Y3cEaSreymdD6ZzccbI2bY0lQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "axe-core": "^3 || ^4", + "cypress": "^10 || ^11 || ^12 || ^13" + } + }, "node_modules/cypress-if": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/cypress-if/-/cypress-if-1.10.5.tgz", @@ -22145,6 +22171,44 @@ "node": ">= 0.8" } }, + "node_modules/raw-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", + "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/raw-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/package.json b/package.json index c91cac50dee..8a3024cccd8 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,6 @@ "camelcase": "^8.0.0", "cancelable-promise": "^4.3.1", "clipboard": "^2.0.11", - "colord": "^2.9.3", "core-js": "^3.33.0", "davclient.js": "github:owncloud/davclient.js.git#0.2.1", "debounce": "^1.2.1", @@ -140,8 +139,10 @@ "babel-jest": "^29.6.4", "babel-loader": "^9.1.0", "babel-loader-exclude-node-modules-except": "^1.2.1", + "colord": "^2.9.3", "css-loader": "^6.8.1", "cypress": "^13.3.0", + "cypress-axe": "^1.5.0", "cypress-if": "^1.10.5", "cypress-split": "^1.15.3", "cypress-wait-until": "^2.0.1", @@ -168,6 +169,7 @@ "karma-viewport": "^1.0.9", "node-polyfill-webpack-plugin": "^2.0.1", "puppeteer": "^21.0.3", + "raw-loader": "^4.0.2", "regextras": "^0.8.0", "sass": "^1.66.1", "sass-loader": "^13.2.2", diff --git a/tsconfig.json b/tsconfig.json index 47b75a076d8..ea4818103c6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "extends": "@vue/tsconfig/tsconfig.json", "include": ["./apps/**/*.ts", "./core/**/*.ts", "./*.d.ts"], "compilerOptions": { - "types": ["cypress", "jest", "node", "vue"], + "types": ["cypress", "cypress-axe", "jest", "node", "vue"], "outDir": "./dist/", "target": "ESNext", "module": "esnext",