From db5e3da8f03a7842338e290e28c05fd300a78f09 Mon Sep 17 00:00:00 2001 From: Nitin Awari Date: Sat, 18 Jan 2025 22:37:47 +0530 Subject: [PATCH] added acccesibility workflow and packages --- .github/workflows/ci-cd.yaml | 17 ++ frontend/eslint.config.js | 18 +++ frontend/jest.setup.ts | 1 + frontend/package-lock.json | 291 +++++++++++++++++++++++++++++++++-- frontend/package.json | 8 +- frontend/src/main.tsx | 8 +- 6 files changed, 325 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index c513ed23d..1d07a45d0 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -68,6 +68,23 @@ jobs: working-directory: frontend run: npm run lint:check + - name: Run accessibility tests + working-directory: frontend + run: | + npm run test -- -t "accessibility" + + - name: Generate accessibility report + working-directory: frontend + run: | + npm run test:a11y + continue-on-error: true + + - name: Upload accessibility report + uses: actions/upload-artifact@v4 + with: + name: accessibility-report + path: frontend/accessibility-report.json + - name: Check for uncommitted changes run: | git diff --exit-code || (echo 'Unstaged changes detected. \ diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index 1077e3ce0..80fa8acea 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -7,6 +7,7 @@ import typescriptParser from '@typescript-eslint/parser' import prettierConfig from 'eslint-config-prettier' import importPlugin from 'eslint-plugin-import' import jest from 'eslint-plugin-jest' +import jsxA11y from 'eslint-plugin-jsx-a11y' import prettier from 'eslint-plugin-prettier' import react from 'eslint-plugin-react' import reactHooks from 'eslint-plugin-react-hooks' @@ -41,6 +42,7 @@ export default [ jest, prettier, react, + 'jsx-a11y': jsxA11y, }, settings: { 'import/resolver': { @@ -59,6 +61,22 @@ export default [ rules: { ...jest.configs.recommended.rules, ...prettierConfig.rules, + 'jsx-a11y/alt-text': 'error', + 'jsx-a11y/anchor-has-content': 'error', + 'jsx-a11y/anchor-is-valid': 'error', + 'jsx-a11y/aria-props': 'error', + 'jsx-a11y/aria-proptypes': 'error', + 'jsx-a11y/aria-unsupported-elements': 'error', + 'jsx-a11y/role-has-required-aria-props': 'error', + 'jsx-a11y/role-supports-aria-props': 'error', + 'jsx-a11y/tabindex-no-positive': 'error', + 'jsx-a11y/no-redundant-roles': 'error', + 'jsx-a11y/label-has-associated-control': 'error', + 'jsx-a11y/no-autofocus': 'warn', + 'jsx-a11y/click-events-have-key-events': 'error', + 'jsx-a11y/no-noninteractive-element-interactions': 'error', + 'jsx-a11y/no-static-element-interactions': 'error', + 'jsx-a11y/no-noninteractive-tabindex': 'error', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-explicit-any': 'warn', diff --git a/frontend/jest.setup.ts b/frontend/jest.setup.ts index 928fcbe07..fa771d6d2 100644 --- a/frontend/jest.setup.ts +++ b/frontend/jest.setup.ts @@ -9,6 +9,7 @@ global.React = React global.TextEncoder = TextEncoder beforeEach(() => { + window.scrollTo = jest.fn() jest.spyOn(console, 'error').mockImplementation((...args) => { throw new Error(`Console error: ${args.join(' ')}`) }) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 41ab04365..c2c05b4f8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -46,12 +46,14 @@ "tailwindcss-animate": "^1.0.7" }, "devDependencies": { + "@axe-core/react": "^4.10.1", "@eslint/js": "^9.15.0", "@swc/core": "^1.10.7", "@swc/jest": "^0.2.37", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", "@types/jest": "^29.5.14", + "@types/jest-axe": "^3.5.9", "@types/mocha": "^10.0.10", "@types/node": "^22.10.7", "@types/react": "^19.0.6", @@ -67,6 +69,7 @@ "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "^28.9.0", + "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.1.0", @@ -74,6 +77,7 @@ "globals": "^15.14.0", "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", + "jest-axe": "^9.0.0", "jest-environment-jsdom": "^29.7.0", "open": "^10.1.0", "postcss": "^8.4.47", @@ -389,6 +393,17 @@ "node": ">=6.0.0" } }, + "node_modules/@axe-core/react": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@axe-core/react/-/react-4.10.1.tgz", + "integrity": "sha512-GRGx/3Gbce9WQYXgY0iZzqOaHIRXBNGyV+zoUhJzBuDeoTL+XoeY3u/9PzdIVyH0pbNs1n1RChfsmWBy6hzKPg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "axe-core": "~4.10.2", + "requestidlecallback": "^0.3.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -1754,12 +1769,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -3634,6 +3643,27 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/jest-axe": { + "version": "3.5.9", + "resolved": "https://registry.npmjs.org/@types/jest-axe/-/jest-axe-3.5.9.tgz", + "integrity": "sha512-z98CzR0yVDalCEuhGXXO4/zN4HHuSebAukXDjTLJyjEAgoUf1H1i+sr7SUB/mz8CRS/03/XChsx0dcLjHkndoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jest": "*", + "axe-core": "^3.5.5" + } + }, + "node_modules/@types/jest-axe/node_modules/axe-core": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-3.5.6.tgz", + "integrity": "sha512-LEUDjgmdJoA3LqklSTwKYqkjcZ4HKc4ddIYGSAiSkr46NTjzg2L9RNB+lekO9P7Dlpa87+hBtzc2Fzn/+GUWMQ==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, "node_modules/@types/jest/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", @@ -4355,6 +4385,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -4422,6 +4459,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axe-core": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", + "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, "node_modules/axios": { "version": "1.7.9", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", @@ -4433,6 +4480,16 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -5083,6 +5140,13 @@ "devOptional": true, "license": "MIT" }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -5467,9 +5531,9 @@ } }, "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "license": "MIT" }, "node_modules/entities": { @@ -5980,6 +6044,70 @@ } } }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-prettier": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.2.tgz", @@ -7838,6 +7966,83 @@ } } }, + "node_modules/jest-axe": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jest-axe/-/jest-axe-9.0.0.tgz", + "integrity": "sha512-Xt7O0+wIpW31lv0SO1wQZUTyJE7DEmnDEZeTt9/S9L5WUywxrv8BrgvTuQEqujtfaQOcJ70p4wg7UUgK1E2F5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "axe-core": "4.9.1", + "chalk": "4.1.2", + "jest-matcher-utils": "29.2.2", + "lodash.merge": "4.6.2" + }, + "engines": { + "node": ">= 16.0.0" + } + }, + "node_modules/jest-axe/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-axe/node_modules/axe-core": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.9.1.tgz", + "integrity": "sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-axe/node_modules/jest-matcher-utils": { + "version": "29.2.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.2.2.tgz", + "integrity": "sha512-4DkJ1sDPT+UX2MR7Y3od6KtvRi9Im1ZGLGgdLFLm4lPexbTaCgJW5NN3IOXlQHF7NSHY/VHhflQ+WoKtD/vyCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.2.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.2.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-axe/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-axe/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/jest-changed-files": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", @@ -8921,6 +9126,26 @@ "node": ">=6" } }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/leaflet": { "version": "1.9.4", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", @@ -10302,9 +10527,9 @@ } }, "node_modules/react-router": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.2.tgz", - "integrity": "sha512-KeallSO30KLpIe/ZZqfk6pCJ1c+5JhMxl3SCS3Zx1LgaGuQbgLDmjuNi6KZ5LnAV9sWjbmBWGRw8Um/Pw6BExg==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.3.tgz", + "integrity": "sha512-EezYymLY6Guk/zLQ2vRA8WvdUhWFEj5fcE3RfWihhxXBW7+cd1LsIiA3lmx+KCmneAGQuyBv820o44L2+TtkSA==", "license": "MIT", "dependencies": { "@types/cookie": "^0.6.0", @@ -10326,12 +10551,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.2.tgz", - "integrity": "sha512-kE7JdrDfeWO/2d6RPucLmqp2UL8Isv1VWtI5MQyYNA99KtncqxWDL6550+0rH4fboJKJbXRcyjRnIRT/gkxTcA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.3.tgz", + "integrity": "sha512-qQGTE+77hleBzv9SIUIkGRvuFBQGagW+TQKy53UTZAO/3+YFNBYvRsNIZ1GT17yHbc63FylMOdS+m3oUriF1GA==", "license": "MIT", "dependencies": { - "react-router": "7.1.2" + "react-router": "7.1.3" }, "engines": { "node": ">=20.0.0" @@ -10440,6 +10665,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/requestidlecallback": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/requestidlecallback/-/requestidlecallback-0.3.0.tgz", + "integrity": "sha512-TWHFkT7S9p7IxLC5A1hYmAYQx2Eb9w1skrXmQ+dS1URyvR8tenMLl4lHbqEOUnpEYxNKpkVMXUgknVpBZWXXfQ==", + "dev": true, + "license": "MIT" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -10981,6 +11213,33 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", diff --git a/frontend/package.json b/frontend/package.json index 98fb77fb5..f84e358a4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,7 +15,9 @@ "preview": "vite preview", "test": "tsc --noEmit && NODE_OPTIONS=--no-warnings=DEP0040 jest", "test:coverage": "jest --coverage && chmod +x scripts/open-coverage.sh && scripts/open-coverage.sh", - "watch": "jest --watch" + "watch": "jest --watch", + "test:a11y": "jest -c jest.config.ts --testPathPattern=__tests__/src/accessibility.test.tsx", + "test:a11y:watch": "jest --config jest.config.ts --testMatch='**/*.test.{ts,tsx}' --watch" }, "dependencies": { "@algolia/autocomplete-js": "^1.17.9", @@ -56,12 +58,14 @@ "tailwindcss-animate": "^1.0.7" }, "devDependencies": { + "@axe-core/react": "^4.10.1", "@eslint/js": "^9.15.0", "@swc/core": "^1.10.7", "@swc/jest": "^0.2.37", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", "@types/jest": "^29.5.14", + "@types/jest-axe": "^3.5.9", "@types/mocha": "^10.0.10", "@types/node": "^22.10.7", "@types/react": "^19.0.6", @@ -77,6 +81,7 @@ "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "^28.9.0", + "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.1.0", @@ -84,6 +89,7 @@ "globals": "^15.14.0", "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", + "jest-axe": "^9.0.0", "jest-environment-jsdom": "^29.7.0", "open": "^10.1.0", "postcss": "^8.4.47", diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 12b369f78..ed08e2575 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,4 +1,6 @@ -import { StrictMode } from 'react' +import axe from '@axe-core/react' +import React, { StrictMode } from 'react' +import ReactDOM from 'react-dom' import { createRoot } from 'react-dom/client' import './index.css' import TagManager from 'react-gtm-module' @@ -16,6 +18,10 @@ const tagManagerArgs = { TagManager.initialize(tagManagerArgs) +if (process.env.NODE_ENV !== 'development') { + axe(React, ReactDOM, 1000) +} + createRoot(document.getElementById('root')!).render(