From 2bd0b2d5005f6ce5d499e3b839125d2dcd315caf Mon Sep 17 00:00:00 2001 From: Milo Fultz Date: Fri, 15 Dec 2023 05:32:47 +1100 Subject: [PATCH 01/18] Add vitest axe --- package-lock.json | 66 +++++++++++++++++++++++++++++++++++++- package.json | 3 +- test/helpers/test-setup.js | 4 +++ vitest.config.js | 26 +++++++++------ 4 files changed, 87 insertions(+), 12 deletions(-) create mode 100644 test/helpers/test-setup.js diff --git a/package-lock.json b/package-lock.json index b0b2f7323..2bfc7d70d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,7 +67,8 @@ "rollup-plugin-visualizer": "^5.9.0", "string_decoder": "^1.3.0", "vite": "^4.5.0", - "vitest": "^0.34.6" + "vitest": "^0.34.6", + "vitest-axe": "^0.1.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -10457,6 +10458,12 @@ "version": "4.17.21", "license": "MIT" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "license": "MIT" @@ -13939,6 +13946,35 @@ } } }, + "node_modules/vitest-axe": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/vitest-axe/-/vitest-axe-0.1.0.tgz", + "integrity": "sha512-jvtXxeQPg8R/2ANTY8QicA5pvvdRP4F0FsVUAHANJ46YCDASie/cuhlSzu0DGcLmZvGBSBNsNuK3HqfaeknyvA==", + "dev": true, + "dependencies": { + "aria-query": "^5.0.0", + "axe-core": "^4.4.2", + "chalk": "^5.0.1", + "dom-accessibility-api": "^0.5.14", + "lodash-es": "^4.17.21", + "redent": "^3.0.0" + }, + "peerDependencies": { + "vitest": ">=0.16.0" + } + }, + "node_modules/vitest-axe/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/vitest/node_modules/acorn": { "version": "8.11.2", "dev": true, @@ -21695,6 +21731,12 @@ "lodash": { "version": "4.17.21" }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true + }, "lodash.clonedeep": { "version": "4.5.0" }, @@ -23867,6 +23909,28 @@ } } }, + "vitest-axe": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/vitest-axe/-/vitest-axe-0.1.0.tgz", + "integrity": "sha512-jvtXxeQPg8R/2ANTY8QicA5pvvdRP4F0FsVUAHANJ46YCDASie/cuhlSzu0DGcLmZvGBSBNsNuK3HqfaeknyvA==", + "dev": true, + "requires": { + "aria-query": "^5.0.0", + "axe-core": "^4.4.2", + "chalk": "^5.0.1", + "dom-accessibility-api": "^0.5.14", + "lodash-es": "^4.17.21", + "redent": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + } + } + }, "w3c-xmlserializer": { "version": "4.0.0", "dev": true, diff --git a/package.json b/package.json index 9942e12c4..aee2055e0 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,8 @@ "rollup-plugin-visualizer": "^5.9.0", "string_decoder": "^1.3.0", "vite": "^4.5.0", - "vitest": "^0.34.6" + "vitest": "^0.34.6", + "vitest-axe": "^0.1.0" }, "lint-staged": { "*.{js,css,ts,tsx,jsx}": [ diff --git a/test/helpers/test-setup.js b/test/helpers/test-setup.js new file mode 100644 index 000000000..7a94cff0f --- /dev/null +++ b/test/helpers/test-setup.js @@ -0,0 +1,4 @@ +import * as matchers from 'vitest-axe/matchers'; +import { expect } from 'vitest'; + +expect.extend(matchers); diff --git a/vitest.config.js b/vitest.config.js index 6163fb135..fe9399b7a 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -1,12 +1,18 @@ // vitest.config.ts -import {mergeConfig} from 'vite' -import {defineConfig} from 'vitest/config' -import viteConfig from './vite.config' +/* eslint-disable import/no-extraneous-dependencies */ +import { mergeConfig } from 'vite'; +import { defineConfig } from 'vitest/config'; +/* eslint-enable import/no-extraneous-dependencies */ +import viteConfig from './vite.config'; -export default mergeConfig(viteConfig, defineConfig({ - test: { - globals: true, - environment: 'jsdom', - globalSetup: './test/helpers/test-globals.js' - }, -})) \ No newline at end of file +export default mergeConfig( + viteConfig, + defineConfig({ + test: { + globals: true, + environment: 'jsdom', + setupFiles: './test/helpers/test-setup.js', + globalSetup: './test/helpers/test-globals.js' + } + }) +); From 7ad9490a620a468db20eba28818b6988df03fa80 Mon Sep 17 00:00:00 2001 From: Milo Fultz Date: Fri, 15 Dec 2023 05:45:35 +1100 Subject: [PATCH 02/18] Make axe test util --- test/utils/axe.js | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 test/utils/axe.js diff --git a/test/utils/axe.js b/test/utils/axe.js new file mode 100644 index 000000000..43c992715 --- /dev/null +++ b/test/utils/axe.js @@ -0,0 +1,10 @@ +import { axe } from 'vitest-axe'; +import { render } from '@testing-library/react'; +import { expect } from 'vitest'; + +const isAccessible = async (jsx) => { + const { container } = render(jsx); + expect(await axe(container)).toHaveNoViolations(); +}; + +export default isAccessible; From 7343e26d1b91dde93cec2f0322c5f0c528eaeb7a Mon Sep 17 00:00:00 2001 From: Milo Fultz Date: Fri, 15 Dec 2023 05:46:10 +1100 Subject: [PATCH 03/18] Add basic test --- test/components/CivicProfileForms/BasicInfo.test.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/components/CivicProfileForms/BasicInfo.test.jsx b/test/components/CivicProfileForms/BasicInfo.test.jsx index bc70244c7..77a602b2c 100644 --- a/test/components/CivicProfileForms/BasicInfo.test.jsx +++ b/test/components/CivicProfileForms/BasicInfo.test.jsx @@ -2,10 +2,15 @@ import React from 'react'; import { render } from '@testing-library/react'; import { expect, it, describe } from 'vitest'; import { BasicInfo } from '@components/CivicProfileForms'; +import isAccessible from '../../utils/axe'; describe('Basic Info Form', () => { it('renders', () => { const { getByText } = render(); expect(getByText('Basic Info')).not.toBeNull(); }); + + it('should be accessible', async () => { + isAccessible(); + }); }); From d86b5a7cadc7c2c49c0fa495451c3e15cc7ee155 Mon Sep 17 00:00:00 2001 From: Milo Fultz Date: Fri, 15 Dec 2023 06:04:16 +1100 Subject: [PATCH 04/18] Add CivicProfileForms --- test/components/CivicProfileForms/BasicInfo.test.jsx | 2 +- test/components/CivicProfileForms/FinancialInfo.test.jsx | 5 +++++ test/components/CivicProfileForms/FormLayout.test.jsx | 5 +++++ test/components/CivicProfileForms/HousingInfo.test.jsx | 6 ++++++ test/utils/axe.js | 6 ++---- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/test/components/CivicProfileForms/BasicInfo.test.jsx b/test/components/CivicProfileForms/BasicInfo.test.jsx index 77a602b2c..64c650e94 100644 --- a/test/components/CivicProfileForms/BasicInfo.test.jsx +++ b/test/components/CivicProfileForms/BasicInfo.test.jsx @@ -11,6 +11,6 @@ describe('Basic Info Form', () => { }); it('should be accessible', async () => { - isAccessible(); + isAccessible(render()); }); }); diff --git a/test/components/CivicProfileForms/FinancialInfo.test.jsx b/test/components/CivicProfileForms/FinancialInfo.test.jsx index a4adf2e4d..0419b6e07 100644 --- a/test/components/CivicProfileForms/FinancialInfo.test.jsx +++ b/test/components/CivicProfileForms/FinancialInfo.test.jsx @@ -2,10 +2,15 @@ import React from 'react'; import { render } from '@testing-library/react'; import { expect, it, describe } from 'vitest'; import { FinancialInfo } from '@components/CivicProfileForms'; +import isAccessible from '../../utils/axe'; describe('Financial Info Form', () => { it('renders', () => { const { getByText } = render(); expect(getByText('Financial Info')).not.toBeNull(); }); + + it('should be accessible', async () => { + isAccessible(render()); + }); }); diff --git a/test/components/CivicProfileForms/FormLayout.test.jsx b/test/components/CivicProfileForms/FormLayout.test.jsx index 269072529..cfbf0b588 100644 --- a/test/components/CivicProfileForms/FormLayout.test.jsx +++ b/test/components/CivicProfileForms/FormLayout.test.jsx @@ -3,6 +3,7 @@ import { render } from '@testing-library/react'; import { MemoryRouter as Router } from 'react-router-dom'; import { expect, it, describe } from 'vitest'; import { FormLayout, CIVIC_FORM_LIST } from '@components/CivicProfileForms'; +import isAccessible from '../../utils/axe'; const renderLayout = (route) => render( @@ -37,3 +38,7 @@ describe('Civic Form Layout', () => { expect(queryAllByRole('link')[0].getAttribute('href')).toBe(`/${prevRoute.path}`); }); }); + +it('should be accessible', () => { + isAccessible(renderLayout(CIVIC_FORM_LIST[0].path)); +}); diff --git a/test/components/CivicProfileForms/HousingInfo.test.jsx b/test/components/CivicProfileForms/HousingInfo.test.jsx index 6bbf85adb..845844d26 100644 --- a/test/components/CivicProfileForms/HousingInfo.test.jsx +++ b/test/components/CivicProfileForms/HousingInfo.test.jsx @@ -4,6 +4,7 @@ import { vi, expect, it, describe } from 'vitest'; import { HousingInfo } from '@components/CivicProfileForms'; import { useCivicProfile } from '@hooks'; import userEvent from '@testing-library/user-event'; +import isAccessible from '../../utils/axe'; vi.mock('@hooks', async () => { const actual = await vi.importActual('@hooks'); @@ -22,6 +23,11 @@ describe('Housing info form', () => { expect(cityField).not.toBeNull(); }); + it('should be accessible', () => { + useCivicProfile.mockReturnValue({ data: {}, isSuccess: true }); + isAccessible(render()); + }); + it('submits an address update when you click the submit button', async () => { const user = userEvent.setup(); const mockAdd = vi.fn(); diff --git a/test/utils/axe.js b/test/utils/axe.js index 43c992715..2b0628931 100644 --- a/test/utils/axe.js +++ b/test/utils/axe.js @@ -1,10 +1,8 @@ import { axe } from 'vitest-axe'; -import { render } from '@testing-library/react'; import { expect } from 'vitest'; -const isAccessible = async (jsx) => { - const { container } = render(jsx); - expect(await axe(container)).toHaveNoViolations(); +const isAccessible = async (renderResult) => { + expect(await axe(renderResult.container)).toHaveNoViolations(); }; export default isAccessible; From 10923f2f48f7fa6b25557c8f276a6eaf587132ba Mon Sep 17 00:00:00 2001 From: Milo Fultz Date: Fri, 15 Dec 2023 06:09:05 +1100 Subject: [PATCH 05/18] Add contacts and documents --- .../Contacts/ContactsListTable.test.jsx | 20 +++++++++++++++++++ .../Documents/DocumentTable.test.jsx | 7 ++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/test/components/Contacts/ContactsListTable.test.jsx b/test/components/Contacts/ContactsListTable.test.jsx index 840334563..fd853c9dd 100644 --- a/test/components/Contacts/ContactsListTable.test.jsx +++ b/test/components/Contacts/ContactsListTable.test.jsx @@ -4,6 +4,7 @@ import { expect, it } from 'vitest'; import { BrowserRouter } from 'react-router-dom'; import { ContactListTable } from '@components/Contacts'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import isAccessible from '../../utils/axe'; const queryClient = new QueryClient(); @@ -15,6 +16,25 @@ const MockTableComponent = ({ contacts }) => ( ); +// TODO: Fix accessibility issues within this component +it.skip('should be accessible', async () => { + const contacts = [ + { + familyName: 'Abby', + givenName: 'Aaron', + person: 'Aaron Abby', + webId: 'https://example.com/Abby' + }, + { + familyName: 'Builder', + givenName: 'Bob', + person: 'Bob Builder', + webId: 'https://example.com/Builder' + } + ]; + isAccessible(render()); +}); + it('renders all clients from client context', () => { const contacts = [ { diff --git a/test/components/Documents/DocumentTable.test.jsx b/test/components/Documents/DocumentTable.test.jsx index 675d8bca0..0d03e161d 100644 --- a/test/components/Documents/DocumentTable.test.jsx +++ b/test/components/Documents/DocumentTable.test.jsx @@ -3,8 +3,8 @@ import { render } from '@testing-library/react'; import { describe, expect, it } from 'vitest'; import { BrowserRouter } from 'react-router-dom'; import DocumentTable from '@components/Documents'; - import { DocumentListContext } from '@contexts'; +import isAccessible from '../../utils/axe'; const mockedDocumentContext = { documentListObject: { @@ -60,4 +60,9 @@ describe('DocumentTable Component', () => { expect(row2FileType).not.toBeNull(); expect(row2Description).not.toBeNull(); }); + + // TODO: Fix accessibility issues with this component + it.skip('should be accessible', async () => { + isAccessible(render()); + }); }); From 497a7353fada037c05eb71c286ccf6e5abf4d88e Mon Sep 17 00:00:00 2001 From: Milo Fultz Date: Fri, 15 Dec 2023 06:12:50 +1100 Subject: [PATCH 06/18] Cleanup unused async --- test/components/CivicProfileForms/BasicInfo.test.jsx | 2 +- test/components/CivicProfileForms/FinancialInfo.test.jsx | 2 +- test/components/Contacts/ContactsListTable.test.jsx | 2 +- test/components/Documents/DocumentTable.test.jsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/components/CivicProfileForms/BasicInfo.test.jsx b/test/components/CivicProfileForms/BasicInfo.test.jsx index 64c650e94..73d4a1083 100644 --- a/test/components/CivicProfileForms/BasicInfo.test.jsx +++ b/test/components/CivicProfileForms/BasicInfo.test.jsx @@ -10,7 +10,7 @@ describe('Basic Info Form', () => { expect(getByText('Basic Info')).not.toBeNull(); }); - it('should be accessible', async () => { + it('should be accessible', () => { isAccessible(render()); }); }); diff --git a/test/components/CivicProfileForms/FinancialInfo.test.jsx b/test/components/CivicProfileForms/FinancialInfo.test.jsx index 0419b6e07..f79334772 100644 --- a/test/components/CivicProfileForms/FinancialInfo.test.jsx +++ b/test/components/CivicProfileForms/FinancialInfo.test.jsx @@ -10,7 +10,7 @@ describe('Financial Info Form', () => { expect(getByText('Financial Info')).not.toBeNull(); }); - it('should be accessible', async () => { + it('should be accessible', () => { isAccessible(render()); }); }); diff --git a/test/components/Contacts/ContactsListTable.test.jsx b/test/components/Contacts/ContactsListTable.test.jsx index fd853c9dd..dfd4ce846 100644 --- a/test/components/Contacts/ContactsListTable.test.jsx +++ b/test/components/Contacts/ContactsListTable.test.jsx @@ -17,7 +17,7 @@ const MockTableComponent = ({ contacts }) => ( ); // TODO: Fix accessibility issues within this component -it.skip('should be accessible', async () => { +it.skip('should be accessible', () => { const contacts = [ { familyName: 'Abby', diff --git a/test/components/Documents/DocumentTable.test.jsx b/test/components/Documents/DocumentTable.test.jsx index 0d03e161d..e747ef800 100644 --- a/test/components/Documents/DocumentTable.test.jsx +++ b/test/components/Documents/DocumentTable.test.jsx @@ -62,7 +62,7 @@ describe('DocumentTable Component', () => { }); // TODO: Fix accessibility issues with this component - it.skip('should be accessible', async () => { + it.skip('should be accessible', () => { isAccessible(render()); }); }); From efa5606b54d6f729c25e2365a5e979260db53a68 Mon Sep 17 00:00:00 2001 From: Milo Fultz Date: Fri, 15 Dec 2023 06:22:13 +1100 Subject: [PATCH 07/18] Add async await to avoid axe async errors --- test/components/CivicProfileForms/BasicInfo.test.jsx | 4 ++-- test/components/CivicProfileForms/FinancialInfo.test.jsx | 4 ++-- test/components/CivicProfileForms/FormLayout.test.jsx | 4 ++-- test/components/CivicProfileForms/HousingInfo.test.jsx | 4 ++-- test/components/Contacts/ContactsListTable.test.jsx | 4 ++-- test/components/Documents/DocumentTable.test.jsx | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/components/CivicProfileForms/BasicInfo.test.jsx b/test/components/CivicProfileForms/BasicInfo.test.jsx index 73d4a1083..25f2599aa 100644 --- a/test/components/CivicProfileForms/BasicInfo.test.jsx +++ b/test/components/CivicProfileForms/BasicInfo.test.jsx @@ -10,7 +10,7 @@ describe('Basic Info Form', () => { expect(getByText('Basic Info')).not.toBeNull(); }); - it('should be accessible', () => { - isAccessible(render()); + it('should be accessible', async () => { + await isAccessible(render()); }); }); diff --git a/test/components/CivicProfileForms/FinancialInfo.test.jsx b/test/components/CivicProfileForms/FinancialInfo.test.jsx index f79334772..e4c5773e7 100644 --- a/test/components/CivicProfileForms/FinancialInfo.test.jsx +++ b/test/components/CivicProfileForms/FinancialInfo.test.jsx @@ -10,7 +10,7 @@ describe('Financial Info Form', () => { expect(getByText('Financial Info')).not.toBeNull(); }); - it('should be accessible', () => { - isAccessible(render()); + it('should be accessible', async () => { + await isAccessible(render()); }); }); diff --git a/test/components/CivicProfileForms/FormLayout.test.jsx b/test/components/CivicProfileForms/FormLayout.test.jsx index cfbf0b588..008b8202e 100644 --- a/test/components/CivicProfileForms/FormLayout.test.jsx +++ b/test/components/CivicProfileForms/FormLayout.test.jsx @@ -39,6 +39,6 @@ describe('Civic Form Layout', () => { }); }); -it('should be accessible', () => { - isAccessible(renderLayout(CIVIC_FORM_LIST[0].path)); +it('should be accessible', async () => { + await isAccessible(renderLayout(CIVIC_FORM_LIST[0].path)); }); diff --git a/test/components/CivicProfileForms/HousingInfo.test.jsx b/test/components/CivicProfileForms/HousingInfo.test.jsx index 845844d26..87c9d76d8 100644 --- a/test/components/CivicProfileForms/HousingInfo.test.jsx +++ b/test/components/CivicProfileForms/HousingInfo.test.jsx @@ -23,9 +23,9 @@ describe('Housing info form', () => { expect(cityField).not.toBeNull(); }); - it('should be accessible', () => { + it('should be accessible', async () => { useCivicProfile.mockReturnValue({ data: {}, isSuccess: true }); - isAccessible(render()); + await isAccessible(render()); }); it('submits an address update when you click the submit button', async () => { diff --git a/test/components/Contacts/ContactsListTable.test.jsx b/test/components/Contacts/ContactsListTable.test.jsx index dfd4ce846..4cbd7e66c 100644 --- a/test/components/Contacts/ContactsListTable.test.jsx +++ b/test/components/Contacts/ContactsListTable.test.jsx @@ -17,7 +17,7 @@ const MockTableComponent = ({ contacts }) => ( ); // TODO: Fix accessibility issues within this component -it.skip('should be accessible', () => { +it.skip('should be accessible', async () => { const contacts = [ { familyName: 'Abby', @@ -32,7 +32,7 @@ it.skip('should be accessible', () => { webId: 'https://example.com/Builder' } ]; - isAccessible(render()); + await isAccessible(render()); }); it('renders all clients from client context', () => { diff --git a/test/components/Documents/DocumentTable.test.jsx b/test/components/Documents/DocumentTable.test.jsx index e747ef800..f26733606 100644 --- a/test/components/Documents/DocumentTable.test.jsx +++ b/test/components/Documents/DocumentTable.test.jsx @@ -62,7 +62,7 @@ describe('DocumentTable Component', () => { }); // TODO: Fix accessibility issues with this component - it.skip('should be accessible', () => { - isAccessible(render()); + it.skip('should be accessible', async () => { + await isAccessible(render()); }); }); From c2ea5bd313286e5c3c9c45739f058662bf1dc4d0 Mon Sep 17 00:00:00 2001 From: Milo Fultz Date: Fri, 15 Dec 2023 06:23:06 +1100 Subject: [PATCH 08/18] Add footer and form --- test/components/Footer/Footer.test.jsx | 15 +++++++++++++++ test/components/Form/FormSection.test.jsx | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/test/components/Footer/Footer.test.jsx b/test/components/Footer/Footer.test.jsx index eed56209d..000ddebfe 100644 --- a/test/components/Footer/Footer.test.jsx +++ b/test/components/Footer/Footer.test.jsx @@ -6,6 +6,7 @@ import { SessionContext } from '@contexts'; import { ThemeProvider } from '@mui/material/styles'; import theme from '../../../src/theme'; import Footer from '../../../src/components/Footer/Footer'; +import isAccessible from '../../utils/axe'; // clear created dom after each test, to start fresh for next afterEach(() => { @@ -28,4 +29,18 @@ describe('Footer', () => { expect(headings.length).toBe(2); }); + + it('should be accessible', async () => { + await isAccessible( + render( + + + +