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/components/CivicProfileForms/BasicInfo.test.jsx b/test/components/CivicProfileForms/BasicInfo.test.jsx index bc70244c7..73d4a1083 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', () => { + isAccessible(render()); + }); }); diff --git a/test/components/CivicProfileForms/FinancialInfo.test.jsx b/test/components/CivicProfileForms/FinancialInfo.test.jsx index a4adf2e4d..f79334772 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', () => { + 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/components/Contacts/ContactsListTable.test.jsx b/test/components/Contacts/ContactsListTable.test.jsx index 840334563..dfd4ce846 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', () => { + 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..e747ef800 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', () => { + isAccessible(render()); + }); }); diff --git a/test/components/Footer/Footer.test.jsx b/test/components/Footer/Footer.test.jsx index eed56209d..479649069 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', () => { + isAccessible( + render( + + + +