test: unit tests for commands/serve/web/connect.js file (#937)

* refactor: `connect()` and `openApp()`

* test: connect function covered

* chore: renaming jests test commands

* chore: adding override rule for non `commands/serve` directories to skip jest rules

* test: openApp() covered

* test: `getConnectionInfo()` covered

* test: covering parseEngineURL() and getParams()

* chore: switching jest test files to `.inspect` to prevent conflict with aw
This commit is contained in:
Ahmad Mirzaei
2022-09-27 13:21:17 +02:00
committed by GitHub
parent 5990d85a95
commit 58e375820c
7 changed files with 525 additions and 32 deletions

View File

@@ -0,0 +1,463 @@
import * as SDK from '@qlik/sdk';
import * as ENIGMA from 'enigma.js';
import qixSchema from 'enigma.js/schemas/12.936.0.json';
import * as SenseUtilities from 'enigma.js/sense-utilities';
import { connect, openApp, getConnectionInfo, getParams, parseEngineURL } from '../connect';
console.log(jest);
jest.mock('@qlik/sdk');
jest.mock('enigma.js');
jest.mock('enigma.js/sense-utilities');
describe('connect.js', () => {
let appId;
let authConfig = {};
let isAuthenticatedMock;
let authenticateMock;
let restCallMock;
let jsonResponseMock;
let generateWebsocketUrlMock;
beforeEach(() => {
appId = 'someAppId';
authConfig = {
authType: 'WebIntegration',
autoRedirect: 'true',
host: 'some.eu.tenant.pte.qlikdev.com',
webIntegrationId: 'someIntegrationId',
};
isAuthenticatedMock = jest.fn();
authenticateMock = jest.fn();
restCallMock = jest.fn();
jsonResponseMock = jest.fn();
generateWebsocketUrlMock = jest.fn();
jest.spyOn(global, 'fetch').mockImplementationOnce(() =>
Promise.resolve({
ok: true,
json: jsonResponseMock,
})
);
SDK.Auth.mockImplementation(() => ({
config: authConfig,
isAuthenticated: isAuthenticatedMock,
authenticate: authenticateMock,
rest: restCallMock,
generateWebsocketUrl: generateWebsocketUrlMock,
}));
});
afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});
describe('connect()', () => {
test('should throw error if fails to get enigma instance', async () => {
jsonResponseMock.mockImplementation(() => Promise.reject(new Error('failed')));
try {
await connect();
} catch (err) {
expect(err.message).toBe('Failed to return enigma instance');
}
});
describe('connectiong with `webIntegrationId` flow', () => {
beforeEach(() => {
jsonResponseMock.mockImplementation(() =>
Promise.resolve({
webIntegrationId: 'someIntegrationId',
enigma: {
secure: false,
host: 'some.eu.tenant.pte.qlikdev.com',
},
})
);
});
test('should not call `Auth.authenticate()` if user is already logged in', async () => {
isAuthenticatedMock.mockImplementationOnce(() => true);
await connect();
expect(SDK.Auth).toHaveBeenCalledTimes(1);
expect(isAuthenticatedMock).toHaveBeenCalledTimes(1);
expect(authenticateMock).toHaveBeenCalledTimes(0);
});
test('should call `Auth.authenticate()` if user is not logged in', async () => {
isAuthenticatedMock.mockImplementationOnce(() => false);
await connect();
expect(SDK.Auth).toHaveBeenCalledTimes(1);
expect(isAuthenticatedMock).toHaveBeenCalledTimes(1);
expect(authenticateMock).toHaveBeenCalledTimes(1);
});
test('should return `getDocList()` and `getConfiguration()`', async () => {
const result = await connect();
expect(result).toMatchObject({
getDocList: expect.any(Function),
getConfiguration: expect.any(Function),
});
});
});
describe('connectiong with Local Engine flow', () => {
let connectionResponse = {};
let buildUrlMock;
let enigmaOpenMock;
let enigmaCreateMock;
beforeEach(() => {
connectionResponse = {
webIntegrationId: undefined,
enigma: {
secure: false,
host: 'localhost:1234/app/engineData',
},
};
jsonResponseMock.mockImplementation(() => Promise.resolve(connectionResponse));
buildUrlMock = jest.fn().mockReturnValue(`ws://${connectionResponse.enigma.host}`);
jest.spyOn(SenseUtilities, 'buildUrl').mockImplementation(buildUrlMock);
enigmaOpenMock = jest.fn();
enigmaCreateMock = jest.fn().mockReturnValue({
open: enigmaOpenMock,
});
ENIGMA.create.mockImplementation(enigmaCreateMock);
});
afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});
test('should call buildUrl with proper arguments', async () => {
await connect();
expect(buildUrlMock).toHaveBeenCalledTimes(1);
expect(buildUrlMock).toHaveBeenCalledWith(connectionResponse.enigma);
});
test('should call `enigma.create` with proper arguments', async () => {
await connect();
expect(enigmaCreateMock).toHaveBeenCalledTimes(1);
expect(enigmaCreateMock).toHaveBeenCalledWith({
schema: qixSchema,
url: `ws://${connectionResponse.enigma.host}`,
});
});
test('should call `enigma.create().open()` one time', async () => {
await connect();
expect(enigmaOpenMock).toHaveBeenCalledTimes(1);
});
});
});
describe('openApp()', () => {
test('should throw error if fails to open app', async () => {
jsonResponseMock.mockImplementation(() => Promise.reject(new Error('failed')));
try {
await openApp();
} catch (err) {
expect(err.message).toBe('Failed to open app!');
}
});
describe('Open app flows', () => {
let connectionResponse = {};
let buildUrlMock;
let enigmaOpenDocMock;
let enigmaOpenMock;
let enigmaCreateMock;
beforeEach(() => {
enigmaOpenDocMock = jest.fn();
enigmaOpenMock = jest.fn().mockReturnValue({
openDoc: enigmaOpenDocMock,
});
enigmaCreateMock = jest.fn().mockReturnValue({
open: enigmaOpenMock,
});
ENIGMA.create.mockImplementation(enigmaCreateMock);
});
describe('open app with `webIntegrationId` flow', () => {
let webSocketURL;
beforeEach(() => {
jsonResponseMock.mockImplementation(() =>
Promise.resolve({
webIntegrationId: 'someIntegrationId',
enigma: {
secure: false,
host: 'some.eu.tenant.pte.qlikdev.com',
},
})
);
webSocketURL = `wss://${authConfig.host}/apps/${appId}?qlik-csrf-token=SOME_CSRF_TOKEN&qlik-web-integration-id=${authConfig.webIntegrationId}`;
generateWebsocketUrlMock.mockReturnValue(webSocketURL);
});
test('should not call `Auth.authenticate()` if user is already logged in', async () => {
isAuthenticatedMock.mockImplementationOnce(() => true);
await openApp(appId);
expect(SDK.Auth).toHaveBeenCalledTimes(1);
expect(isAuthenticatedMock).toHaveBeenCalledTimes(1);
expect(authenticateMock).toHaveBeenCalledTimes(0);
});
test('should call `Auth.authenticate()` if user is not logged in', async () => {
isAuthenticatedMock.mockImplementationOnce(() => false);
await openApp(appId);
expect(SDK.Auth).toHaveBeenCalledTimes(1);
expect(isAuthenticatedMock).toHaveBeenCalledTimes(1);
expect(authenticateMock).toHaveBeenCalledTimes(1);
});
test('should call `enigma.create` with proper arguments', async () => {
await openApp(appId);
expect(enigmaCreateMock).toHaveBeenCalledTimes(1);
expect(enigmaCreateMock).toHaveBeenCalledWith({
schema: qixSchema,
url: webSocketURL,
});
});
test('should call `enigma.create().open()` one time', async () => {
await openApp(appId);
expect(enigmaOpenMock).toHaveBeenCalledTimes(1);
});
test('should call `enigma.create().open().openDoc()` one time with proper app id', async () => {
await openApp(appId);
expect(enigmaOpenDocMock).toHaveBeenCalledTimes(1);
expect(enigmaOpenDocMock).toHaveBeenCalledWith(appId);
});
});
describe('with Local engine flow', () => {
beforeEach(() => {
connectionResponse = {
webIntegrationId: undefined,
enigma: {
secure: false,
host: 'localhost:1234/app/engineData',
},
};
jsonResponseMock.mockImplementation(() => Promise.resolve(connectionResponse));
buildUrlMock = jest.fn().mockReturnValue(`ws://${connectionResponse.enigma.host}`);
jest.spyOn(SenseUtilities, 'buildUrl').mockImplementation(buildUrlMock);
});
test('should call buildUrl with proper arguments', async () => {
await openApp(appId);
expect(buildUrlMock).toHaveBeenCalledTimes(1);
expect(buildUrlMock).toHaveBeenCalledWith(connectionResponse.enigma);
});
test('should call `enigma.create` with proper arguments', async () => {
await openApp(appId);
expect(enigmaCreateMock).toHaveBeenCalledTimes(1);
expect(enigmaCreateMock).toHaveBeenCalledWith({
schema: qixSchema,
url: `ws://${connectionResponse.enigma.host}`,
});
});
test('should call `enigma.create().open()` one time', async () => {
await openApp(appId);
expect(enigmaOpenMock).toHaveBeenCalledTimes(1);
});
test('should call `enigma.create().open().openDoc()` one time with proper app id', async () => {
await openApp(appId);
expect(enigmaOpenDocMock).toHaveBeenCalledTimes(1);
expect(enigmaOpenDocMock).toHaveBeenCalledWith(appId);
});
});
});
});
describe('getConnectionInfo()', () => {
beforeEach(() => {
jsonResponseMock.mockImplementation(() =>
Promise.resolve({
webIntegrationId: 'someIntegrationId',
enigma: {
secure: false,
host: 'some.eu.tenant.pte.qlikdev.com',
},
})
);
});
test('should parse engine url while connecting into SDE', async () => {
window.location.assign(
`/some-url?engine_url=wss://${authConfig.host}&qlik-web-integration-id=${authConfig.webIntegrationId}`
);
const result = await getConnectionInfo();
expect(result).toEqual(
expect.objectContaining({
appUrl: undefined,
enigma: expect.objectContaining({
secure: true, // since it is "wss"
host: authConfig.host,
port: undefined, // since we are providing link
appId: undefined, // since we are providing link
}),
engineUrl: `wss://${authConfig.host}`,
rootPath: `https://${authConfig.host}`,
})
);
});
test('should pass `qlik-web-integration-id` if it is provided in url', async () => {
window.location.assign(
`/some-url?engine_url=wss://${authConfig.host}&qlik-web-integration-id=${authConfig.webIntegrationId}`
);
const result = await getConnectionInfo();
expect(result).toEqual(
expect.objectContaining({
appUrl: undefined,
webIntegrationId: authConfig.webIntegrationId,
engineUrl: `wss://${authConfig.host}`,
rootPath: `https://${authConfig.host}`,
})
);
});
test('should parse engine url while connecting to local docker engine', async () => {
window.location.assign('/?engine_url=ws://localhost:1234');
const result = await getConnectionInfo();
expect(result).toEqual(
expect.objectContaining({
enigma: expect.objectContaining({
secure: false, // since it is "ws"
host: 'localhost',
port: '1234',
appId: undefined, // there is no appId
}),
engineUrl: 'ws://localhost:1234',
appUrl: undefined,
})
);
});
test('should pass appId if it is provided in url', async () => {
window.location.assign('/?app=SOME_APP_ID');
const result = await getConnectionInfo();
expect(result).toEqual(
expect.objectContaining({
enigma: expect.objectContaining({
secure: false, // since it is "ws"
host: authConfig.host,
appId: 'SOME_APP_ID',
}),
webIntegrationId: authConfig.webIntegrationId,
})
);
});
test('should return invalid if url was not matching anything', async () => {
jsonResponseMock.mockImplementation(() =>
Promise.resolve({
invalid: true,
})
);
const result = await getConnectionInfo();
expect(result).toEqual({
invalid: true,
});
});
});
describe('getParams()', () => {
beforeEach(() => {
jsonResponseMock.mockImplementation(() =>
Promise.resolve({
webIntegrationId: 'someIntegrationId',
enigma: {
secure: false,
host: 'some.eu.tenant.pte.qlikdev.com',
},
})
);
});
test('should detect engine url and integration id from provided link', () => {
window.location.assign(
`/some-url?engine_url=wss://${authConfig.host}&qlik-web-integration-id=${authConfig.webIntegrationId}`
);
expect(getParams()).toEqual({
engine_url: `wss://${authConfig.host}`,
'qlik-web-integration-id': authConfig.webIntegrationId,
});
});
test('should separate columns in to an array in case of any `cols` params in link', () => {
window.location.assign('/?engine_url=ws://localhost:1234&cols=col_01,col_02,col_03');
expect(getParams()).toEqual({
engine_url: 'ws://localhost:1234',
cols: ['col_01', 'col_02', 'col_03'],
});
});
});
describe('parseEngineURL()', () => {
let url;
test('should return invalid object if there was no match', () => {
url = '/?someParam=https://localhost:1234';
const result = parseEngineURL(url);
expect(result).toEqual({
engineUrl: url,
invalid: true,
});
});
test('should return expected info from SDE url', () => {
url = `/some-url?engine_url=wss://${authConfig.host}&qlik-web-integration-id=${authConfig.webIntegrationId}`;
const result = parseEngineURL(url);
expect(result).toMatchObject({
enigma: expect.objectContaining({
secure: expect.any(Boolean),
host: expect.any(String),
port: undefined, // because of providing a link
appId: undefined, // since there is not appid in link
}),
engineUrl: expect.any(String),
appUrl: undefined, // because no app has been provided
});
});
test('should return expected info from Local Docker url', () => {
url = '/?engine_url=ws://localhost:1234';
const result = parseEngineURL(url);
expect(result).toMatchObject({
enigma: expect.objectContaining({
secure: expect.any(Boolean),
host: expect.any(String),
port: expect.any(String),
appId: undefined, // since there is not appid in link
}),
engineUrl: expect.any(String),
appUrl: undefined, // because no app has been provided
});
});
test('should find app url param from provided link', () => {
url = `/app/SOME_APP_ID/?engine_url=wss://${authConfig.host}`;
const result = parseEngineURL(url);
expect(result).toMatchObject({
enigma: expect.objectContaining({
secure: expect.any(Boolean),
host: expect.any(String),
port: undefined, // because of providing a link
appId: 'SOME_APP_ID/', // since there is not appid in link
}),
engineUrl: expect.any(String),
appUrl: url,
});
});
});
});

View File

@@ -3,7 +3,7 @@ import qixSchema from 'enigma.js/schemas/12.936.0.json';
import SenseUtilities from 'enigma.js/sense-utilities';
import { Auth, AuthType } from '@qlik/sdk';
const params = (() => {
const getParams = () => {
const opts = {};
window.location.search
.substring(1)
@@ -17,19 +17,16 @@ const params = (() => {
}
opts[name] = value;
});
return opts;
})();
};
// Qlik Core: ws://<host>:<port>/app/<data-folder>/<app-name>
// QCS: wss://<tenant-url>.<region>.qlikcloud.com/app/<app-GUID>
// QSEoK: wss://<host>/app/<app-GUID>
// QSEoW: wss://<host>/<virtual-proxy-prefix>/app/<app-GUID>
const RX = /(wss?):\/\/([^/:?&]+)(?::(\d+))?/;
const parseEngineURL = (url) => {
const m = RX.exec(url);
if (!m) {
const parseEngineURL = (url, urlRegex = /(wss?):\/\/([^/:?&]+)(?::(\d+))?/, appRegex = /\/app\/([^?&#:]+)/) => {
const match = urlRegex.exec(url);
if (!match) {
return {
engineUrl: url,
invalid: true,
@@ -41,19 +38,19 @@ const parseEngineURL = (url) => {
let engineUrl = trimmedUrl;
let appUrl;
const rxApp = /\/app\/([^?&#:]+)/.exec(trimmedUrl);
const appMatch = appRegex.exec(trimmedUrl);
if (rxApp) {
[, appId] = rxApp;
engineUrl = trimmedUrl.substring(0, rxApp.index);
if (appMatch) {
[, appId] = appMatch;
engineUrl = trimmedUrl.substring(0, appMatch.index);
appUrl = trimmedUrl;
}
return {
enigma: {
secure: m[1] === 'wss',
host: m[2],
port: m[3] || undefined,
secure: match[1] === 'wss',
host: match[2],
port: match[3] || undefined,
appId,
},
engineUrl,
@@ -66,6 +63,7 @@ const getConnectionInfo = () =>
.then((response) => response.json())
.then(async (n) => {
let info = n;
const params = getParams();
if (params.engine_url) {
info = {
...info,
@@ -151,26 +149,19 @@ const openApp = async (id) => {
enigma: { host },
} = await getConnectionInfo();
let url = '';
if (webIntegrationId) {
const authInstance = getAuthInstance({ webIntegrationId, host });
const url = await authInstance.generateWebsocketUrl(id);
const enigmaGlobal = await enigma.create({ schema: qixSchema, url }).open();
return enigmaGlobal.openDoc(id);
url = await authInstance.generateWebsocketUrl(id);
} else {
url = SenseUtilities.buildUrl(enigmaInfo);
}
const url = SenseUtilities.buildUrl({
secure: false,
...enigmaInfo,
urlParams: {},
appId: id,
});
return enigma
.create({ schema: qixSchema, url })
.open()
.then((global) => global.openDoc(id));
const enigmaGlobal = await enigma.create({ schema: qixSchema, url }).open();
return enigmaGlobal.openDoc(id);
} catch (error) {
throw new Error('Failed to open app!');
}
};
export { connect, openApp, params, getConnectionInfo, getAuthInstance };
export { connect, openApp, getParams, getConnectionInfo, getAuthInstance, parseEngineURL };

View File

@@ -2,7 +2,7 @@
import { embed } from '@nebula.js/stardust';
import snapshooter from '@nebula.js/snapshooter/client';
import { openApp, params, getConnectionInfo } from './connect';
import { openApp, getParams, getConnectionInfo } from './connect';
import initiateWatch from './hot';
import renderFixture from './render-fixture';
@@ -36,6 +36,7 @@ async function renderWithEngine() {
if (!info.enigma.appId) {
location.href = location.origin; //eslint-disable-line
}
const params = getParams();
const app = await openApp(info.enigma.appId);
const nebbie = await nuke({ app, ...info, theme: params.theme, language: params.language });
const element = document.querySelector('#chart-container');
@@ -107,6 +108,7 @@ async function renderSnapshot() {
});
window.onHotChange(supernova.name, async () => {
const params = getParams();
snapshooter({
embed: n,
element,
@@ -116,6 +118,7 @@ async function renderSnapshot() {
}
function render() {
const params = getParams();
if (params.fixture) {
renderFixture(params);
} else if (params.snapshot) {

View File

@@ -1,12 +1,13 @@
module.exports = {
clearMocks: true,
testEnvironment: 'jest-environment-jsdom',
testRegex: ['commands/serve/web/.+\\.(test|spec)\\.[jt]sx?$'],
testRegex: ['commands/serve/web/.+\\.(spec|test|inspect)\\.[jt]sx?$'],
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
collectCoverageFrom: [
'commands/serve/web/**/*.{js,jsx}',
'!commands/serve/web/**/*.spec.{js,jsx}',
'!commands/serve/web/**/*.test.{js,jsx}',
'!commands/serve/web/**/*.inspect.{js,jsx}',
'!commands/serve/web/**/__tests__/**/*',
'!**/dist/**',

View File

@@ -1,2 +1,3 @@
import '@testing-library/jest-dom';
import 'whatwg-fetch';
import 'jest-location-mock';

View File

@@ -79,6 +79,7 @@
"husky": "8.0.1",
"jest": "^29.0.3",
"jest-environment-jsdom": "^29.0.3",
"jest-location-mock": "^1.0.9",
"jimp": "0.16.1",
"lerna": "5.5.0",
"lint-staged": "13.0.3",

View File

@@ -2800,6 +2800,11 @@
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
"@jedmao/location@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@jedmao/location/-/location-3.0.0.tgz#f2b24e937386f95252f3a1fefbf7ca2e0a4b87e9"
integrity sha512-p7mzNlgJbCioUYLUEKds3cQG4CHONVFJNYqMe6ocEtENCL/jYmMo1Q3ApwsMmU+L0ZkaDJEyv4HokaByLoPwlQ==
"@jest/console@^29.0.3":
version "29.0.3"
resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.0.3.tgz#a222ab87e399317a89db88a58eaec289519e807a"
@@ -9919,6 +9924,11 @@ diff-sequences@^25.2.6:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd"
integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==
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-sequences@^29.0.0:
version "29.0.0"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.0.0.tgz#bae49972ef3933556bcb0800b72e8579d19d9e4f"
@@ -13510,6 +13520,16 @@ jest-diff@^25.1.0, jest-diff@^25.5.0:
jest-get-type "^25.2.6"
pretty-format "^25.5.0"
jest-diff@^27.0.1:
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@^29.0.3:
version "29.0.3"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.0.3.tgz#41cc02409ad1458ae1bf7684129a3da2856341ac"
@@ -13569,6 +13589,11 @@ jest-get-type@^25.1.0, jest-get-type@^25.2.6:
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877"
integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==
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@^29.0.0:
version "29.0.0"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.0.0.tgz#843f6c50a1b778f7325df1129a0fd7aa713aef80"
@@ -13622,6 +13647,14 @@ jest-leak-detector@^29.0.3:
jest-get-type "^29.0.0"
pretty-format "^29.0.3"
jest-location-mock@^1.0.9:
version "1.0.9"
resolved "https://registry.yarnpkg.com/jest-location-mock/-/jest-location-mock-1.0.9.tgz#f4466362423b273e12ca3716467a3d478ce78fa8"
integrity sha512-DN/v7Zsa3N4uGgWTCrMrPPxhZORr/4N5gi+u7Tk6sLdORYplrC0//wfFN5FOtx4ZdQzDVfY6rLa4d+wfTKzQHw==
dependencies:
"@jedmao/location" "^3.0.0"
jest-diff "^27.0.1"
jest-matcher-utils@^25.1.0, jest-matcher-utils@^25.5.0:
version "25.5.0"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.5.0.tgz#fbc98a12d730e5d2453d7f1ed4a4d948e34b7867"
@@ -17269,7 +17302,7 @@ pretty-format@^25.1.0, pretty-format@^25.5.0:
ansi-styles "^4.0.0"
react-is "^16.12.0"
pretty-format@^27.0.2:
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==