From ca3437af74a2fcc7614107ed8d357a6fed06a8c1 Mon Sep 17 00:00:00 2001 From: guillaume-peoch-sonarsource Date: Tue, 23 May 2023 12:37:05 +0200 Subject: [PATCH] [No JIRA] add jest-axe --- server/sonar-web/.eslintrc | 2 +- server/sonar-web/config/jest/SetupJestAxe.ts | 28 ++++++ .../config/jest/SetupReactTestingLibrary.ts | 1 - ...=> use-await-expect-async-matcher-test.js} | 10 +- server/sonar-web/eslint-local-rules/index.js | 2 +- ...t.js => use-await-expect-async-matcher.js} | 15 +-- server/sonar-web/jest.config.js | 5 +- server/sonar-web/package.json | 2 + .../__tests__/ProjectManagementApp-it.tsx | 2 +- .../js/apps/users/__tests__/UsersApp-it.tsx | 95 ++++++++++++------- .../js/apps/users/components/PasswordForm.tsx | 8 +- .../js/components/controls/HelpTooltip.tsx | 2 + .../__snapshots__/HelpTooltip-test.tsx.snap | 2 + .../src/main/js/components/icons/Icon.tsx | 5 +- .../__snapshots__/Icon-test.tsx.snap | 2 +- .../__snapshots__/Alert-test.tsx.snap | 1 - server/sonar-web/src/main/js/types/jest.d.ts | 1 + server/sonar-web/yarn.lock | 78 ++++++++++++--- 18 files changed, 191 insertions(+), 70 deletions(-) create mode 100644 server/sonar-web/config/jest/SetupJestAxe.ts rename server/sonar-web/eslint-local-rules/__tests__/{use-await-expect-tohaveatooltipwithcontent-test.js => use-await-expect-async-matcher-test.js} (80%) rename server/sonar-web/eslint-local-rules/{use-await-expect-tohaveatooltipwithcontent.js => use-await-expect-async-matcher.js} (71%) diff --git a/server/sonar-web/.eslintrc b/server/sonar-web/.eslintrc index d42654a5813..eb6d15c4886 100644 --- a/server/sonar-web/.eslintrc +++ b/server/sonar-web/.eslintrc @@ -15,6 +15,6 @@ "local-rules/convert-class-to-function-component": "warn", "local-rules/no-conditional-rendering-of-deferredspinner": "warn", "local-rules/use-jest-mocked": "warn", - "local-rules/use-await-expect-tohaveatooltipwithcontent": "warn" + "local-rules/use-await-expect-async-matcher": "warn" } } diff --git a/server/sonar-web/config/jest/SetupJestAxe.ts b/server/sonar-web/config/jest/SetupJestAxe.ts new file mode 100644 index 00000000000..91eb4a19020 --- /dev/null +++ b/server/sonar-web/config/jest/SetupJestAxe.ts @@ -0,0 +1,28 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import '@testing-library/jest-dom/extend-expect'; +import { axe, toHaveNoViolations } from 'jest-axe'; + +expect.extend({ + async toHaveNoA11yViolations(received: HTMLElement) { + const result = await axe(received); + return toHaveNoViolations.toHaveNoViolations(result); + }, +}); diff --git a/server/sonar-web/config/jest/SetupReactTestingLibrary.ts b/server/sonar-web/config/jest/SetupReactTestingLibrary.ts index c7fcfac0aee..04589b32dc0 100644 --- a/server/sonar-web/config/jest/SetupReactTestingLibrary.ts +++ b/server/sonar-web/config/jest/SetupReactTestingLibrary.ts @@ -24,7 +24,6 @@ configure({ asyncUtilTimeout: 3000, }); -// Don't forget to update src/main/js/types/jest.d.ts when registering custom matchers. expect.extend({ async toHaveATooltipWithContent(received: any, content: string) { if (!(received instanceof Element)) { diff --git a/server/sonar-web/eslint-local-rules/__tests__/use-await-expect-tohaveatooltipwithcontent-test.js b/server/sonar-web/eslint-local-rules/__tests__/use-await-expect-async-matcher-test.js similarity index 80% rename from server/sonar-web/eslint-local-rules/__tests__/use-await-expect-tohaveatooltipwithcontent-test.js rename to server/sonar-web/eslint-local-rules/__tests__/use-await-expect-async-matcher-test.js index 5c738aae598..f87e32a2da7 100644 --- a/server/sonar-web/eslint-local-rules/__tests__/use-await-expect-tohaveatooltipwithcontent-test.js +++ b/server/sonar-web/eslint-local-rules/__tests__/use-await-expect-async-matcher-test.js @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ const { RuleTester } = require('eslint'); -const useJestMocked = require('../use-await-expect-tohaveatooltipwithcontent'); +const useJestMocked = require('../use-await-expect-async-matcher'); const ruleTester = new RuleTester({ parser: require.resolve('@typescript-eslint/parser'), @@ -33,7 +33,13 @@ ruleTester.run('use-await-expect-tohaveatooltipwithcontent', useJestMocked, { invalid: [ { code: `expect(node).toHaveATooltipWithContent("Help text");`, - errors: [{ messageId: 'useAwaitExpectToHaveATooltipWithContent' }], + errors: [ + { + message: + 'expect.toHaveATooltipWithContent() is asynchronous; you must prefix expect() with await', + }, + ], + output: `await expect(node).toHaveATooltipWithContent("Help text");`, }, ], }); diff --git a/server/sonar-web/eslint-local-rules/index.js b/server/sonar-web/eslint-local-rules/index.js index 930d7318243..6f38c65a645 100644 --- a/server/sonar-web/eslint-local-rules/index.js +++ b/server/sonar-web/eslint-local-rules/index.js @@ -25,5 +25,5 @@ module.exports = { 'use-componentqualifier-enum': require('./use-componentqualifier-enum'), 'use-metrickey-enum': require('./use-metrickey-enum'), 'use-metrictype-enum': require('./use-metrictype-enum'), - 'use-await-expect-tohaveatooltipwithcontent': require('./use-await-expect-tohaveatooltipwithcontent'), + 'use-await-expect-async-matcher': require('./use-await-expect-async-matcher'), }; diff --git a/server/sonar-web/eslint-local-rules/use-await-expect-tohaveatooltipwithcontent.js b/server/sonar-web/eslint-local-rules/use-await-expect-async-matcher.js similarity index 71% rename from server/sonar-web/eslint-local-rules/use-await-expect-tohaveatooltipwithcontent.js rename to server/sonar-web/eslint-local-rules/use-await-expect-async-matcher.js index 05c2650e144..336c070a6ce 100644 --- a/server/sonar-web/eslint-local-rules/use-await-expect-tohaveatooltipwithcontent.js +++ b/server/sonar-web/eslint-local-rules/use-await-expect-async-matcher.js @@ -19,19 +19,22 @@ */ module.exports = { meta: { - messages: { - useAwaitExpectToHaveATooltipWithContent: - 'expect.toHaveATooltipWithContent() is asynchronous; you must prefix expect() with await', - }, + fixable: 'code', }, create(context) { return { Identifier(node) { if ( - node.name === 'toHaveATooltipWithContent' && + ['toHaveATooltipWithContent', 'toHaveNoA11yViolations'].includes(node.name) && node.parent?.parent?.parent?.type !== 'AwaitExpression' ) { - context.report({ node, messageId: 'useAwaitExpectToHaveATooltipWithContent' }); + context.report({ + node: node.parent?.parent?.parent, + message: `expect.${node.name}() is asynchronous; you must prefix expect() with await`, + fix(fixer) { + return fixer.insertTextBefore(node.parent?.parent?.parent, 'await '); + }, + }); } }, }; diff --git a/server/sonar-web/jest.config.js b/server/sonar-web/jest.config.js index 58d840de95a..567f049d18d 100644 --- a/server/sonar-web/jest.config.js +++ b/server/sonar-web/jest.config.js @@ -19,7 +19,10 @@ module.exports = { '/config/jest/SetupTestEnvironment.ts', '/config/jest/SetupTheme.js', ], - setupFilesAfterEnv: ['/config/jest/SetupReactTestingLibrary.ts'], + setupFilesAfterEnv: [ + '/config/jest/SetupReactTestingLibrary.ts', + '/config/jest/SetupJestAxe.ts', + ], snapshotSerializers: ['enzyme-to-json/serializer', '@emotion/jest/serializer'], testEnvironment: 'jsdom', testPathIgnorePatterns: [ diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json index eb0daa0571b..6d42f3de7bb 100644 --- a/server/sonar-web/package.json +++ b/server/sonar-web/package.json @@ -60,6 +60,7 @@ "@types/dompurify": "3.0.2", "@types/enzyme": "3.10.13", "@types/jest": "29.5.2", + "@types/jest-axe": "3.5.5", "@types/lodash": "4.14.195", "@types/node": "18.16.18", "@types/react": "17.0.39", @@ -93,6 +94,7 @@ "fs-extra": "11.1.1", "http-proxy": "1.18.1", "jest": "29.5.0", + "jest-axe": "7.0.1", "jest-environment-jsdom": "29.5.0", "jest-junit": "16.0.0", "jsdom": "21.1.1", diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx index 5a0ae311096..ef615cd608d 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx @@ -99,7 +99,7 @@ const ui = { qualifierFilter: byRole('combobox', { name: 'projects_management.filter_by_component' }), analysisDateFilter: byPlaceholderText('last_analysis_before'), provisionedFilter: byRole('checkbox', { - name: 'provisioning.only_provisioned provisioning.only_provisioned.tooltip', + name: 'provisioning.only_provisioned help', }), searchFilter: byRole('searchbox', { name: 'search.search_by_name_or_key' }), diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx b/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx index 56e63dc6293..e455cc52725 100644 --- a/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx @@ -58,6 +58,7 @@ const ui = { bobUpdateButton: byRole('button', { name: 'users.manage_user.bob.marley' }), scmAddButton: byRole('button', { name: 'add_verb' }), createUserDialogButton: byRole('button', { name: 'create' }), + cancelButton: byRole('button', { name: 'cancel' }), reloadButton: byRole('button', { name: 'reload' }), doneButton: byRole('button', { name: 'done' }), changeButton: byRole('button', { name: 'change_verb' }), @@ -295,13 +296,7 @@ describe('in non managed mode', () => { const user = userEvent.setup(); renderUsersApp(); - await act(async () => - user.click( - await within(await ui.aliceRow.find()).findByRole('button', { - name: 'users.update_users_groups.alice.merveille', - }) - ) - ); + await act(async () => user.click(await ui.aliceUpdateGroupButton.find())); expect(await ui.dialogGroups.find()).toBeInTheDocument(); expect(ui.getGroups()).toHaveLength(2); @@ -334,13 +329,7 @@ describe('in non managed mode', () => { const user = userEvent.setup(); renderUsersApp(); - await act(async () => - user.click( - await within(await ui.aliceRow.find()).findByRole('button', { - name: 'users.manage_user.alice.merveille', - }) - ) - ); + await act(async () => user.click(await ui.aliceUpdateButton.find())); await user.click( await within(ui.aliceRow.get()).findByRole('button', { name: 'update_details' }) ); @@ -360,13 +349,7 @@ describe('in non managed mode', () => { const user = userEvent.setup(); renderUsersApp(); - await act(async () => - user.click( - await within(await ui.aliceRow.find()).findByRole('button', { - name: 'users.manage_user.alice.merveille', - }) - ) - ); + await act(async () => user.click(await ui.aliceUpdateButton.find())); await user.click( await within(ui.aliceRow.get()).findByRole('button', { name: 'users.deactivate' }) ); @@ -388,13 +371,7 @@ describe('in non managed mode', () => { const currentUser = mockLoggedInUser({ login: 'alice.merveille' }); renderUsersApp([], currentUser); - await act(async () => - user.click( - await within(await ui.aliceRow.find()).findByRole('button', { - name: 'users.manage_user.alice.merveille', - }) - ) - ); + await act(async () => user.click(await ui.aliceUpdateButton.find())); await user.click( await within(ui.aliceRow.get()).findByRole('button', { name: 'my_profile.password.title' }) ); @@ -511,8 +488,8 @@ describe('in manage mode', () => { await user.click(await ui.localFilter.find()); }); - expect(ui.aliceRowWithLocalBadge.get()).toBeInTheDocument(); expect(ui.bobRow.query()).not.toBeInTheDocument(); + expect(ui.aliceRowWithLocalBadge.get()).toBeInTheDocument(); }); it('should be able to change tokens of a user', async () => { @@ -586,12 +563,11 @@ describe('in manage mode', () => { authenticationHandler.addProvisioningTask({ status: TaskStatuses.Failed, executedAt: '2022-02-03T11:45:35+0200', - errorMessage: "T'es mauvais Jacques", + errorMessage: 'Error Message', }); renderUsersApp([Feature.GithubProvisioning]); await act(async () => expect(await ui.githubProvisioningAlert.find()).toBeInTheDocument()); - expect(screen.queryByText("T'es mauvais Jacques")).not.toBeInTheDocument(); - + expect(screen.queryByText('Error Message')).not.toBeInTheDocument(); expect(ui.githubProvisioningSuccess.query()).not.toBeInTheDocument(); }); @@ -603,11 +579,11 @@ describe('in manage mode', () => { authenticationHandler.addProvisioningTask({ status: TaskStatuses.Failed, executedAt: '2022-02-03T11:45:35+0200', - errorMessage: "T'es mauvais Jacques", + errorMessage: 'Error Message', }); renderUsersApp([Feature.GithubProvisioning]); await act(async () => expect(await ui.githubProvisioningAlert.find()).toBeInTheDocument()); - expect(screen.queryByText("T'es mauvais Jacques")).not.toBeInTheDocument(); + expect(screen.queryByText('Error Message')).not.toBeInTheDocument(); expect(ui.githubProvisioningSuccess.query()).not.toBeInTheDocument(); expect(ui.githubProvisioningInProgress.query()).not.toBeInTheDocument(); }); @@ -621,6 +597,57 @@ it('should render external identity Providers', async () => { expect(await ui.denisRow.find()).toHaveTextContent(/test2: UnknownExternalProvider/); }); +it('accessibility', async () => { + userHandler.setIsManaged(false); + const user = userEvent.setup(); + renderUsersApp(); + + // user list page should be accessible + expect(await ui.aliceRow.find()).toBeInTheDocument(); + await expect(document.body).toHaveNoA11yViolations(); + + // user creation dialog should be accessible + await user.click(await ui.createUserButton.find()); + expect(await ui.dialogCreateUser.find()).toBeInTheDocument(); + await expect(ui.dialogCreateUser.get()).toHaveNoA11yViolations(); + await user.click(ui.cancelButton.get()); + + // users group membership dialog should be accessible + user.click(await ui.aliceUpdateGroupButton.find()); + expect(await ui.dialogGroups.find()).toBeInTheDocument(); + await expect(await ui.dialogGroups.find()).toHaveNoA11yViolations(); + await act(async () => { + await user.click(ui.doneButton.get()); + }); + + // user update dialog should be accessible + await user.click(await ui.aliceUpdateButton.find()); + await user.click(await ui.aliceRow.byRole('button', { name: 'update_details' }).find()); + expect(await ui.dialogUpdateUser.find()).toBeInTheDocument(); + await expect(await ui.dialogUpdateUser.find()).toHaveNoA11yViolations(); + await user.click(ui.cancelButton.get()); + + // user tokens dialog should be accessible + user.click( + await ui.aliceRow + .byRole('button', { + name: 'users.update_tokens_for_x.Alice Merveille', + }) + .find() + ); + expect(await ui.dialogTokens.find()).toBeInTheDocument(); + await expect(await ui.dialogTokens.find()).toHaveNoA11yViolations(); + await user.click(ui.doneButton.get()); + + // user password dialog should be accessible + await user.click(await ui.aliceUpdateButton.find()); + await user.click( + await ui.aliceRow.byRole('button', { name: 'my_profile.password.title' }).find() + ); + expect(await ui.dialogPasswords.find()).toBeInTheDocument(); + await expect(await ui.dialogPasswords.find()).toHaveNoA11yViolations(); +}); + function renderUsersApp(featureList: Feature[] = [], currentUser?: CurrentUser) { // eslint-disable-next-line testing-library/no-unnecessary-act renderApp('admin/users', , { diff --git a/server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx b/server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx index 5611cb2de2c..d13ff24e7fb 100644 --- a/server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx @@ -19,8 +19,8 @@ */ import * as React from 'react'; import { changePassword } from '../../../api/users'; -import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import Modal from '../../../components/controls/Modal'; +import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import { Alert } from '../../../components/ui/Alert'; import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker'; import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation'; @@ -118,7 +118,7 @@ export default class PasswordForm extends React.PureComponent { {/* keep this fake field to hack browser autofill */} - + { {/* keep this fake field to hack browser autofill */} - + { {/* keep this fake field to hack browser autofill */} - + diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/HelpTooltip-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/HelpTooltip-test.tsx.snap index 34f670866ac..f57bb80bd3a 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/HelpTooltip-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/HelpTooltip-test.tsx.snap @@ -33,12 +33,14 @@ exports[`should render properly: default 1`] = ` data-testid="help-tooltip-activator" > } fill="#888" + role="img" size={12} /> diff --git a/server/sonar-web/src/main/js/components/icons/Icon.tsx b/server/sonar-web/src/main/js/components/icons/Icon.tsx index 5e02dc790fe..6bd8195d7ab 100644 --- a/server/sonar-web/src/main/js/components/icons/Icon.tsx +++ b/server/sonar-web/src/main/js/components/icons/Icon.tsx @@ -20,7 +20,7 @@ import { uniqueId } from 'lodash'; import * as React from 'react'; -export interface IconProps extends React.AriaAttributes { +export interface IconProps extends React.HTMLAttributes { className?: string; fill?: string; size?: number; @@ -28,7 +28,7 @@ export interface IconProps extends React.AriaAttributes { description?: React.ReactNode; } -interface Props extends React.AriaAttributes { +interface Props extends React.HTMLAttributes { children: React.ReactNode; className?: string; size?: number; @@ -72,7 +72,6 @@ export default function Icon({ width={width} xmlnsXlink="http://www.w3.org/1999/xlink" xmlSpace="preserve" - role="img" aria-describedby={description && !hidden ? id : undefined} {...iconProps} > diff --git a/server/sonar-web/src/main/js/components/icons/__tests__/__snapshots__/Icon-test.tsx.snap b/server/sonar-web/src/main/js/components/icons/__tests__/__snapshots__/Icon-test.tsx.snap index bb617d3683c..8ce95580571 100644 --- a/server/sonar-web/src/main/js/components/icons/__tests__/__snapshots__/Icon-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/icons/__tests__/__snapshots__/Icon-test.tsx.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should render correctly 1`] = `"
"`; +exports[`should render correctly 1`] = `"
"`; diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/Alert-test.tsx.snap b/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/Alert-test.tsx.snap index 3f1c5a78494..ed55cd5b070 100644 --- a/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/Alert-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/Alert-test.tsx.snap @@ -86,7 +86,6 @@ exports[`should render banner alert with correct css 1`] = ` > { toHaveATooltipWithContent(content: string): Promise; + toHaveNoA11yViolations(): Promise; } } diff --git a/server/sonar-web/yarn.lock b/server/sonar-web/yarn.lock index db1cfd1d136..17a4c72095d 100644 --- a/server/sonar-web/yarn.lock +++ b/server/sonar-web/yarn.lock @@ -4230,6 +4230,16 @@ __metadata: languageName: node linkType: hard +"@types/jest-axe@npm:3.5.5": + version: 3.5.5 + resolution: "@types/jest-axe@npm:3.5.5" + dependencies: + "@types/jest": "*" + axe-core: ^3.5.5 + checksum: 535038968034fe80fb466dcd5939ea5d9e9adb3ef00852ded3e41c62536c05137eb30bcbfd608142d2bc571d65c20b8e3563181674fb48594c2662d340bb4da5 + languageName: node + linkType: hard + "@types/jest@npm:*": version: 27.4.0 resolution: "@types/jest@npm:27.4.0" @@ -4852,6 +4862,7 @@ __metadata: "@types/dompurify": 3.0.2 "@types/enzyme": 3.10.13 "@types/jest": 29.5.2 + "@types/jest-axe": 3.5.5 "@types/lodash": 4.14.195 "@types/node": 18.16.18 "@types/react": 17.0.39 @@ -4898,6 +4909,7 @@ __metadata: fs-extra: 11.1.1 http-proxy: 1.18.1 jest: 29.5.0 + jest-axe: 7.0.1 jest-environment-jsdom: 29.5.0 jest-junit: 16.0.0 jsdom: 21.1.1 @@ -5335,6 +5347,20 @@ __metadata: languageName: node linkType: hard +"axe-core@npm:4.5.1": + version: 4.5.1 + resolution: "axe-core@npm:4.5.1" + checksum: db90c6b41483e9c3452393933072fe0b8c2221e6d9b96ae0e6a03a4ce1e4c35bec539c92e9b6fcd63c4acf6678ad3c3ca7f5ab1d884210d157867cc54acd4f6a + languageName: node + linkType: hard + +"axe-core@npm:^3.5.5": + version: 3.5.6 + resolution: "axe-core@npm:3.5.6" + checksum: 000777d2b6bf1f390beb1fb4b8714ed9127797c021c345b032db0c144e07320dbbe8cb0bcb7688b90b79cfbd3cdc1f27a4dc857804e3c61d7e0defb34deeb830 + languageName: node + linkType: hard + "axe-core@npm:^4.6.2": version: 4.6.3 resolution: "axe-core@npm:4.6.3" @@ -9048,6 +9074,18 @@ __metadata: languageName: node linkType: hard +"jest-axe@npm:7.0.1": + version: 7.0.1 + resolution: "jest-axe@npm:7.0.1" + dependencies: + axe-core: 4.5.1 + chalk: 4.1.2 + jest-matcher-utils: 29.2.2 + lodash.merge: 4.6.2 + checksum: 3c9b0b8669f6fe5d143ee74aa0414831ea75bbe9e38551aa82352d9102e2827952a14cb3a6782fbd847a0febf6da3461424d1a2eb77256159719890f502cb01f + languageName: node + linkType: hard + "jest-changed-files@npm:^29.5.0": version: 29.5.0 resolution: "jest-changed-files@npm:29.5.0" @@ -9163,6 +9201,18 @@ __metadata: languageName: node linkType: hard +"jest-diff@npm:^29.2.1, jest-diff@npm:^29.5.0": + version: 29.5.0 + resolution: "jest-diff@npm:29.5.0" + dependencies: + chalk: ^4.0.0 + diff-sequences: ^29.4.3 + jest-get-type: ^29.4.3 + pretty-format: ^29.5.0 + checksum: dfd0f4a299b5d127779c76b40106c37854c89c3e0785098c717d52822d6620d227f6234c3a9291df204d619e799e3654159213bf93220f79c8e92a55475a3d39 + languageName: node + linkType: hard + "jest-diff@npm:^29.3.1": version: 29.3.1 resolution: "jest-diff@npm:29.3.1" @@ -9175,18 +9225,6 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-diff@npm:29.5.0" - dependencies: - chalk: ^4.0.0 - diff-sequences: ^29.4.3 - jest-get-type: ^29.4.3 - pretty-format: ^29.5.0 - checksum: dfd0f4a299b5d127779c76b40106c37854c89c3e0785098c717d52822d6620d227f6234c3a9291df204d619e799e3654159213bf93220f79c8e92a55475a3d39 - languageName: node - linkType: hard - "jest-docblock@npm:^29.4.3": version: 29.4.3 resolution: "jest-docblock@npm:29.4.3" @@ -9310,6 +9348,18 @@ __metadata: languageName: node linkType: hard +"jest-matcher-utils@npm:29.2.2": + version: 29.2.2 + resolution: "jest-matcher-utils@npm:29.2.2" + dependencies: + chalk: ^4.0.0 + jest-diff: ^29.2.1 + jest-get-type: ^29.2.0 + pretty-format: ^29.2.1 + checksum: 97ef2638ab826c25f84bfedea231cef091820ae0876ba316922da81145e950d2b9d2057d3645813b5ee880bb975ed4f22e228dda5d0d26a20715e575b675357d + languageName: node + linkType: hard + "jest-matcher-utils@npm:^29.3.1": version: 29.3.1 resolution: "jest-matcher-utils@npm:29.3.1" @@ -10004,7 +10054,7 @@ __metadata: languageName: node linkType: hard -"lodash.merge@npm:^4.6.2": +"lodash.merge@npm:4.6.2, lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" checksum: ad580b4bdbb7ca1f7abf7e1bce63a9a0b98e370cf40194b03380a46b4ed799c9573029599caebc1b14e3f24b111aef72b96674a56cfa105e0f5ac70546cdc005 @@ -11085,7 +11135,7 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.5.0": +"pretty-format@npm:^29.2.1, pretty-format@npm:^29.5.0": version: 29.5.0 resolution: "pretty-format@npm:29.5.0" dependencies: -- 2.39.5