Skip to content

Commit

Permalink
Set up jest-axe for a11y testing (#2262)
Browse files Browse the repository at this point in the history
## Summary:
- Configure `jest-axe`
- Adds `toHaveNoA11yViolations` matcher for jest tests
- Configure jest await async lint rule for toHaveNoA11yViolations matcher. This is so we don't forget to await the a11y checks. Also fixing some linting issues that were caught when this rule was enabled
- Adds an example a11y test for Title component

Note: this set up is similar to how `jest-axe` is set up in webapp (I referenced this [jest-axe webapp PR](Khan/webapp#5850)!)

Issue: WB-466

## Test plan:
- Run unit tests and linting to make sure there are no errors
- Manually tested jest-axe is working by making sure that it catches expected a11y errors locally. Used this example with headings that are out of order:
```tsx
test("expect a11y test to fail", async () => {
  // Arrange
  // Act
  const {container} = render(
      <div>
        <h1>title1</h1>
        <h3>title2</h3>
      </div>,
  );

  // Assert
  await expect(container).toHaveNoA11yViolations();
});
```

Author: beaesguerra

Reviewers: beaesguerra, jeresig

Required Reviewers:

Approved By: jeresig

Checks: ✅ codecov/project, ✅ Lint (ubuntu-latest, 20.x), ✅ Test (ubuntu-latest, 20.x, 2/2), ✅ Check build sizes (ubuntu-latest, 20.x), ✅ Test (ubuntu-latest, 20.x, 1/2), ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ✅ gerald, ✅ Chromatic - Get results on regular PRs (ubuntu-latest, 20.x), ✅ Test (ubuntu-latest, 20.x, 2/2), ✅ Lint (ubuntu-latest, 20.x), ✅ Check build sizes (ubuntu-latest, 20.x), ✅ Test (ubuntu-latest, 20.x, 1/2), ✅ Chromatic - Build on regular PRs / chromatic (ubuntu-latest, 20.x), ⏭️  Chromatic - Skip on Release PR (changesets), 🚫 Chromatic - Get results on regular PRs, ✅ Test (ubuntu-latest, 20.x, 2/2), ✅ Test (ubuntu-latest, 20.x, 1/2), ✅ Check build sizes (ubuntu-latest, 20.x), ✅ Lint (ubuntu-latest, 20.x), ⏭️  Publish npm snapshot, ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ✅ gerald, 🚫 Chromatic - Build on regular PRs / chromatic (ubuntu-latest, 20.x), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ⏭️  Chromatic - Skip on Release PR (changesets), ⏭️  Publish npm snapshot, ⌛ undefined

Pull Request URL: #2262
  • Loading branch information
beaesguerra authored Jun 28, 2024
1 parent dcafa86 commit 69c6b37
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 17 deletions.
2 changes: 2 additions & 0 deletions .changeset/rotten-bats-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
8 changes: 8 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,14 @@ module.exports = {
"testing-library/await-async-utils": "off",
"testing-library/await-async-query": "off",

// @khanacademy
"@khanacademy/jest-await-async-matchers": [
"error",
{
matchers: ["toHaveNoA11yViolations"],
},
],

/**
* TypeScript rules
*/
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/node-ci-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
with:
changed-files: ${{ steps.changed.outputs.files }}
files: packages/ # Only look for changes in packages
globs: "!(**/__tests__/**), !(**/dist/*)" # Ignore test files
globs: "!(**/__tests__/**), !(**/dist/*), !(**/*.test.ts)" # Ignore test files
matchAllGlobs: true # All globs must match (disjunction is the default)
conjunctive: true # Only return files that match all of the above

Expand Down
28 changes: 28 additions & 0 deletions config/jest/matchers/to-have-no-a11y-violations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// custom matcher for the jest-axe testing library. This uses the axe testing
// engine to flag a11y violations
// https://github.com/nickcolley/jest-axe
import {configureAxe, toHaveNoViolations} from "jest-axe";

const axe = configureAxe({
globalOptions: {
rules: [
// This rule is that all content must be inside a landmark region.
// Since our components might have a region outside of the
// component boundary, it doesn't make sense to enable here.
// If we were to apply this testing in a whole page context we
// should enable in that context
{id: "region", enabled: false},
{
id: "meta-viewport",
enabled: false,
},
],
},
});

expect.extend({
async toHaveNoA11yViolations(received: HTMLElement) {
const result = await axe(received); // note this is the axe we configured above
return toHaveNoViolations.toHaveNoViolations(result);
},
});
1 change: 1 addition & 0 deletions config/jest/test.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module.exports = {
"@testing-library/jest-dom/extend-expect",
"<rootDir>/config/jest/test-setup.js",
"jest-extended/all",
"<rootDir>/config/jest/matchers/to-have-no-a11y-violations.ts",
],
moduleNameMapper: {
"^@khanacademy/wonder-blocks-(.*)$":
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"@testing-library/react-hooks": "^7.0.2",
"@testing-library/user-event": "^14.5.1",
"@types/jest": "29",
"@types/jest-axe": "^3.5.9",
"@types/jscodeshift": "^0.11.11",
"@types/node": "^18.14.1",
"@types/node-fetch": "^2.6.11",
Expand Down Expand Up @@ -99,6 +100,7 @@
"eslint-watch": "^8.0.0",
"fast-glob": "^3.2.12",
"jest": "^29.5.0",
"jest-axe": "^9.0.0",
"jest-date-mock": "^1.0.8",
"jest-environment-jsdom": "^29.0.2",
"jest-extended": "^3.2.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/wonder-blocks-layout/src/util/test-util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe("Test utils", () => {
await promise;

// Assert
expect(promise).resolves.toHaveProperty("type", "resize");
await expect(promise).resolves.toHaveProperty("type", "resize");
window.removeEventListener("resize", handler);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ describe("RespondWith", () => {
const act = Promise.race([settleableResponse, otherResponse]);

// Assert
await expect(act).resolves;
await expect(act).toResolve();
});

it("should settle if the signal is raised", async () => {
Expand Down Expand Up @@ -305,7 +305,7 @@ describe("RespondWith", () => {
const act = Promise.race([settleableResponse, otherResponse]);

// Assert
await expect(act).resolves;
await expect(act).toResolve();
});

it("should settle if the signal is raised", async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,15 @@ describe("Title", () => {
// Assert
expect(ref.current).toBeInstanceOf(HTMLHeadingElement);
});

describe("a11y", () => {
test("has no accessibility violations", async () => {
// Arrange
// Act
const {container} = render(<Title>Test title</Title>);

// Assert
await expect(container).toHaveNoA11yViolations();
});
});
});
4 changes: 4 additions & 0 deletions types/matchers.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@
declare namespace jest {
interface Matchers<R> {
toBeFunction(): R;
/*
* From: config/jest/matchers/to-have-no-a11y-violations.ts
*/
toHaveNoA11yViolations(): Promise<R>;
}
}
104 changes: 91 additions & 13 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4535,6 +4535,14 @@
dependencies:
"@types/istanbul-lib-report" "*"

"@types/jest-axe@^3.5.9":
version "3.5.9"
resolved "https://registry.yarnpkg.com/@types/jest-axe/-/jest-axe-3.5.9.tgz#97b1317371a48707ca93825d4c990b0d07690d99"
integrity sha512-z98CzR0yVDalCEuhGXXO4/zN4HHuSebAukXDjTLJyjEAgoUf1H1i+sr7SUB/mz8CRS/03/XChsx0dcLjHkndoQ==
dependencies:
"@types/jest" "*"
axe-core "^3.5.5"

"@types/jest@*":
version "29.5.12"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544"
Expand Down Expand Up @@ -5462,6 +5470,16 @@ available-typed-arrays@^1.0.5:
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==

[email protected]:
version "4.9.1"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.9.1.tgz#fcd0f4496dad09e0c899b44f6c4bb7848da912ae"
integrity sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==

axe-core@^3.5.5:
version "3.5.6"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.6.tgz#e762a90d7f6dbd244ceacb4e72760ff8aad521b5"
integrity sha512-LEUDjgmdJoA3LqklSTwKYqkjcZ4HKc4ddIYGSAiSkr46NTjzg2L9RNB+lekO9P7Dlpa87+hBtzc2Fzn/+GUWMQ==

axe-core@^4.2.0, axe-core@^4.6.2:
version "4.7.0"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf"
Expand Down Expand Up @@ -5943,6 +5961,14 @@ chai@^4.3.10, chai@^4.3.7:
pathval "^1.1.1"
type-detect "^4.0.8"

[email protected], chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"

chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
Expand All @@ -5960,14 +5986,6 @@ chalk@^3.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"

chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"

chalk@^5.0.1:
version "5.2.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3"
Expand Down Expand Up @@ -9144,6 +9162,16 @@ jake@^10.8.5:
filelist "^1.0.4"
minimatch "^3.1.2"

jest-axe@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/jest-axe/-/jest-axe-9.0.0.tgz#9794dc0427f9f2c6a5224d30acece6183cefb2f2"
integrity sha512-Xt7O0+wIpW31lv0SO1wQZUTyJE7DEmnDEZeTt9/S9L5WUywxrv8BrgvTuQEqujtfaQOcJ70p4wg7UUgK1E2F5g==
dependencies:
axe-core "4.9.1"
chalk "4.1.2"
jest-matcher-utils "29.2.2"
lodash.merge "4.6.2"

jest-changed-files@^29.5.0:
version "29.5.0"
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e"
Expand Down Expand Up @@ -9239,6 +9267,16 @@ jest-diff@^29.0.0, jest-diff@^29.5.0:
jest-get-type "^29.4.3"
pretty-format "^29.5.0"

jest-diff@^29.2.1:
version "29.7.0"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a"
integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==
dependencies:
chalk "^4.0.0"
diff-sequences "^29.6.3"
jest-get-type "^29.6.3"
pretty-format "^29.7.0"

jest-docblock@^29.4.3:
version "29.4.3"
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8"
Expand Down Expand Up @@ -9296,6 +9334,11 @@ jest-get-type@^29.0.0, jest-get-type@^29.4.3:
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5"
integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==

jest-get-type@^29.2.0, jest-get-type@^29.6.3:
version "29.6.3"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1"
integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==

jest-haste-map@^29.5.0:
version "29.5.0"
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de"
Expand Down Expand Up @@ -9323,6 +9366,16 @@ jest-leak-detector@^29.5.0:
jest-get-type "^29.4.3"
pretty-format "^29.5.0"

[email protected]:
version "29.2.2"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.2.2.tgz#9202f8e8d3a54733266784ce7763e9a08688269c"
integrity sha512-4DkJ1sDPT+UX2MR7Y3od6KtvRi9Im1ZGLGgdLFLm4lPexbTaCgJW5NN3IOXlQHF7NSHY/VHhflQ+WoKtD/vyCw==
dependencies:
chalk "^4.0.0"
jest-diff "^29.2.1"
jest-get-type "^29.2.0"
pretty-format "^29.2.1"

jest-matcher-utils@^29.5.0:
version "29.5.0"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5"
Expand Down Expand Up @@ -9934,7 +9987,7 @@ lodash.kebabcase@^4.1.1:
resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY=

lodash.merge@^4.6.2:
lodash.merge@4.6.2, lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
Expand Down Expand Up @@ -12156,7 +12209,7 @@ pretty-format@^29.0.0, pretty-format@^29.5.0:
ansi-styles "^5.0.0"
react-is "^18.0.0"

pretty-format@^29.7.0:
pretty-format@^29.2.1, pretty-format@^29.7.0:
version "29.7.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"
integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==
Expand Down Expand Up @@ -13506,7 +13559,16 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"

"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==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

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==
Expand Down Expand Up @@ -13606,7 +13668,14 @@ stringify-entities@^4.0.0:
character-entities-html4 "^2.0.0"
character-entities-legacy "^3.0.0"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1", [email protected], strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.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==
dependencies:
ansi-regex "^5.0.1"

[email protected], strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand Down Expand Up @@ -14796,7 +14865,7 @@ wordwrap@^1.0.0, wordwrap@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"

"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==
Expand All @@ -14814,6 +14883,15 @@ wrap-ansi@^6.2.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==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
Expand Down

0 comments on commit 69c6b37

Please sign in to comment.