From 566ee4f6d94f541ac8577d2e350fc111f86bcfb8 Mon Sep 17 00:00:00 2001 From: Mike van Veenhuijzen Date: Mon, 29 Apr 2024 17:27:10 +0200 Subject: [PATCH 01/13] chore(e2e): wcag compliance using axe-playwright --- platforms/web/package.json | 1 + .../web/test-e2e/tests/accessibility_test.ts | 39 +++++++++ platforms/web/test-e2e/utils/steps_file.ts | 9 ++ platforms/web/tsconfig.json | 3 +- yarn.lock | 87 ++++++++++++++++++- 5 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 platforms/web/test-e2e/tests/accessibility_test.ts diff --git a/platforms/web/package.json b/platforms/web/package.json index ddd55da58..3de5b3ed9 100644 --- a/platforms/web/package.json +++ b/platforms/web/package.json @@ -53,6 +53,7 @@ "@vitejs/plugin-react": "^4.0.4", "@vitest/coverage-v8": "^0.33.0", "allure-commandline": "^2.17.2", + "axe-playwright": "^2.0.1", "babel-plugin-transform-typescript-metadata": "^0.3.2", "codeceptjs": "3.5.5", "eslint-plugin-codeceptjs": "^1.3.0", diff --git a/platforms/web/test-e2e/tests/accessibility_test.ts b/platforms/web/test-e2e/tests/accessibility_test.ts new file mode 100644 index 000000000..6ab31f749 --- /dev/null +++ b/platforms/web/test-e2e/tests/accessibility_test.ts @@ -0,0 +1,39 @@ +import { testConfigs } from '@jwp/ott-testing/constants'; + +Feature('accessbility'); + +Scenario('Homepage WCAG compliant', async ({ I }) => { + I.useConfig(testConfigs.basicNoAuth); + I.amOnPage('/'); + I.checkA11y(undefined); +}); + +Scenario('Video detail page WCAG compliant', async ({ I }) => { + I.useConfig(testConfigs.basicNoAuth); + I.amOnPage('/m/awWEFyPu/big-buck-bunny'); + I.checkA11y(undefined); +}); + +Scenario('Playlist page WCAG compliant', async ({ I }) => { + await I.amOnPage('/p/dGSUzs9o/'); // "Films" page + I.checkA11y(undefined); +}); + +Scenario('Video detail inline page WCAG compliant', async ({ I }) => { + // Fails because of role="image" with aria-label + I.useConfig(testConfigs.inlinePlayer); + I.amOnPage('/m/awWEFyPu/big-buck-bunny'); + I.checkA11y(undefined, { verbose: true, detailedReport: true, detailedReportOptions: { html: true } }); // Debug error +}); + +// Scenario('Signup modal WCAG compliant', async ({ I }) => { +// I.useConfig(testConfigs.jwpAuth); +// I.amOnPage('/?u=create-account'); +// I.checkA11y(undefined); +// }); + +// Scenario('Signin modal WCAG compliant', async ({ I }) => { +// I.useConfig(testConfigs.jwpAuth); +// I.amOnPage('/?u=login'); +// I.checkA11y(undefined); +// }); diff --git a/platforms/web/test-e2e/utils/steps_file.ts b/platforms/web/test-e2e/utils/steps_file.ts index 070101fba..fa1e8c7dc 100644 --- a/platforms/web/test-e2e/utils/steps_file.ts +++ b/platforms/web/test-e2e/utils/steps_file.ts @@ -1,6 +1,9 @@ import * as assert from 'assert'; import { TestConfig } from '@jwp/ott-testing/constants'; +import { type ElementContext } from 'axe-core'; +import { injectAxe, checkA11y } from 'axe-playwright'; +import { AxeOptions } from 'axe-playwright/dist/types'; import { randomDate } from './randomizers'; @@ -541,6 +544,12 @@ const stepsObj = { clickHome: function (this: CodeceptJS.I) { this.click('a[href="/"]'); }, + checkA11y: function (context?: ElementContext, axeOptions?: AxeOptions) { + this.usePlaywrightTo('Run accessibility tests', async ({ page }) => { + await injectAxe(page); + await checkA11y(page, context, axeOptions); + }); + }, }; declare global { // noinspection JSUnusedGlobalSymbols diff --git a/platforms/web/tsconfig.json b/platforms/web/tsconfig.json index b63d0a57f..7015ffd9b 100644 --- a/platforms/web/tsconfig.json +++ b/platforms/web/tsconfig.json @@ -13,7 +13,8 @@ "@testing-library/jest-dom", "@types/jwplayer", "vite-plugin-pwa/client", - "reflect-metadata" + "reflect-metadata", + "axe-playwright" ] }, "include": [ diff --git a/yarn.lock b/yarn.lock index e8d77714d..6abc78830 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2590,6 +2590,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/junit-report-builder@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/junit-report-builder/-/junit-report-builder-3.0.2.tgz#17cc131d14ceff59dcf14e5847bd971b96f2cbe0" + integrity sha512-R5M+SYhMbwBeQcNXYWNCZkl09vkVfAtcPIaCGdzIkkbeaTrVbGQ7HVgi4s+EmM/M1K4ZuWQH0jGcvMvNePfxYA== + "@types/jwplayer@^8.2.13": version "8.28.4" resolved "https://registry.yarnpkg.com/@types/jwplayer/-/jwplayer-8.28.4.tgz#ee2fa65a48cc6bfa6472464e92c90b0d6b053a04" @@ -3382,6 +3387,30 @@ axe-core@4.4.1: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.1.tgz#7dbdc25989298f9ad006645cd396782443757413" integrity sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw== +axe-core@^4.5.1: + version "4.9.0" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.9.0.tgz#b18971494551ab39d4ff5f7d4c6411bd20cc7c2a" + integrity sha512-H5orY+M2Fr56DWmMFpMrq5Ge93qjNdPVqzBv5gWK3aD1OvjBEJlEzxf09z93dGVQeI0LiW+aCMIx1QtShC/zUw== + +axe-html-reporter@2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/axe-html-reporter/-/axe-html-reporter-2.2.3.tgz#2d56e239fe9bd1f09ba0735d94596bf79dd389a7" + integrity sha512-io8aCEt4fJvv43W+33n3zEa8rdplH5Ti2v5fOnth3GBKLhLHarNs7jj46xGfpnGnpaNrz23/tXPHC3HbwTzwwA== + dependencies: + mustache "^4.0.1" + rimraf "^3.0.2" + +axe-playwright@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/axe-playwright/-/axe-playwright-2.0.1.tgz#c474be6b7f3826e6a4cd1955ef006e33e4a145b9" + integrity sha512-MHjNjGARulF9XzqSfspmNjw+tpBz4x9o1VlTuLWEUW9fqzhn+xWa1qEpuOIQPbsRWQiLfooDjQAunLeE0PM5AQ== + dependencies: + "@types/junit-report-builder" "^3.0.0" + axe-core "^4.5.1" + axe-html-reporter "2.2.3" + junit-report-builder "^3.0.1" + picocolors "^1.0.0" + axios@1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.3.tgz#e7011384ba839b885007c9c9fae1ff23dceb295b" @@ -4478,6 +4507,11 @@ date-fns@^2.28.0: dependencies: "@babel/runtime" "^7.21.0" +date-format@4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.3.tgz#f63de5dc08dc02efd8ef32bf2a6918e486f35873" + integrity sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ== + de-indent@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" @@ -7084,6 +7118,16 @@ jsonpointer@^5.0.0: object.assign "^4.1.4" object.values "^1.1.6" +junit-report-builder@^3.0.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/junit-report-builder/-/junit-report-builder-3.2.1.tgz#93067512353c3d47d2dd5913e695b32d3262096c" + integrity sha512-IMCp5XyDQ4YESDE4Za7im3buM0/7cMnRfe17k2X8B05FnUl9vqnaliX6cgOEmPIeWKfJrEe/gANRq/XgqttCqQ== + dependencies: + date-format "4.0.3" + lodash "^4.17.21" + make-dir "^3.1.0" + xmlbuilder "^15.1.1" + jwt-decode@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" @@ -7488,7 +7532,7 @@ make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -make-dir@^3.0.0, make-dir@^3.0.2: +make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -7849,6 +7893,11 @@ ms@2.1.3, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +mustache@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" + integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== + mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" @@ -10034,7 +10083,7 @@ string-argv@0.3.2: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -10051,6 +10100,15 @@ string-width@^2.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -10148,7 +10206,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -10169,6 +10227,13 @@ strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -11773,7 +11838,16 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -11857,6 +11931,11 @@ xml-name-validator@^4.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== +xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" From 7c25d428ac86fd85f4b7d29840f9b0c96d337d71 Mon Sep 17 00:00:00 2001 From: Mike van Veenhuijzen Date: Mon, 29 Apr 2024 17:28:15 +0200 Subject: [PATCH 02/13] fix(a11y): axe heading-order rule --- packages/ui-react/src/components/Shelf/Shelf.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui-react/src/components/Shelf/Shelf.tsx b/packages/ui-react/src/components/Shelf/Shelf.tsx index eb89179b4..159c79d29 100644 --- a/packages/ui-react/src/components/Shelf/Shelf.tsx +++ b/packages/ui-react/src/components/Shelf/Shelf.tsx @@ -146,7 +146,7 @@ const Shelf = ({ return (
- {!featured ?

{title || playlist.title}

: null} +

{title || playlist.title}

items={playlist.playlist} tilesToShow={tilesToShow} From 0c27c1c1ea5b71c02f7b5287b325518618bd3ecc Mon Sep 17 00:00:00 2001 From: Mike van Veenhuijzen Date: Mon, 29 Apr 2024 17:33:02 +0200 Subject: [PATCH 03/13] fix(a11y): axe aria-valid-attr-value rule --- packages/ui-react/src/components/LayoutGrid/LayoutGrid.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui-react/src/components/LayoutGrid/LayoutGrid.tsx b/packages/ui-react/src/components/LayoutGrid/LayoutGrid.tsx index 385a9abab..4124b5f5a 100644 --- a/packages/ui-react/src/components/LayoutGrid/LayoutGrid.tsx +++ b/packages/ui-react/src/components/LayoutGrid/LayoutGrid.tsx @@ -113,13 +113,13 @@ const LayoutGrid = ({ className, columnCount, data, renderC return (
{Array.from({ length: rowCount }).map((_, rowIndex) => ( -
+
{data.slice(rowIndex * columnCount, rowIndex * columnCount + columnCount).map((item, columnIndex) => (
From 9b35f023089af99c4991b208f6df91a2ef1d551b Mon Sep 17 00:00:00 2001 From: Mike van Veenhuijzen Date: Mon, 29 Apr 2024 17:33:46 +0200 Subject: [PATCH 04/13] chore: code cleanup --- platforms/web/test-e2e/tests/accessibility_test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/platforms/web/test-e2e/tests/accessibility_test.ts b/platforms/web/test-e2e/tests/accessibility_test.ts index 6ab31f749..5f0f9f554 100644 --- a/platforms/web/test-e2e/tests/accessibility_test.ts +++ b/platforms/web/test-e2e/tests/accessibility_test.ts @@ -5,18 +5,18 @@ Feature('accessbility'); Scenario('Homepage WCAG compliant', async ({ I }) => { I.useConfig(testConfigs.basicNoAuth); I.amOnPage('/'); - I.checkA11y(undefined); + I.checkA11y(); }); Scenario('Video detail page WCAG compliant', async ({ I }) => { I.useConfig(testConfigs.basicNoAuth); I.amOnPage('/m/awWEFyPu/big-buck-bunny'); - I.checkA11y(undefined); + I.checkA11y(); }); Scenario('Playlist page WCAG compliant', async ({ I }) => { await I.amOnPage('/p/dGSUzs9o/'); // "Films" page - I.checkA11y(undefined); + I.checkA11y(); }); Scenario('Video detail inline page WCAG compliant', async ({ I }) => { @@ -29,11 +29,11 @@ Scenario('Video detail inline page WCAG compliant', async ({ I }) => { // Scenario('Signup modal WCAG compliant', async ({ I }) => { // I.useConfig(testConfigs.jwpAuth); // I.amOnPage('/?u=create-account'); -// I.checkA11y(undefined); +// I.checkA11y(); // }); // Scenario('Signin modal WCAG compliant', async ({ I }) => { // I.useConfig(testConfigs.jwpAuth); // I.amOnPage('/?u=login'); -// I.checkA11y(undefined); +// I.checkA11y(); // }); From 2fe06bc8e8c5116e4a92c35ae8ea52a6c1aae28d Mon Sep 17 00:00:00 2001 From: Mike van Veenhuijzen Date: Sun, 26 May 2024 13:24:20 +0200 Subject: [PATCH 05/13] chore: add ignore option for certain elements --- .../web/test-e2e/tests/accessibility_test.ts | 9 +++++-- platforms/web/test-e2e/utils/steps_file.ts | 24 +++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/platforms/web/test-e2e/tests/accessibility_test.ts b/platforms/web/test-e2e/tests/accessibility_test.ts index 5f0f9f554..feaf35281 100644 --- a/platforms/web/test-e2e/tests/accessibility_test.ts +++ b/platforms/web/test-e2e/tests/accessibility_test.ts @@ -16,14 +16,19 @@ Scenario('Video detail page WCAG compliant', async ({ I }) => { Scenario('Playlist page WCAG compliant', async ({ I }) => { await I.amOnPage('/p/dGSUzs9o/'); // "Films" page - I.checkA11y(); + I.checkA11y(null, { + ignore: [ + // { selector: 'html', id: 'document-title' }, + // { id: 'aria-valid-attr-value', selector: '[role="gridcell"]' }, + ], + }); }); Scenario('Video detail inline page WCAG compliant', async ({ I }) => { // Fails because of role="image" with aria-label I.useConfig(testConfigs.inlinePlayer); I.amOnPage('/m/awWEFyPu/big-buck-bunny'); - I.checkA11y(undefined, { verbose: true, detailedReport: true, detailedReportOptions: { html: true } }); // Debug error + I.checkA11y(); }); // Scenario('Signup modal WCAG compliant', async ({ I }) => { diff --git a/platforms/web/test-e2e/utils/steps_file.ts b/platforms/web/test-e2e/utils/steps_file.ts index fa1e8c7dc..e850e0417 100644 --- a/platforms/web/test-e2e/utils/steps_file.ts +++ b/platforms/web/test-e2e/utils/steps_file.ts @@ -1,9 +1,10 @@ import * as assert from 'assert'; import { TestConfig } from '@jwp/ott-testing/constants'; -import { type ElementContext } from 'axe-core'; -import { injectAxe, checkA11y } from 'axe-playwright'; -import { AxeOptions } from 'axe-playwright/dist/types'; +import { type ElementContext, type RunOptions } from 'axe-core'; +import { injectAxe, getViolations, reportViolations } from 'axe-playwright'; +import DefaultTerminalReporter from 'axe-playwright/dist/reporter/defaultTerminalReporter'; +import { JSDOM } from 'jsdom'; import { randomDate } from './randomizers'; @@ -15,6 +16,8 @@ const loaderElement = '[class*=_loadingOverlay]'; type SwipeTarget = { text: string } | { xpath: string }; type SwipeDirection = { direction: 'left' | 'right' } | { points: { x1: number; y1: number; x2: number; y2: number } }; +type IgnoreRule = { id: string; selector: string }; +type AccessbilityOptions = { ignore?: IgnoreRule[] } & RunOptions; const stepsObj = { useConfig: function (this: CodeceptJS.I, config: TestConfig) { @@ -544,10 +547,21 @@ const stepsObj = { clickHome: function (this: CodeceptJS.I) { this.click('a[href="/"]'); }, - checkA11y: function (context?: ElementContext, axeOptions?: AxeOptions) { + checkA11y: function (context?: ElementContext | null, options: AccessbilityOptions = {}) { + const debug = process.argv.includes('--debug'); this.usePlaywrightTo('Run accessibility tests', async ({ page }) => { await injectAxe(page); - await checkA11y(page, context, axeOptions); + const violations = await getViolations(page, context || undefined); + const impactedViolations = violations.filter((violation) => { + const domDocuments = violation.nodes.map((node) => new JSDOM(node.html).window.document); + return !options.ignore?.some((rule) => { + return violation.id === rule.id && domDocuments.every((document) => !!document.querySelector(rule.selector)); + }); + }); + if (impactedViolations.length > 0) { + reportViolations(impactedViolations, new DefaultTerminalReporter(debug, false, debug)); + assert.fail(`Failed WCAG check with ${impactedViolations.length} violation(s)`); + } }); }, }; From 41e2c389827fdc55f87f3fde197700e5e0251237 Mon Sep 17 00:00:00 2001 From: Mike van Veenhuijzen Date: Mon, 27 May 2024 14:45:28 +0200 Subject: [PATCH 06/13] chore: improve e2e stability for accessibility --- platforms/web/test-e2e/tests/accessibility_test.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/platforms/web/test-e2e/tests/accessibility_test.ts b/platforms/web/test-e2e/tests/accessibility_test.ts index feaf35281..2d6e341aa 100644 --- a/platforms/web/test-e2e/tests/accessibility_test.ts +++ b/platforms/web/test-e2e/tests/accessibility_test.ts @@ -2,17 +2,21 @@ import { testConfigs } from '@jwp/ott-testing/constants'; Feature('accessbility'); +const disableRetryFailedStep = (test: { disableRetryFailedStep: boolean }): void => { + test.disableRetryFailedStep = true; +}; + Scenario('Homepage WCAG compliant', async ({ I }) => { I.useConfig(testConfigs.basicNoAuth); I.amOnPage('/'); I.checkA11y(); -}); +}).config(disableRetryFailedStep); Scenario('Video detail page WCAG compliant', async ({ I }) => { I.useConfig(testConfigs.basicNoAuth); I.amOnPage('/m/awWEFyPu/big-buck-bunny'); I.checkA11y(); -}); +}).config(disableRetryFailedStep); Scenario('Playlist page WCAG compliant', async ({ I }) => { await I.amOnPage('/p/dGSUzs9o/'); // "Films" page @@ -22,14 +26,14 @@ Scenario('Playlist page WCAG compliant', async ({ I }) => { // { id: 'aria-valid-attr-value', selector: '[role="gridcell"]' }, ], }); -}); +}).config(disableRetryFailedStep); Scenario('Video detail inline page WCAG compliant', async ({ I }) => { // Fails because of role="image" with aria-label I.useConfig(testConfigs.inlinePlayer); I.amOnPage('/m/awWEFyPu/big-buck-bunny'); I.checkA11y(); -}); +}).config(disableRetryFailedStep); // Scenario('Signup modal WCAG compliant', async ({ I }) => { // I.useConfig(testConfigs.jwpAuth); From 80314ae17b13ea813fa2ec2b9018eae4634fd59c Mon Sep 17 00:00:00 2001 From: Melissa 't Hart Date: Wed, 29 May 2024 17:14:03 +0200 Subject: [PATCH 07/13] fix(a11y): aria-prohibited-attr rule and add account pages tests --- .../VideoListItem/VideoListItem.tsx | 2 +- .../web/test-e2e/tests/accessibility_test.ts | 35 +++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/packages/ui-react/src/components/VideoListItem/VideoListItem.tsx b/packages/ui-react/src/components/VideoListItem/VideoListItem.tsx index e975f0011..250eab4f3 100644 --- a/packages/ui-react/src/components/VideoListItem/VideoListItem.tsx +++ b/packages/ui-react/src/components/VideoListItem/VideoListItem.tsx @@ -73,7 +73,7 @@ function VideoListItem({ onHover, progress, activeLabel, item, url, loading = fa setImageLoaded(true)} width={320} alt="" />
{isLocked && ( -
+
)} diff --git a/platforms/web/test-e2e/tests/accessibility_test.ts b/platforms/web/test-e2e/tests/accessibility_test.ts index 2d6e341aa..5076696bc 100644 --- a/platforms/web/test-e2e/tests/accessibility_test.ts +++ b/platforms/web/test-e2e/tests/accessibility_test.ts @@ -1,24 +1,24 @@ import { testConfigs } from '@jwp/ott-testing/constants'; -Feature('accessbility'); +Feature('Accessibility'); const disableRetryFailedStep = (test: { disableRetryFailedStep: boolean }): void => { test.disableRetryFailedStep = true; }; -Scenario('Homepage WCAG compliant', async ({ I }) => { +Scenario('WCAG compliant - Home Page', async ({ I }) => { I.useConfig(testConfigs.basicNoAuth); I.amOnPage('/'); I.checkA11y(); }).config(disableRetryFailedStep); -Scenario('Video detail page WCAG compliant', async ({ I }) => { +Scenario('WCAG compliant - Video Detail Page', async ({ I }) => { I.useConfig(testConfigs.basicNoAuth); I.amOnPage('/m/awWEFyPu/big-buck-bunny'); I.checkA11y(); }).config(disableRetryFailedStep); -Scenario('Playlist page WCAG compliant', async ({ I }) => { +Scenario('WCAG compliant - Playlist Page', async ({ I }) => { await I.amOnPage('/p/dGSUzs9o/'); // "Films" page I.checkA11y(null, { ignore: [ @@ -28,14 +28,37 @@ Scenario('Playlist page WCAG compliant', async ({ I }) => { }); }).config(disableRetryFailedStep); -Scenario('Video detail inline page WCAG compliant', async ({ I }) => { +Scenario('WCAG compliant - Video Detail Inline Page', async ({ I }) => { // Fails because of role="image" with aria-label I.useConfig(testConfigs.inlinePlayer); I.amOnPage('/m/awWEFyPu/big-buck-bunny'); I.checkA11y(); }).config(disableRetryFailedStep); -// Scenario('Signup modal WCAG compliant', async ({ I }) => { +// Account Pages + +Scenario('WCAG compliant - Account Page', async ({ I }) => { + I.useConfig(testConfigs.jwpAuth); + I.amOnPage('/u/my-account'); + I.checkA11y(); +}).config(disableRetryFailedStep); + +Scenario('WCAG compliant - Payments Page', async ({ I }) => { + I.useConfig(testConfigs.jwpAuth); + I.amOnPage('/u/payments'); + I.checkA11y(); +}).config(disableRetryFailedStep); + +Scenario('WCAG compliant - Favorites Page', async ({ I }) => { + I.useConfig(testConfigs.jwpAuth); + I.amOnPage('/u/favorites'); + I.checkA11y(); +}).config(disableRetryFailedStep); + + +// Modals + +// Scenario('Signup - Modal - WCAG compliant', async ({ I }) => { // I.useConfig(testConfigs.jwpAuth); // I.amOnPage('/?u=create-account'); // I.checkA11y(); From ef34132ce7742be3aef1b8492eb6e728a0db3b41 Mon Sep 17 00:00:00 2001 From: Melissa 't Hart Date: Wed, 29 May 2024 17:57:57 +0200 Subject: [PATCH 08/13] fix(a11y): add epg test --- .../web/test-e2e/tests/accessibility_test.ts | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/platforms/web/test-e2e/tests/accessibility_test.ts b/platforms/web/test-e2e/tests/accessibility_test.ts index 5076696bc..1184e8518 100644 --- a/platforms/web/test-e2e/tests/accessibility_test.ts +++ b/platforms/web/test-e2e/tests/accessibility_test.ts @@ -5,6 +5,10 @@ Feature('Accessibility'); const disableRetryFailedStep = (test: { disableRetryFailedStep: boolean }): void => { test.disableRetryFailedStep = true; }; +function waitForEpgAnimation(I: CodeceptJS.I, sec: number = 1) { + I.waitForLoaderDone(); + return I.wait(sec); +} Scenario('WCAG compliant - Home Page', async ({ I }) => { I.useConfig(testConfigs.basicNoAuth); @@ -22,19 +26,29 @@ Scenario('WCAG compliant - Playlist Page', async ({ I }) => { await I.amOnPage('/p/dGSUzs9o/'); // "Films" page I.checkA11y(null, { ignore: [ - // { selector: 'html', id: 'document-title' }, - // { id: 'aria-valid-attr-value', selector: '[role="gridcell"]' }, + { id: 'document-title', selector: 'html' }, + { id: 'aria-valid-attr-value', selector: '[role="gridcell"]' }, ], }); }).config(disableRetryFailedStep); Scenario('WCAG compliant - Video Detail Inline Page', async ({ I }) => { - // Fails because of role="image" with aria-label I.useConfig(testConfigs.inlinePlayer); I.amOnPage('/m/awWEFyPu/big-buck-bunny'); I.checkA11y(); }).config(disableRetryFailedStep); +Scenario('WCAG compliant - Live Page (EPG)', async ({ I }) => { + I.useConfig(testConfigs.basicNoAuth); + I.amOnPage('/p/fWpLtzVh/?channel=Uh7zcqVm/'); + waitForEpgAnimation(I); + I.checkA11y(null, { + ignore: [ + { id: 'scrollable-region-focusable', selector: '[class^="css-"]' }, + ], + }); +}).config(disableRetryFailedStep); + // Account Pages Scenario('WCAG compliant - Account Page', async ({ I }) => { @@ -55,7 +69,6 @@ Scenario('WCAG compliant - Favorites Page', async ({ I }) => { I.checkA11y(); }).config(disableRetryFailedStep); - // Modals // Scenario('Signup - Modal - WCAG compliant', async ({ I }) => { From 5dd437afff96f9f685de90bfe25ccebea76437cf Mon Sep 17 00:00:00 2001 From: Melissa 't Hart Date: Thu, 30 May 2024 16:24:33 +0200 Subject: [PATCH 09/13] fix(a11y): add search tests and fix its heading hierarchy --- .../__snapshots__/CardGrid.test.tsx.snap | 8 +++---- .../__snapshots__/Favorites.test.tsx.snap | 8 +++---- .../Shelf/__snapshots__/Shelf.test.tsx.snap | 5 +++++ .../VideoListItem/VideoListItem.tsx | 2 +- .../src/pages/Search/Search.module.scss | 2 +- packages/ui-react/src/pages/Search/Search.tsx | 5 +++-- .../User/__snapshots__/User.test.tsx.snap | 8 +++---- .../web/test-e2e/tests/accessibility_test.ts | 21 +++++++++++++++++++ 8 files changed, 43 insertions(+), 16 deletions(-) diff --git a/packages/ui-react/src/components/CardGrid/__snapshots__/CardGrid.test.tsx.snap b/packages/ui-react/src/components/CardGrid/__snapshots__/CardGrid.test.tsx.snap index 7eb8d1a4d..888929dbd 100644 --- a/packages/ui-react/src/components/CardGrid/__snapshots__/CardGrid.test.tsx.snap +++ b/packages/ui-react/src/components/CardGrid/__snapshots__/CardGrid.test.tsx.snap @@ -9,12 +9,12 @@ exports[` > renders and matches snapshot 1`] = ` role="grid" >
> renders and matches snapshot 1`] = `
> renders and matches snapshot 1`] = `
> renders and matches snapshot 1`] = ` role="grid" >
> renders and matches snapshot 1`] = `
> renders and matches snapshot 1`] = `
Featured shelf 1`] = `
+
diff --git a/packages/ui-react/src/components/VideoListItem/VideoListItem.tsx b/packages/ui-react/src/components/VideoListItem/VideoListItem.tsx index 250eab4f3..1dd84c4cb 100644 --- a/packages/ui-react/src/components/VideoListItem/VideoListItem.tsx +++ b/packages/ui-react/src/components/VideoListItem/VideoListItem.tsx @@ -73,7 +73,7 @@ function VideoListItem({ onHover, progress, activeLabel, item, url, loading = fa setImageLoaded(true)} width={320} alt="" />
{isLocked && ( -
+
)} diff --git a/packages/ui-react/src/pages/Search/Search.module.scss b/packages/ui-react/src/pages/Search/Search.module.scss index 72722d6c0..e0bf25c5b 100644 --- a/packages/ui-react/src/pages/Search/Search.module.scss +++ b/packages/ui-react/src/pages/Search/Search.module.scss @@ -30,7 +30,7 @@ align-items: center; margin: 24px 0; - > h2 { + > h1 { font-family: var(--body-alt-font-family); font-weight: var(--body-font-weight-bold); font-size: 24px; diff --git a/packages/ui-react/src/pages/Search/Search.tsx b/packages/ui-react/src/pages/Search/Search.tsx index 30004fac1..776d44c40 100644 --- a/packages/ui-react/src/pages/Search/Search.tsx +++ b/packages/ui-react/src/pages/Search/Search.tsx @@ -109,9 +109,9 @@ const Search = () => {
-

+

{title} -

+
{ accessModel={accessModel} isLoggedIn={!!user} hasSubscription={!!subscription} + headingLevel={2} />
); diff --git a/packages/ui-react/src/pages/User/__snapshots__/User.test.tsx.snap b/packages/ui-react/src/pages/User/__snapshots__/User.test.tsx.snap index c6c0ec92b..a19b9af59 100644 --- a/packages/ui-react/src/pages/User/__snapshots__/User.test.tsx.snap +++ b/packages/ui-react/src/pages/User/__snapshots__/User.test.tsx.snap @@ -416,12 +416,12 @@ exports[`User Component tests > Favorites Page 1`] = ` role="grid" >
Favorites Page 1`] = `
Favorites Page 1`] = `
{ test.disableRetryFailedStep = true; }; + function waitForEpgAnimation(I: CodeceptJS.I, sec: number = 1) { I.waitForLoaderDone(); return I.wait(sec); @@ -38,12 +40,31 @@ Scenario('WCAG compliant - Video Detail Inline Page', async ({ I }) => { I.checkA11y(); }).config(disableRetryFailedStep); +Scenario('WCAG compliant - Search Page with results', async ({ I }) => { + I.useConfig(testConfigs.basicNoAuth); + I.amOnPage('/q/Caminandes'); + I.checkA11y(); +}).config(disableRetryFailedStep); + +Scenario('WCAG compliant - Search Page containing no results', async ({ I }) => { + I.useConfig(testConfigs.basicNoAuth); + I.amOnPage('/q/querywithoutresults'); + I.checkA11y(); +}).config(disableRetryFailedStep); + +Scenario('WCAG compliant - Player Page', async ({ I }) => { + I.useConfig(testConfigs.basicNoAuth); + I.amOnPage('/m/awWEFyPu/big-buck-bunny?r=dGSUzs9o&play=1'); + I.checkA11y(); +}).config(disableRetryFailedStep); + Scenario('WCAG compliant - Live Page (EPG)', async ({ I }) => { I.useConfig(testConfigs.basicNoAuth); I.amOnPage('/p/fWpLtzVh/?channel=Uh7zcqVm/'); waitForEpgAnimation(I); I.checkA11y(null, { ignore: [ + // It is a known issue that the EPG is not fully accessible due to the planby EPG package { id: 'scrollable-region-focusable', selector: '[class^="css-"]' }, ], }); From f5bed39129321b69bff21d6d3e2081449f8f69a5 Mon Sep 17 00:00:00 2001 From: Melissa 't Hart Date: Thu, 30 May 2024 17:51:49 +0200 Subject: [PATCH 10/13] fix(a11y): add hidden label and registration modal tests --- .../CustomRegisterField.test.tsx.snap | 12 ++++ .../PersonalDetailsForm.test.tsx.snap | 12 ++++ .../ui-react/src/components/Select/Select.tsx | 3 +- .../web/test-e2e/tests/accessibility_test.ts | 67 ++++++++++++++++--- 4 files changed, 82 insertions(+), 12 deletions(-) diff --git a/packages/ui-react/src/components/CustomRegisterField/__snapshots__/CustomRegisterField.test.tsx.snap b/packages/ui-react/src/components/CustomRegisterField/__snapshots__/CustomRegisterField.test.tsx.snap index 4e8facf32..4a61abda8 100644 --- a/packages/ui-react/src/components/CustomRegisterField/__snapshots__/CustomRegisterField.test.tsx.snap +++ b/packages/ui-react/src/components/CustomRegisterField/__snapshots__/CustomRegisterField.test.tsx.snap @@ -41,6 +41,10 @@ exports[` > renders and matches snapshot +