Jest axe a11y testing (#127185)

* [A11y] Added axe testing to testBed to be used in jest tests (CITs)

* [A11y] Refactored axe helpers to be outside of testBed for better separation of concerns. Also added tests for indices tab in Index Management.

* [A11y] Switched `axe_helpers` to use a Kibana wide axe config and rules that are now in kbn-test package

* [A11y] Switched `axe_helpers` to use a Kibana wide axe config and rules that are now in kbn-test package

* [A11y] Completed a11y tests in Index Management Indices tab. Also refactored http requests helper file.

* Removed import comment

* Reverted yarn.lock change

* Correct yarn.lock changes

* [IM] Updated the branch after merging main

* [IM] Reverted unrelated ResponseError interface changes

* [IM] Fixed eslint issue with kbn/test import
This commit is contained in:
Yulia Čech 2022-03-29 10:44:11 +02:00 committed by GitHub
parent aa4ce92457
commit bef90a5866
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 317 additions and 24 deletions

View file

@ -571,6 +571,7 @@
"@types/inquirer": "^7.3.1",
"@types/intl-relativeformat": "^2.1.0",
"@types/jest": "^26.0.22",
"@types/jest-axe": "^3.5.3",
"@types/jest-specific-snapshot": "^0.5.5",
"@types/joi": "^17.2.3",
"@types/jquery": "^3.3.31",
@ -821,6 +822,7 @@
"is-glob": "^4.0.1",
"is-path-inside": "^3.0.2",
"jest": "^26.6.3",
"jest-axe": "^5.0.0",
"jest-canvas-mock": "^2.3.1",
"jest-circus": "^26.6.3",
"jest-cli": "^26.6.3",

View file

@ -34,6 +34,7 @@ NPM_MODULE_EXTRA_FILES = [
RUNTIME_DEPS = [
"//packages/kbn-dev-utils",
"//packages/kbn-i18n-react",
"//packages/kbn-test",
"//packages/kbn-std",
"//packages/kbn-utils",
"@npm//@elastic/elasticsearch",
@ -51,6 +52,7 @@ RUNTIME_DEPS = [
"@npm//he",
"@npm//history",
"@npm//jest",
"@npm//jest-axe",
"@npm//jest-cli",
"@npm//jest-snapshot",
"@npm//jest-styled-components",
@ -76,9 +78,11 @@ TYPES_DEPS = [
"//packages/kbn-dev-utils:npm_module_types",
"//packages/kbn-i18n-react:npm_module_types",
"//packages/kbn-std:npm_module_types",
"//packages/kbn-test:npm_module_types",
"//packages/kbn-utils:npm_module_types",
"@npm//@elastic/elasticsearch",
"@npm//axios",
"@npm//axe-core",
"@npm//elastic-apm-node",
"@npm//del",
"@npm//exit-hook",
@ -96,6 +100,7 @@ TYPES_DEPS = [
"@npm//@types/he",
"@npm//@types/history",
"@npm//@types/jest",
"@npm//@types/jest-axe",
"@npm//@types/joi",
"@npm//@types/lodash",
"@npm//@types/mustache",

View file

@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { configureAxe } from 'jest-axe';
import { Result } from 'axe-core';
import { AXE_OPTIONS, AXE_CONFIG } from '@kbn/test';
import { ReactWrapper } from './testbed/types';
const axeRunner = configureAxe({ globalOptions: { ...AXE_CONFIG } });
/**
* Function to test if a component doesn't have a11y violations from axe automated testing
* @param component
*/
export const expectToBeAccessible = async (component: ReactWrapper): Promise<void> => {
const violations = await getA11yViolations(component);
expect(violations).toHaveLength(0);
};
/**
* Returns a11y violations as found by axe testing
* @param component
*/
export const getA11yViolations = async (component: ReactWrapper): Promise<Result[]> => {
const axeResults = await axeRunner(component.html(), {
...AXE_OPTIONS,
resultTypes: ['violations'],
});
return axeResults.violations;
};

View file

@ -24,6 +24,8 @@ export * from './stub_web_worker';
export * from './testbed';
export * from './axe_helpers';
export const nextTick = () => new Promise((res) => process.nextTick(res));
export const delay = (time = 0) => new Promise((resolve) => setTimeout(resolve, time));

View file

@ -30,7 +30,7 @@ export interface EuiTableMetaData {
}
export interface TestBed<T = string> {
/** The comonent under test */
/** The component under test */
component: ReactWrapper;
/**
* Pass it a `data-test-subj` and it will return true if it exists or false if it does not exist.

View file

@ -86,6 +86,7 @@ TYPES_DEPS = [
"@npm//@elastic/elasticsearch",
"@npm//@jest/console",
"@npm//@jest/reporters",
"@npm//axe-core",
"@npm//axios",
"@npm//elastic-apm-node",
"@npm//del",

View file

@ -69,3 +69,5 @@ export { runJest } from './jest/run';
export * from './kbn_archiver_cli';
export * from './kbn_client';
export { AXE_CONFIG, AXE_OPTIONS } from './a11y/config';

View file

@ -8,9 +8,9 @@
import chalk from 'chalk';
import testSubjectToCss from '@kbn/test-subj-selector';
import { AXE_CONFIG, AXE_OPTIONS } from '@kbn/test';
import { FtrService } from '../../ftr_provider_context';
import { AXE_CONFIG, AXE_OPTIONS } from './constants';
import { AxeReport, printResult } from './axe_report';
// @ts-ignore JS that is run in browser as is
import { analyzeWithAxe, analyzeWithAxeWithClient } from './analyze_with_axe';

View file

@ -7,10 +7,7 @@
import 'cypress-real-events/support';
import { Interception } from 'cypress/types/net-stubbing';
import 'cypress-axe';
import {
AXE_CONFIG,
AXE_OPTIONS,
} from 'test/accessibility/services/a11y/constants';
import { AXE_CONFIG, AXE_OPTIONS } from '@kbn/test';
Cypress.Commands.add('loginAsReadOnlyUser', () => {
cy.loginAs({ username: 'apm_read_user', password: 'changeme' });

View file

@ -38,9 +38,11 @@ export const login = ({
* Cypress setup/helpers
*/
// eslint complains this should be in `dependencies` and not `devDependencies`, but these tests should only run on dev
// eslint-disable-next-line import/no-extraneous-dependencies
import 'cypress-axe'; // eslint complains this should be in `dependencies` and not `devDependencies`, but these tests should only run on dev
import { AXE_CONFIG, AXE_OPTIONS } from 'test/accessibility/services/a11y/constants';
import 'cypress-axe';
// eslint-disable-next-line import/no-extraneous-dependencies
import { AXE_CONFIG, AXE_OPTIONS } from '@kbn/test';
const axeConfig = {
...AXE_CONFIG,

View file

@ -0,0 +1,125 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { act } from 'react-dom/test-utils';
// import { expectToBeAccessible } from '@kbn/test-jest-helpers';
import { getA11yViolations } from '@kbn/test-jest-helpers';
import { IndicesTestBed, setup } from '../client_integration/home/indices_tab.helpers';
import {
indexMappings,
indexSettings,
indexStats,
setupEnvironment,
} from '../client_integration/helpers';
import {
createDataStreamBackingIndex,
createNonDataStreamIndex,
} from '../client_integration/home/data_streams_tab.helpers';
describe('A11y Indices tab', () => {
let testBed: IndicesTestBed;
let httpSetup: ReturnType<typeof setupEnvironment>['httpSetup'];
let httpRequestsMockHelpers: ReturnType<typeof setupEnvironment>['httpRequestsMockHelpers'];
beforeEach(() => {
const mockEnvironment = setupEnvironment();
httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers;
httpSetup = mockEnvironment.httpSetup;
});
it('when there are no indices', async () => {
httpRequestsMockHelpers.setLoadIndicesResponse([]);
await act(async () => {
testBed = await setup(httpSetup);
});
const { component } = testBed;
component.update();
// this is expected to fail and needs to be updated when EUI 52.0.0 is available in Kibana
// await expectToBeAccessible(component);
// until then check that only 1 expected violation is found
const violations = await getA11yViolations(component);
expect(violations).toHaveLength(1);
const { id } = violations[0];
expect(id).toEqual('aria-allowed-attr');
});
it('when there are indices', async () => {
httpRequestsMockHelpers.setLoadIndicesResponse([
createNonDataStreamIndex('non-data-stream-test-index'),
createDataStreamBackingIndex('data-stream-test-index', 'test-data-stream'),
]);
await act(async () => {
testBed = await setup(httpSetup);
});
const { component } = testBed;
component.update();
// this is expected to fail and needs to be updated when EUI 52.0.0 is available in Kibana
// await expectToBeAccessible(component);
// until then check that only 1 expected violation is found
const violations = await getA11yViolations(component);
expect(violations).toHaveLength(1);
const { id } = violations[0];
expect(id).toEqual('aria-allowed-attr');
});
describe('index details flyout', () => {
beforeEach(async () => {
httpRequestsMockHelpers.setLoadIndicesResponse([
createNonDataStreamIndex('non-data-stream-test-index'),
]);
httpRequestsMockHelpers.setLoadIndexSettingsResponse(indexSettings);
httpRequestsMockHelpers.setLoadIndexMappingResponse(indexMappings);
httpRequestsMockHelpers.setLoadIndexStatsResponse(indexStats);
await act(async () => {
testBed = await setup(httpSetup);
});
const { component, find } = testBed;
component.update();
find('indexTableIndexNameLink').at(0).simulate('click');
component.update();
});
it('summary tab', async () => {
const { component, find } = testBed;
expect(find('detailPanelTabSelected').text()).toEqual('Summary');
// this is expected to fail and needs to be updated when EUI 52.0.0 is available in Kibana
// await expectToBeAccessible(component);
// until then check that only 1 expected violation is found
const violations = await getA11yViolations(component);
expect(violations).toHaveLength(1);
const { id } = violations[0];
expect(id).toEqual('aria-allowed-attr');
});
['settings', 'mappings', 'stats'].forEach((tab) => {
it(`${tab} tab`, async () => {
const { component, find, actions } = testBed;
await actions.selectIndexDetailsTab(tab as 'settings');
expect(find('detailPanelTabSelected').text().toLowerCase()).toEqual(tab);
// this is expected to fail and needs to be updated when EUI 52.0.0 is available in Kibana
// await expectToBeAccessible(component);
// until then check that only 1 expected violation is found
const violations = await getA11yViolations(component);
expect(violations).toHaveLength(1);
const { id } = violations[0];
expect(id).toEqual('aria-allowed-attr');
});
});
it('edit settings tab', async () => {
const { component, find, actions } = testBed;
await actions.selectIndexDetailsTab('edit_settings');
expect(find('detailPanelTabSelected').text()).toEqual('Edit settings');
// this is expected to fail and needs to be updated when EUI 52.0.0 is available in Kibana
// await expectToBeAccessible(component);
// until then check that only 1 expected violation is found
const violations = await getA11yViolations(component);
expect(violations).toHaveLength(1);
const { id } = violations[0];
expect(id).toEqual('aria-allowed-attr');
});
});
});

View file

@ -0,0 +1,47 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export const indexSettings = {
settings: { index: { number_of_shards: '1' } },
defaults: { index: { flush_after_merge: '512mb' } },
};
export const indexMappings = {
mappings: {
dynamic: 'strict',
properties: {
'@timestamp': {
type: 'date',
},
},
},
};
export const indexStats = {
_shards: {
total: 1,
successful: 1,
failed: 0,
},
stats: {
uuid: 'test-uuid',
health: 'green',
status: 'open',
primaries: {
docs: {
count: 0,
deleted: 0,
},
},
total: {
docs: {
count: 0,
deleted: 0,
},
},
},
};

View file

@ -94,11 +94,14 @@ const registerHttpRequestMockHelpers = (
const setCreateTemplateResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('POST', `${API_BASE_PATH}/index_templates`, response, error);
const setUpdateTemplateResponse = (
templateId: string,
response?: HttpResponse,
error?: ResponseError
) => mockResponse('PUT', `${API_BASE_PATH}/index_templates/${templateId}`, response, error);
const setLoadIndexSettingsResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('GET', `${API_BASE_PATH}/settings/:name`, response, error);
const setLoadIndexMappingResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('GET', `${API_BASE_PATH}/mapping/:name`, response, error);
const setLoadIndexStatsResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('GET', `${API_BASE_PATH}/stats/:name`, response, error);
const setUpdateIndexSettingsResponse = (
indexName: string,
@ -128,7 +131,9 @@ const registerHttpRequestMockHelpers = (
setDeleteTemplateResponse,
setLoadTemplateResponse,
setCreateTemplateResponse,
setUpdateTemplateResponse,
setLoadIndexSettingsResponse,
setLoadIndexMappingResponse,
setLoadIndexStatsResponse,
setUpdateIndexSettingsResponse,
setSimulateTemplateResponse,
setLoadComponentTemplatesResponse,

View file

@ -18,3 +18,5 @@ export {
} from './setup_environment';
export type { TestSubjects } from './test_subjects';
export * from './fixtures';

View file

@ -62,4 +62,5 @@ export type TestSubjects =
| 'unfreezeIndexMenuButton'
| 'updateEditIndexSettingsButton'
| 'updateIndexSettingsErrorCallout'
| 'viewButton';
| 'viewButton'
| 'detailPanelTabSelected';

View file

@ -5826,6 +5826,14 @@
dependencies:
"@types/istanbul-lib-report" "*"
"@types/jest-axe@^3.5.3":
version "3.5.3"
resolved "https://registry.yarnpkg.com/@types/jest-axe/-/jest-axe-3.5.3.tgz#5af918553388aa0a448af75603b44093985778c6"
integrity sha512-ad9qI9f+00N8IlOuGh6dnZ6o0BDdV9VhGfTUr1zCejsPvOfZd6eohffe4JYxUoUuRYEftyMcaJ6Ux4+MsOpGHg==
dependencies:
"@types/jest" "*"
axe-core "^3.5.5"
"@types/jest-specific-snapshot@^0.5.3", "@types/jest-specific-snapshot@^0.5.5":
version "0.5.5"
resolved "https://registry.yarnpkg.com/@types/jest-specific-snapshot/-/jest-specific-snapshot-0.5.5.tgz#47ce738870be99898ed6d7b08dbf0240c74ae553"
@ -8447,6 +8455,16 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
axe-core@4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.2.1.tgz#2e50bcf10ee5b819014f6e342e41e45096239e34"
integrity sha512-evY7DN8qSIbsW2H/TWQ1bX3sXN1d4MNb5Vb4n7BzPuCwRHdkZ1H2eNLuSh73EoQqkGKUtju2G2HCcjCfhvZIAA==
axe-core@^3.5.5:
version "3.5.6"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.6.tgz#e762a90d7f6dbd244ceacb4e72760ff8aad521b5"
integrity sha512-LEUDjgmdJoA3LqklSTwKYqkjcZ4HKc4ddIYGSAiSkr46NTjzg2L9RNB+lekO9P7Dlpa87+hBtzc2Fzn/+GUWMQ==
axe-core@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.0.2.tgz#c7cf7378378a51fcd272d3c09668002a4990b1cb"
@ -9752,6 +9770,14 @@ chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@4.1.0, chalk@^4.0.0, chalk@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@4.1.2, chalk@^4.1.1, chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
@ -9779,14 +9805,6 @@ chalk@^3.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@^4.0.0, chalk@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174"
@ -12376,6 +12394,11 @@ diff-sequences@^27.0.6:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723"
integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==
diff-sequences@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327"
integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==
diff@5.0.0, diff@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
@ -17573,6 +17596,16 @@ jake@^10.6.1:
filelist "^1.0.1"
minimatch "^3.0.4"
jest-axe@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/jest-axe/-/jest-axe-5.0.1.tgz#26c43643b2e5f2bd4900c1ab36f8283635957a6e"
integrity sha512-MMOWA6gT4pcZGbTLS8ZEqABH08Lnj5bInfLPpn9ADWX2wFF++odbbh8csmSfkwKjHaioVPzlCtIypAtxFDx/rw==
dependencies:
axe-core "4.2.1"
chalk "4.1.0"
jest-matcher-utils "27.0.2"
lodash.merge "4.6.2"
jest-canvas-mock@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.3.1.tgz#9535d14bc18ccf1493be36ac37dd349928387826"
@ -17680,6 +17713,16 @@ jest-diff@^26.0.0, jest-diff@^26.6.2:
jest-get-type "^26.3.0"
pretty-format "^26.6.2"
jest-diff@^27.0.2:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def"
integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==
dependencies:
chalk "^4.0.0"
diff-sequences "^27.5.1"
jest-get-type "^27.5.1"
pretty-format "^27.5.1"
jest-diff@^27.2.0:
version "27.2.0"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.2.0.tgz#bda761c360f751bab1e7a2fe2fc2b0a35ce8518c"
@ -17752,6 +17795,11 @@ jest-get-type@^26.3.0:
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0"
integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==
jest-get-type@^27.0.1, jest-get-type@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1"
integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==
jest-get-type@^27.0.6:
version "27.0.6"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe"
@ -17810,6 +17858,16 @@ jest-leak-detector@^26.6.2:
jest-get-type "^26.3.0"
pretty-format "^26.6.2"
jest-matcher-utils@27.0.2:
version "27.0.2"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.0.2.tgz#f14c060605a95a466cdc759acc546c6f4cbfc4f0"
integrity sha512-Qczi5xnTNjkhcIB0Yy75Txt+Ez51xdhOxsukN7awzq2auZQGPHcQrJ623PZj0ECDEMOk2soxWx05EXdXGd1CbA==
dependencies:
chalk "^4.0.0"
jest-diff "^27.0.2"
jest-get-type "^27.0.1"
pretty-format "^27.0.2"
jest-matcher-utils@^24.9.0:
version "24.9.0"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073"
@ -19162,7 +19220,7 @@ lodash.memoize@~3.0.3:
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f"
integrity sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=
lodash.merge@^4.6.2:
lodash.merge@4.6.2, lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
@ -22889,6 +22947,15 @@ pretty-format@^26.0.0, pretty-format@^26.6.2:
ansi-styles "^4.0.0"
react-is "^17.0.1"
pretty-format@^27.0.2, pretty-format@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
dependencies:
ansi-regex "^5.0.1"
ansi-styles "^5.0.0"
react-is "^17.0.1"
pretty-format@^27.2.0:
version "27.2.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.2.0.tgz#ee37a94ce2a79765791a8649ae374d468c18ef19"