Skip to content

Commit 334efab

Browse files
committed
UILD-841: Incorporate jest-axe tests into components for accessibility checks
1 parent 534549b commit 334efab

127 files changed

Lines changed: 3050 additions & 33 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"@testing-library/user-event": "^14.6.1",
6262
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
6363
"@types/jest": "^30.0.0",
64+
"@types/jest-axe": "^3.5.9",
6465
"@types/lodash": "^4.17.14",
6566
"@types/node": "^26.1.1",
6667
"@types/react": "^19.2.2",
@@ -82,6 +83,7 @@
8283
"husky": "^9.1.7",
8384
"identity-obj-proxy": "^3.0.0",
8485
"jest": "^30.3.0",
86+
"jest-axe": "^10.0.0",
8587
"jest-environment-jsdom": "^30.3.0",
8688
"jest-fetch-mock": "^3.0.3",
8789
"lint-staged": "^16.4.0",

src/features/comparison/components/Comparison/Comparison.test.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { RouterProvider, createMemoryRouter } from 'react-router-dom';
66
import { Fragment } from 'react/jsx-runtime';
77

88
import { fireEvent, render } from '@testing-library/react';
9+
import { axe } from 'jest-axe';
910

1011
import { useComparisonData } from '@/features/comparison/hooks';
1112

@@ -165,4 +166,17 @@ describe('Comparison', () => {
165166

166167
expect(getAllByText(1)[0]).toBeInTheDocument();
167168
});
169+
170+
describe('accessibility', () => {
171+
test.each([
172+
['no comparison items are selected', [] as StoreWithState[], []],
173+
['only one resource is selected', baseMockState, undefined],
174+
])('has no accessibility violations when %s', async (_description, stateArgs, comparisonItems) => {
175+
const { container } = renderWithState(stateArgs, comparisonItems);
176+
177+
const results = await axe(container);
178+
179+
expect(results).toHaveNoViolations();
180+
});
181+
});
168182
});

src/features/complexLookup/components/ComplexLookupField/ComplexLookupField.test.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { fireEvent, render, screen } from '@testing-library/react';
2+
import { axe } from 'jest-axe';
23

34
import { ComplexLookupType } from '@/features/complexLookup/constants/complexLookup.constants';
45

@@ -277,4 +278,56 @@ describe('ComplexLookupField', () => {
277278
);
278279
});
279280
});
281+
282+
describe('accessibility', () => {
283+
const twoValues = [
284+
{ id: '1', label: 'Item 1', meta: {} },
285+
{ id: '2', label: 'Item 2', meta: {} },
286+
];
287+
288+
test.each([
289+
[
290+
'read-only input when isNew is false',
291+
{ entry: { ...defaultEntry, layout: { ...defaultEntry.layout, isNew: false } } },
292+
{},
293+
],
294+
[
295+
'read-only input with formatted value',
296+
{
297+
entry: { ...defaultEntry, layout: { ...defaultEntry.layout, isNew: false } },
298+
value: twoValues,
299+
},
300+
{ localValue: twoValues },
301+
],
302+
['interactive field with button', { entry: defaultEntry }, {}],
303+
[
304+
'selected items when value exists',
305+
{ entry: defaultEntry, value: twoValues },
306+
{ localValue: twoValues, buttonLabelId: 'ld.change' },
307+
],
308+
['modal open', { entry: defaultEntry }, { isModalOpen: true }],
309+
[
310+
'modal open with initialQuery',
311+
{ entry: defaultEntry, value: [{ id: '1', label: 'Test Query', meta: {} }] },
312+
{ localValue: [{ id: '1', label: 'Test Query', meta: {} }], isModalOpen: true },
313+
],
314+
['modalConfig is null', { entry: defaultEntry }, { modalConfig: null, isModalOpen: true }],
315+
[
316+
'lookupType is undefined',
317+
{ entry: { ...defaultEntry, layout: { ...defaultEntry.layout, api: undefined } } },
318+
{},
319+
],
320+
])('has no accessibility violations when %s', async (_description, componentProps, hookOverrides) => {
321+
(ComplexLookupHooks.useComplexLookupField as jest.Mock).mockReturnValue({
322+
...defaultHookReturn,
323+
...hookOverrides,
324+
});
325+
326+
const { container } = render(<ComplexLookupField onChange={mockOnChange} {...componentProps} />);
327+
328+
const results = await axe(container);
329+
330+
expect(results).toHaveNoViolations();
331+
});
332+
});
280333
});

src/features/complexLookup/components/ComplexLookupSelectedItem/ComplexLookupSelectedItem.test.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { fireEvent, render } from '@testing-library/react';
2+
import { axe } from 'jest-axe';
23

34
import { ComplexLookupSelectedItem } from './ComplexLookupSelectedItem';
45

@@ -56,4 +57,17 @@ describe('ComplexLookupSelectedItem', () => {
5657
expect(container).toHaveClass('complex-lookup-selected-withWarning');
5758
expect(container).not.toHaveClass('complex-lookup-selected-embedded');
5859
});
60+
61+
describe('accessibility', () => {
62+
test.each([
63+
['default props', {}],
64+
['noWarningValue is false', { noWarningValue: false }],
65+
])('has no accessibility violations when %s', async (_description, overrides) => {
66+
const { container } = renderComponent(overrides);
67+
68+
const results = await axe(container);
69+
70+
expect(results).toHaveNoViolations();
71+
});
72+
});
5973
});

src/features/complexLookup/components/HubPreview/HubPreview.test.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { fireEvent, render, screen } from '@testing-library/react';
2+
import { axe } from 'jest-axe';
23

34
import { SOURCE_TYPES } from '@/common/constants/lookup.constants';
45

@@ -263,4 +264,29 @@ describe('HubPreview', () => {
263264
expect(screen.getByTestId('preview')).toBeInTheDocument();
264265
});
265266
});
267+
268+
describe('accessibility', () => {
269+
const emptyPreviewData = { id: 'hub_2', resource: {} as HubResourceData };
270+
271+
test.each([
272+
[
273+
'title from meta and assign button',
274+
{ previewData: mockPreviewData, previewMeta: mockPreviewMeta, onAssign: mockOnAssign },
275+
],
276+
['meta is null', { previewData: mockPreviewData, previewMeta: null }],
277+
['onAssign is not provided', { previewData: mockPreviewData, previewMeta: mockPreviewMeta }],
278+
[
279+
'preview resource is empty object and onAssign is provided',
280+
{ previewData: emptyPreviewData, previewMeta: mockPreviewMeta, onAssign: mockOnAssign },
281+
],
282+
['preview resource is empty object', { previewData: emptyPreviewData, previewMeta: mockPreviewMeta }],
283+
['preview data is null', { previewData: null, previewMeta: mockPreviewMeta }],
284+
])('has no accessibility violations when %s', async (_description, overrides) => {
285+
const { container } = render(<HubPreview onClose={mockOnClose} {...overrides} />);
286+
287+
const results = await axe(container);
288+
289+
expect(results).toHaveNoViolations();
290+
});
291+
});
266292
});

src/features/complexLookup/components/LookupModal/LookupModal.test.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { render, screen } from '@testing-library/react';
2+
import { axe } from 'jest-axe';
23

34
import { LookupModal } from './LookupModal';
45

@@ -101,4 +102,22 @@ describe('LookupModal', () => {
101102
expect(mockOnClose).toHaveBeenCalledTimes(1);
102103
});
103104
});
105+
106+
describe('accessibility', () => {
107+
test.each([
108+
['open with string title', { isOpen: true, title: 'Test Title' }],
109+
['closed', { isOpen: false, title: 'Test Title' }],
110+
['open with ReactElement title', { isOpen: true, title: <span data-testid="custom-title">Custom Title</span> }],
111+
])('has no accessibility violations when %s', async (_description, overrides) => {
112+
const { container } = render(
113+
<LookupModal onClose={mockOnClose} {...overrides}>
114+
<div>Test Content</div>
115+
</LookupModal>,
116+
);
117+
118+
const results = await axe(container);
119+
120+
expect(results).toHaveNoViolations();
121+
});
122+
});
104123
});

src/features/complexLookup/components/MarcPreview/MarcPreview.test.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { setInitialGlobalState } from '@/test/__mocks__/store';
33
import { IntlProvider } from 'react-intl';
44

55
import { fireEvent, render, screen } from '@testing-library/react';
6+
import { axe } from 'jest-axe';
67

78
import { useMarcPreviewStore, useUIStore } from '@/store';
89

@@ -186,4 +187,33 @@ describe('MarcPreview', () => {
186187
expect(assignButton).not.toBeDisabled();
187188
});
188189
});
190+
191+
describe('accessibility', () => {
192+
beforeEach(() => {
193+
mockCheckFailedId.mockReturnValue(true);
194+
});
195+
196+
test.each([
197+
['open with marc data and metadata', true, marcPreviewData, marcPreviewMetadata, {}],
198+
['isMarcPreviewOpen is false', false, marcPreviewData, marcPreviewMetadata, {}],
199+
['marcPreviewData is null', true, null, marcPreviewMetadata, {}],
200+
['assign button is provided', true, marcPreviewData, marcPreviewMetadata, { onAssign: mockOnAssign }],
201+
[
202+
'assign button is disabled via checkFailedId',
203+
true,
204+
marcPreviewData,
205+
marcPreviewMetadata,
206+
{ onAssign: mockOnAssign, checkFailedId: mockCheckFailedId },
207+
],
208+
])(
209+
'has no accessibility violations when %s',
210+
async (_description, isMarcPreviewOpen, marcData, metadata, props) => {
211+
const { container } = renderComponent(isMarcPreviewOpen, marcData, metadata, props);
212+
213+
const results = await axe(container);
214+
215+
expect(results).toHaveNoViolations();
216+
},
217+
);
218+
});
189219
});

src/features/complexLookup/components/content/AuthoritiesContent.test.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { render, screen } from '@testing-library/react';
2+
import { axe } from 'jest-axe';
23

34
import { AuthoritiesContent } from './AuthoritiesContent';
45

@@ -310,4 +311,30 @@ describe('AuthoritiesContent', () => {
310311
expect(screen.queryByTestId('content-container')).not.toBeInTheDocument();
311312
});
312313
});
314+
315+
describe('accessibility', () => {
316+
const baseProps = {
317+
isMarcPreviewOpen: false,
318+
isMarcLoading: false,
319+
handleAuthoritiesAssign: mockHandleAuthoritiesAssign,
320+
handleTitleClick: mockHandleTitleClick,
321+
handleCloseMarcPreview: mockHandleCloseMarcPreview,
322+
};
323+
324+
test.each([
325+
['result list view', {}],
326+
['result list view with checkFailedId', { checkFailedId: mockCheckFailedId }],
327+
['result list view with notSpecifiedLabel', { notSpecifiedLabel: 'Localized fallback' }],
328+
['MARC preview is loading', { isMarcPreviewOpen: true, isMarcLoading: true }],
329+
['MARC preview is open', { isMarcPreviewOpen: true }],
330+
['MARC preview is open without complex flow', { isMarcPreviewOpen: true, hasComplexFlow: false }],
331+
['MARC preview is open with checkFailedId', { isMarcPreviewOpen: true, checkFailedId: mockCheckFailedId }],
332+
])('has no accessibility violations when %s', async (_description, overrides) => {
333+
const { container } = render(<AuthoritiesContent {...baseProps} {...overrides} />);
334+
335+
const results = await axe(container);
336+
337+
expect(results).toHaveNoViolations();
338+
});
339+
});
313340
});

src/features/complexLookup/components/content/HubsContent.test.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { render, screen } from '@testing-library/react';
2+
import { axe } from 'jest-axe';
23

34
import { HubsContent } from './HubsContent';
45

@@ -102,4 +103,18 @@ describe('HubsContent', () => {
102103
expect(screen.getByTestId('hubs-lookup-result-list')).toBeInTheDocument();
103104
});
104105
});
106+
107+
describe('accessibility', () => {
108+
test.each([
109+
['default', {}],
110+
['handleHubTitleClick provided', { handleHubTitleClick: mockHandleHubTitleClick }],
111+
['isAssigning is true', { isAssigning: true }],
112+
])('has no accessibility violations when %s', async (_description, overrides) => {
113+
const { container } = render(<HubsContent {...overrides} />);
114+
115+
const results = await axe(container);
116+
117+
expect(results).toHaveNoViolations();
118+
});
119+
});
105120
});

src/features/complexLookup/components/modals/AuthoritiesModal/AuthoritiesModal.test.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { setInitialGlobalState } from '@/test/__mocks__/store';
22

33
import { fireEvent, render, screen } from '@testing-library/react';
4+
import { axe } from 'jest-axe';
45

56
import { useSchemaPipeline } from '@/common/hooks/useSchemaPipeline';
67

@@ -471,4 +472,57 @@ describe('AuthoritiesModal', () => {
471472
});
472473
});
473474
});
475+
476+
describe('accessibility', () => {
477+
const baseAuthoritiesModalLogicReturn = {
478+
isMarcPreviewOpen: false,
479+
isMarcLoading: false,
480+
authoritiesData: {
481+
onSegmentEnter: mockOnSegmentEnter,
482+
},
483+
handleTitleClick: mockHandleTitleClick,
484+
handleAuthoritiesAssign: mockHandleAuthoritiesAssign,
485+
handleCloseMarcPreview: mockHandleCloseMarcPreview,
486+
handleResetMarcPreview: jest.fn(),
487+
checkFailedId: undefined,
488+
cleanup: {
489+
setIsMarcPreviewOpen: mockSetIsMarcPreviewOpen,
490+
resetPreview: mockResetPreview,
491+
resetMarcPreviewData: mockResetComplexValue,
492+
resetMarcPreviewMetadata: mockResetMetadata,
493+
},
494+
};
495+
496+
test.each([
497+
['modal open with search results', {}, {}],
498+
['modal closed', { isOpen: false }, {}],
499+
['MARC preview is open', {}, { isMarcPreviewOpen: true, isMarcLoading: false }],
500+
['MARC preview is loading', {}, { isMarcPreviewOpen: true, isMarcLoading: true }],
501+
[
502+
'modal config with notSpecified label',
503+
{
504+
modalConfig: {
505+
labels: {
506+
button: { base: 'ld.assignAuthority', change: 'ld.change' },
507+
notSpecified: 'ld.notSpecified',
508+
},
509+
} as unknown as ModalConfig,
510+
},
511+
{},
512+
],
513+
])('has no accessibility violations when %s', async (_description, componentProps, hookOverrides) => {
514+
(ComplexLookupHooks.useAuthoritiesModalLogic as jest.Mock).mockReturnValue({
515+
...baseAuthoritiesModalLogicReturn,
516+
...hookOverrides,
517+
});
518+
519+
const { container } = render(
520+
<AuthoritiesModal isOpen={true} onClose={mockOnClose} onAssign={mockOnAssign} {...componentProps} />,
521+
);
522+
523+
const results = await axe(container);
524+
525+
expect(results).toHaveNoViolations();
526+
});
527+
});
474528
});

0 commit comments

Comments
 (0)