From 91e78ad214eb2e107521f6f89913d4634582429c Mon Sep 17 00:00:00 2001 From: Leesa Ward Date: Sat, 14 Sep 2024 10:54:21 +1000 Subject: [PATCH 1/3] Install and implement Jest-Axe for accessibility testing in unit tests (#18) --- jest.setup.ts | 3 + jest.utils.tsx | 1 + package-lock.json | 144 ++++++++++++++++++ package.json | 2 + src/components/Alert/Alert.test.tsx | 20 +++ src/components/Button/Button.test.tsx | 30 ++++ .../ImageSlider/ImageSlider.test.tsx | 10 ++ src/components/Label/Label.test.tsx | 20 +++ src/components/LinkButton/LinkButton.test.tsx | 30 ++++ .../TruncatedText/TruncatedText.test.tsx | 8 + src/types.ts | 1 + templates/TemplateName.test.tsx | 10 +- 12 files changed, 278 insertions(+), 1 deletion(-) diff --git a/jest.setup.ts b/jest.setup.ts index 2bda4ca..6c7a1b9 100644 --- a/jest.setup.ts +++ b/jest.setup.ts @@ -1,6 +1,9 @@ import '@testing-library/jest-dom'; +import { toHaveNoViolations } from 'jest-axe'; jest.mock('./src/providers/RedbackUiThemeProvider/GlobalStyle.tsx', () => ({ GlobalStyle: () => null })); +// Ref: https://github.com/NickColley/jest-axe?tab=readme-ov-file#testing-react-with-react-testing-library +expect.extend(toHaveNoViolations); \ No newline at end of file diff --git a/jest.utils.tsx b/jest.utils.tsx index ff69b5f..56793b4 100644 --- a/jest.utils.tsx +++ b/jest.utils.tsx @@ -20,3 +20,4 @@ export const renderWithDeps = (component: ReactNode) => { ); }; + diff --git a/package-lock.json b/package-lock.json index 49e805c..6c54475 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "@testing-library/jest-dom": "^6.4.2", "@testing-library/react": "^14.2.2", "@types/jest": "^29.5.12", + "@types/jest-axe": "^3.5.9", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", "@typescript-eslint/eslint-plugin": "^7.2.0", @@ -49,6 +50,7 @@ "eslint-plugin-react-refresh": "^0.4.6", "eslint-plugin-storybook": "^0.8.0", "jest": "^29.7.0", + "jest-axe": "^9.0.0", "jest-environment-jsdom": "^29.7.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -5047,6 +5049,25 @@ "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, + "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, + "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", @@ -9886,6 +9907,129 @@ } } }, + "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, + "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": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "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, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-axe/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-axe/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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, + "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, + "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/pretty-format/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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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 + }, + "node_modules/jest-axe/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-changed-files": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", diff --git a/package.json b/package.json index baf771b..16a1fec 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "@testing-library/jest-dom": "^6.4.2", "@testing-library/react": "^14.2.2", "@types/jest": "^29.5.12", + "@types/jest-axe": "^3.5.9", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", "@typescript-eslint/eslint-plugin": "^7.2.0", @@ -63,6 +64,7 @@ "eslint-plugin-react-refresh": "^0.4.6", "eslint-plugin-storybook": "^0.8.0", "jest": "^29.7.0", + "jest-axe": "^9.0.0", "jest-environment-jsdom": "^29.7.0", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/components/Alert/Alert.test.tsx b/src/components/Alert/Alert.test.tsx index f5630fb..02868d9 100644 --- a/src/components/Alert/Alert.test.tsx +++ b/src/components/Alert/Alert.test.tsx @@ -1,6 +1,7 @@ import { screen } from '@testing-library/react'; import { renderWithDeps } from '../../../jest.utils'; import { Alert } from './Alert'; +import { axe } from 'jest-axe'; describe('', () => { it('renders', () => { @@ -8,4 +9,23 @@ describe('', () => { expect( screen.getByText('Example alert')).toBeInTheDocument(); }); + + it('has no accessibility violations', async () => { + const { container } = renderWithDeps(Example alert); + const results = await axe(container); + + expect(results).toHaveNoViolations(); + }); + + describe('default theme accessibility', () => { + ['success', 'info', 'warning', 'error'].forEach(type => { + it(`has no accessibility violations for type "${type}" in default theme`, async () => { + // @ts-expect-error TS2322: Type string is not assignable to type 'success' | 'info' | 'warning' | 'error' | undefined + const { container } = renderWithDeps(Example alert); + const results = await axe(container); + + expect(results).toHaveNoViolations(); + }); + }); + }); }); diff --git a/src/components/Button/Button.test.tsx b/src/components/Button/Button.test.tsx index cabf915..219bbdf 100644 --- a/src/components/Button/Button.test.tsx +++ b/src/components/Button/Button.test.tsx @@ -1,6 +1,9 @@ import { screen, fireEvent } from '@testing-library/react'; import { renderWithDeps } from '../../../jest.utils'; import { Button } from './Button'; +import { axe } from 'jest-axe'; +import { themes } from '../../themes'; +import { ThemeColor } from '../../types'; const mockClick = jest.fn(); @@ -11,6 +14,33 @@ describe('