mirror of
https://github.com/hanzoai/jest.git
synced 2026-09-24 16:59:57 +00:00
feat: split some jest-snapshot utility functions to its own package @jest/snapshot-utils (#15095)
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
- `[@jest/types]` Improve argument type inference passed to `test` and `describe` callback functions from `each` tables ([#14920](https://github.com/jestjs/jest/pull/14920))
|
||||
- `[jest-snapshot]` [**BREAKING**] Add support for [Error causes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) in snapshots ([#13965](https://github.com/facebook/jest/pull/13965))
|
||||
- `[jest-snapshot]` Support Prettier 3 ([#14566](https://github.com/facebook/jest/pull/14566))
|
||||
- `[@jest/util-snapshot]` Extract utils used by tooling from `jest-snapshot` into its own package ([#15095](https://github.com/facebook/jest/pull/15095))
|
||||
- `[pretty-format]` [**BREAKING**] Do not render empty string children (`''`) in React plugin ([#14470](https://github.com/facebook/jest/pull/14470))
|
||||
|
||||
### Fixes
|
||||
|
||||
@@ -18,7 +18,7 @@ afterEach(() => cleanup(DIR));
|
||||
describe('--findRelatedTests flag', () => {
|
||||
test('runs tests related to filename', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'__tests__/test.test.js': `
|
||||
const a = require('../a');
|
||||
test('a', () => {});
|
||||
@@ -44,7 +44,7 @@ describe('--findRelatedTests flag', () => {
|
||||
}
|
||||
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'__tests__/test.test.js': `
|
||||
const a = require('../a');
|
||||
test('a', () => {});
|
||||
@@ -65,7 +65,7 @@ describe('--findRelatedTests flag', () => {
|
||||
|
||||
test('runs tests related to filename with a custom dependency extractor', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'__tests__/test-skip-deps.test.js': `
|
||||
const dynamicImport = path => Promise.resolve(require(path));
|
||||
test('a', () => dynamicImport('../a').then(a => {
|
||||
@@ -118,7 +118,7 @@ describe('--findRelatedTests flag', () => {
|
||||
|
||||
test('runs tests related to filename with a custom dependency extractor written in ESM', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'__tests__/test-skip-deps.test.js': `
|
||||
const dynamicImport = path => Promise.resolve(require(path));
|
||||
test('a', () => dynamicImport('../a').then(a => {
|
||||
@@ -168,7 +168,7 @@ describe('--findRelatedTests flag', () => {
|
||||
|
||||
test('generates coverage report for filename', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'__tests__/a.test.js': `
|
||||
require('../a');
|
||||
require('../b');
|
||||
@@ -219,7 +219,7 @@ describe('--findRelatedTests flag', () => {
|
||||
|
||||
test('coverage configuration is applied correctly', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'__tests__/a.test.js': `
|
||||
require('../a');
|
||||
test('a', () => expect(1).toBe(1));
|
||||
|
||||
@@ -19,7 +19,7 @@ afterEach(() => cleanup(DIR));
|
||||
|
||||
test("--listTests doesn't duplicate the test files", () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'/project1.js': "module.exports = {rootDir: './', displayName: 'BACKEND'}",
|
||||
'/project2.js': "module.exports = {rootDir: './', displayName: 'BACKEND'}",
|
||||
'__tests__/inBothProjectsTest.js': "test('test', () => {});",
|
||||
@@ -35,7 +35,7 @@ test("--listTests doesn't duplicate the test files", () => {
|
||||
|
||||
test('can pass projects or global config', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'base_config.js': `
|
||||
module.exports = {
|
||||
haste: {
|
||||
@@ -132,7 +132,7 @@ test('can pass projects or global config', () => {
|
||||
|
||||
test('"No tests found" message for projects', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'package.json': '{}',
|
||||
'project1/__tests__/file1.test.js': `
|
||||
const file1 = require('../file1');
|
||||
@@ -336,7 +336,7 @@ test('allows a single project', () => {
|
||||
|
||||
test('resolves projects and their <rootDir> properly', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'package.json': JSON.stringify({
|
||||
jest: {
|
||||
projects: [
|
||||
@@ -436,7 +436,7 @@ test('resolves projects and their <rootDir> properly', () => {
|
||||
|
||||
test('Does transform files with the corresponding project transformer', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'file.js': SAMPLE_FILE_CONTENT,
|
||||
'package.json': '{}',
|
||||
'project1/__tests__/project1.test.js': `
|
||||
@@ -487,7 +487,7 @@ test('Does transform files with the corresponding project transformer', () => {
|
||||
describe("doesn't bleed module file extensions resolution with multiple workers", () => {
|
||||
test('external config files', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'file.js': 'module.exports = "file1"',
|
||||
'file.p2.js': 'module.exports = "file2"',
|
||||
'package.json': '{}',
|
||||
@@ -537,7 +537,7 @@ describe("doesn't bleed module file extensions resolution with multiple workers"
|
||||
|
||||
test('inline config files', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'file.js': 'module.exports = "file1"',
|
||||
'file.p2.js': 'module.exports = "file2"',
|
||||
'package.json': JSON.stringify({
|
||||
|
||||
@@ -17,7 +17,7 @@ afterEach(() => cleanup(DIR));
|
||||
|
||||
test('triggers unexpected token error message for non-JS assets', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'asset.css': '.style {}',
|
||||
'package.json': JSON.stringify({jest: {testEnvironment: 'node'}}),
|
||||
});
|
||||
@@ -37,7 +37,7 @@ test('triggers unexpected token error message for non-JS assets', () => {
|
||||
|
||||
test('triggers unexpected token error message for untranspiled node_modules', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'node_modules/untranspiled-module': 'import {module} from "some-module"',
|
||||
'package.json': JSON.stringify({jest: {testEnvironment: 'node'}}),
|
||||
});
|
||||
@@ -59,7 +59,7 @@ test('triggers unexpected token error message for untranspiled node_modules', ()
|
||||
|
||||
test('does not trigger unexpected token error message for regular syntax errors', () => {
|
||||
writeFiles(DIR, {
|
||||
'.watchmanconfig': '',
|
||||
'.watchmanconfig': '{}',
|
||||
'faulty.js': 'import {module from "some-module"',
|
||||
'faulty2.js': 'const name = {first: "Name" second: "Second"}',
|
||||
'package.json': JSON.stringify({jest: {testEnvironment: 'node'}}),
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "@jest/snapshot-utils",
|
||||
"version": "30.0.0-alpha.4",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jestjs/jest.git",
|
||||
"directory": "packages/jest-snapshot-utils"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "./build/index.js",
|
||||
"types": "./build/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./build/index.d.ts",
|
||||
"require": "./build/index.js",
|
||||
"import": "./build/index.mjs",
|
||||
"default": "./build/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jest/types": "workspace:*",
|
||||
"chalk": "^4.0.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"natural-compare": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/graceful-fs": "^4.1.3",
|
||||
"@types/natural-compare": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.10.0 || ^18.12.0 || >=20.0.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
jest.mock('graceful-fs', () => ({
|
||||
...jest.createMockFromModule<typeof import('fs')>('fs'),
|
||||
existsSync: jest.fn().mockReturnValue(true),
|
||||
}));
|
||||
|
||||
import * as path from 'path';
|
||||
import chalk = require('chalk');
|
||||
import * as fs from 'graceful-fs';
|
||||
import {
|
||||
SNAPSHOT_GUIDE_LINK,
|
||||
SNAPSHOT_VERSION,
|
||||
SNAPSHOT_VERSION_WARNING,
|
||||
getSnapshotData,
|
||||
keyToTestName,
|
||||
saveSnapshotFile,
|
||||
testNameToKey,
|
||||
} from '../utils';
|
||||
|
||||
test('keyToTestName()', () => {
|
||||
expect(keyToTestName('abc cde 12')).toBe('abc cde');
|
||||
expect(keyToTestName('abc cde 12')).toBe('abc cde ');
|
||||
expect(() => keyToTestName('abc cde')).toThrow(
|
||||
'Snapshot keys must end with a number.',
|
||||
);
|
||||
});
|
||||
|
||||
test('testNameToKey', () => {
|
||||
expect(testNameToKey('abc cde', 1)).toBe('abc cde 1');
|
||||
expect(testNameToKey('abc cde ', 12)).toBe('abc cde 12');
|
||||
});
|
||||
|
||||
test('saveSnapshotFile() works with \r\n', () => {
|
||||
const filename = path.join(__dirname, 'remove-newlines.snap');
|
||||
const data = {
|
||||
myKey: '<div>\r\n</div>',
|
||||
};
|
||||
|
||||
saveSnapshotFile(data, filename);
|
||||
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
||||
filename,
|
||||
`// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` +
|
||||
'exports[`myKey`] = `<div>\n</div>`;\n',
|
||||
);
|
||||
});
|
||||
|
||||
test('saveSnapshotFile() works with \r', () => {
|
||||
const filename = path.join(__dirname, 'remove-newlines.snap');
|
||||
const data = {
|
||||
myKey: '<div>\r</div>',
|
||||
};
|
||||
|
||||
saveSnapshotFile(data, filename);
|
||||
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
||||
filename,
|
||||
`// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` +
|
||||
'exports[`myKey`] = `<div>\n</div>`;\n',
|
||||
);
|
||||
});
|
||||
|
||||
test('getSnapshotData() throws when no snapshot version', () => {
|
||||
const filename = path.join(__dirname, 'old-snapshot.snap');
|
||||
jest
|
||||
.mocked(fs.readFileSync)
|
||||
.mockReturnValue('exports[`myKey`] = `<div>\n</div>`;\n');
|
||||
const update = 'none';
|
||||
|
||||
expect(() => getSnapshotData(filename, update)).toThrow(
|
||||
chalk.red(
|
||||
`${chalk.bold('Outdated snapshot')}: No snapshot header found. ` +
|
||||
'Jest 19 introduced versioned snapshots to ensure all developers on ' +
|
||||
'a project are using the same version of Jest. ' +
|
||||
'Please update all snapshots during this upgrade of Jest.\n\n',
|
||||
) + SNAPSHOT_VERSION_WARNING,
|
||||
);
|
||||
});
|
||||
|
||||
test('getSnapshotData() throws for older snapshot version', () => {
|
||||
const filename = path.join(__dirname, 'old-snapshot.snap');
|
||||
jest
|
||||
.mocked(fs.readFileSync)
|
||||
.mockReturnValue(
|
||||
`// Jest Snapshot v0.99, ${SNAPSHOT_GUIDE_LINK}\n\n` +
|
||||
'exports[`myKey`] = `<div>\n</div>`;\n',
|
||||
);
|
||||
const update = 'none';
|
||||
|
||||
expect(() => getSnapshotData(filename, update)).toThrow(
|
||||
`${chalk.red(
|
||||
`${chalk.red.bold('Outdated snapshot')}: The version of the snapshot ` +
|
||||
'file associated with this test is outdated. The snapshot file ' +
|
||||
'version ensures that all developers on a project are using ' +
|
||||
'the same version of Jest. ' +
|
||||
'Please update all snapshots during this upgrade of Jest.',
|
||||
)}\n\nExpected: v${SNAPSHOT_VERSION}\n` +
|
||||
`Received: v0.99\n\n${SNAPSHOT_VERSION_WARNING}`,
|
||||
);
|
||||
});
|
||||
|
||||
test('getSnapshotData() throws for newer snapshot version', () => {
|
||||
const filename = path.join(__dirname, 'old-snapshot.snap');
|
||||
jest
|
||||
.mocked(fs.readFileSync)
|
||||
.mockReturnValue(
|
||||
`// Jest Snapshot v2, ${SNAPSHOT_GUIDE_LINK}\n\n` +
|
||||
'exports[`myKey`] = `<div>\n</div>`;\n',
|
||||
);
|
||||
const update = 'none';
|
||||
|
||||
expect(() => getSnapshotData(filename, update)).toThrow(
|
||||
`${chalk.red(
|
||||
`${chalk.red.bold('Outdated Jest version')}: The version of this ` +
|
||||
'snapshot file indicates that this project is meant to be used ' +
|
||||
'with a newer version of Jest. ' +
|
||||
'The snapshot file version ensures that all developers on a project ' +
|
||||
'are using the same version of Jest. ' +
|
||||
'Please update your version of Jest and re-run the tests.',
|
||||
)}\n\nExpected: v${SNAPSHOT_VERSION}\nReceived: v2`,
|
||||
);
|
||||
});
|
||||
|
||||
test('getSnapshotData() does not throw for when updating', () => {
|
||||
const filename = path.join(__dirname, 'old-snapshot.snap');
|
||||
jest
|
||||
.mocked(fs.readFileSync)
|
||||
.mockReturnValue('exports[`myKey`] = `<div>\n</div>`;\n');
|
||||
const update = 'all';
|
||||
|
||||
expect(() => getSnapshotData(filename, update)).not.toThrow();
|
||||
});
|
||||
|
||||
test('getSnapshotData() marks invalid snapshot dirty when updating', () => {
|
||||
const filename = path.join(__dirname, 'old-snapshot.snap');
|
||||
jest
|
||||
.mocked(fs.readFileSync)
|
||||
.mockReturnValue('exports[`myKey`] = `<div>\n</div>`;\n');
|
||||
const update = 'all';
|
||||
|
||||
expect(getSnapshotData(filename, update)).toMatchObject({dirty: true});
|
||||
});
|
||||
|
||||
test('getSnapshotData() marks valid snapshot not dirty when updating', () => {
|
||||
const filename = path.join(__dirname, 'old-snapshot.snap');
|
||||
jest
|
||||
.mocked(fs.readFileSync)
|
||||
.mockReturnValue(
|
||||
`// Jest Snapshot v${SNAPSHOT_VERSION}, ${SNAPSHOT_GUIDE_LINK}\n\n` +
|
||||
'exports[`myKey`] = `<div>\n</div>`;\n',
|
||||
);
|
||||
const update = 'all';
|
||||
|
||||
expect(getSnapshotData(filename, update)).toMatchObject({dirty: false});
|
||||
});
|
||||
|
||||
test('escaping', () => {
|
||||
const filename = path.join(__dirname, 'escaping.snap');
|
||||
const data = '"\'\\';
|
||||
const writeFileSync = jest.mocked(fs.writeFileSync);
|
||||
|
||||
writeFileSync.mockReset();
|
||||
saveSnapshotFile({key: data}, filename);
|
||||
const writtenData = writeFileSync.mock.calls[0][1];
|
||||
expect(writtenData).toBe(
|
||||
`// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` +
|
||||
'exports[`key`] = `"\'\\\\`;\n',
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-eval
|
||||
const readData = eval(`var exports = {}; ${writtenData} exports`);
|
||||
expect(readData).toEqual({key: data});
|
||||
const snapshotData = readData.key;
|
||||
expect(data).toEqual(snapshotData);
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
export * from './utils';
|
||||
export * from './types';
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
export type SnapshotData = Record<string, string>;
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import * as path from 'path';
|
||||
import chalk = require('chalk');
|
||||
import * as fs from 'graceful-fs';
|
||||
import naturalCompare = require('natural-compare');
|
||||
import type {Config} from '@jest/types';
|
||||
import type {SnapshotData} from './types';
|
||||
|
||||
export const SNAPSHOT_VERSION = '1';
|
||||
const SNAPSHOT_VERSION_REGEXP = /^\/\/ Jest Snapshot v(.+),/;
|
||||
export const SNAPSHOT_GUIDE_LINK = 'https://goo.gl/fbAQLP';
|
||||
export const SNAPSHOT_VERSION_WARNING = chalk.yellow(
|
||||
`${chalk.bold('Warning')}: Before you upgrade snapshots, ` +
|
||||
'we recommend that you revert any local changes to tests or other code, ' +
|
||||
'to ensure that you do not store invalid state.',
|
||||
);
|
||||
|
||||
const writeSnapshotVersion = () =>
|
||||
`// Jest Snapshot v${SNAPSHOT_VERSION}, ${SNAPSHOT_GUIDE_LINK}`;
|
||||
|
||||
const validateSnapshotVersion = (snapshotContents: string) => {
|
||||
const versionTest = SNAPSHOT_VERSION_REGEXP.exec(snapshotContents);
|
||||
const version = versionTest && versionTest[1];
|
||||
|
||||
if (!version) {
|
||||
return new Error(
|
||||
chalk.red(
|
||||
`${chalk.bold('Outdated snapshot')}: No snapshot header found. ` +
|
||||
'Jest 19 introduced versioned snapshots to ensure all developers ' +
|
||||
'on a project are using the same version of Jest. ' +
|
||||
'Please update all snapshots during this upgrade of Jest.\n\n',
|
||||
) + SNAPSHOT_VERSION_WARNING,
|
||||
);
|
||||
}
|
||||
|
||||
if (version < SNAPSHOT_VERSION) {
|
||||
return new Error(
|
||||
// eslint-disable-next-line prefer-template
|
||||
chalk.red(
|
||||
`${chalk.red.bold('Outdated snapshot')}: The version of the snapshot ` +
|
||||
'file associated with this test is outdated. The snapshot file ' +
|
||||
'version ensures that all developers on a project are using ' +
|
||||
'the same version of Jest. ' +
|
||||
'Please update all snapshots during this upgrade of Jest.',
|
||||
) +
|
||||
'\n\n' +
|
||||
`Expected: v${SNAPSHOT_VERSION}\n` +
|
||||
`Received: v${version}\n\n` +
|
||||
SNAPSHOT_VERSION_WARNING,
|
||||
);
|
||||
}
|
||||
|
||||
if (version > SNAPSHOT_VERSION) {
|
||||
return new Error(
|
||||
// eslint-disable-next-line prefer-template
|
||||
chalk.red(
|
||||
`${chalk.red.bold('Outdated Jest version')}: The version of this ` +
|
||||
'snapshot file indicates that this project is meant to be used ' +
|
||||
'with a newer version of Jest. The snapshot file version ensures ' +
|
||||
'that all developers on a project are using the same version of ' +
|
||||
'Jest. Please update your version of Jest and re-run the tests.',
|
||||
) +
|
||||
'\n\n' +
|
||||
`Expected: v${SNAPSHOT_VERSION}\n` +
|
||||
`Received: v${version}`,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const testNameToKey = (testName: string, count: number): string =>
|
||||
`${testName} ${count}`;
|
||||
|
||||
export const keyToTestName = (key: string): string => {
|
||||
if (!/ \d+$/.test(key)) {
|
||||
throw new Error('Snapshot keys must end with a number.');
|
||||
}
|
||||
|
||||
return key.replace(/ \d+$/, '');
|
||||
};
|
||||
|
||||
export const getSnapshotData = (
|
||||
snapshotPath: string,
|
||||
update: Config.SnapshotUpdateState,
|
||||
): {
|
||||
data: SnapshotData;
|
||||
dirty: boolean;
|
||||
} => {
|
||||
const data = Object.create(null);
|
||||
let snapshotContents = '';
|
||||
let dirty = false;
|
||||
|
||||
if (fs.existsSync(snapshotPath)) {
|
||||
try {
|
||||
snapshotContents = fs.readFileSync(snapshotPath, 'utf8');
|
||||
// eslint-disable-next-line no-new-func
|
||||
const populate = new Function('exports', snapshotContents);
|
||||
populate(data);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const validationResult = validateSnapshotVersion(snapshotContents);
|
||||
const isInvalid = snapshotContents && validationResult;
|
||||
|
||||
if (update === 'none' && isInvalid) {
|
||||
throw validationResult;
|
||||
}
|
||||
|
||||
if ((update === 'all' || update === 'new') && isInvalid) {
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
return {data, dirty};
|
||||
};
|
||||
|
||||
export const escapeBacktickString = (str: string): string =>
|
||||
str.replaceAll(/`|\\|\${/g, '\\$&');
|
||||
|
||||
const printBacktickString = (str: string): string =>
|
||||
`\`${escapeBacktickString(str)}\``;
|
||||
|
||||
export const ensureDirectoryExists = (filePath: string): void => {
|
||||
try {
|
||||
fs.mkdirSync(path.dirname(filePath), {recursive: true});
|
||||
} catch {}
|
||||
};
|
||||
|
||||
export const normalizeNewlines = (string: string): string =>
|
||||
string.replaceAll(/\r\n|\r/g, '\n');
|
||||
|
||||
export const saveSnapshotFile = (
|
||||
snapshotData: SnapshotData,
|
||||
snapshotPath: string,
|
||||
): void => {
|
||||
const snapshots = Object.keys(snapshotData)
|
||||
.sort(naturalCompare)
|
||||
.map(
|
||||
key =>
|
||||
`exports[${printBacktickString(key)}] = ${printBacktickString(
|
||||
normalizeNewlines(snapshotData[key]),
|
||||
)};`,
|
||||
);
|
||||
|
||||
ensureDirectoryExists(snapshotPath);
|
||||
fs.writeFileSync(
|
||||
snapshotPath,
|
||||
`${writeSnapshotVersion()}\n\n${snapshots.join('\n\n')}\n`,
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "build"
|
||||
},
|
||||
"include": ["./src/**/*"],
|
||||
"exclude": ["./**/__mocks__/**/*", "./**/__tests__/**/*"],
|
||||
"references": [{"path": "../jest-types"}]
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
"@babel/plugin-syntax-typescript": "^7.7.2",
|
||||
"@babel/types": "^7.3.3",
|
||||
"@jest/expect-utils": "workspace:*",
|
||||
"@jest/snapshot-utils": "workspace:*",
|
||||
"@jest/transform": "workspace:*",
|
||||
"@jest/types": "workspace:*",
|
||||
"babel-preset-current-node-syntax": "^1.0.0",
|
||||
@@ -36,7 +37,6 @@
|
||||
"jest-matcher-utils": "workspace:*",
|
||||
"jest-message-util": "workspace:*",
|
||||
"jest-util": "workspace:*",
|
||||
"natural-compare": "^1.4.0",
|
||||
"pretty-format": "workspace:*",
|
||||
"semver": "^7.5.3",
|
||||
"synckit": "^0.9.0"
|
||||
@@ -47,7 +47,6 @@
|
||||
"@jest/test-utils": "workspace:*",
|
||||
"@types/babel__core": "^7.1.14",
|
||||
"@types/graceful-fs": "^4.1.3",
|
||||
"@types/natural-compare": "^1.4.0",
|
||||
"@types/prettier-v2": "npm:@types/prettier@^2.1.5",
|
||||
"@types/semver": "^7.1.0",
|
||||
"ansi-regex": "^5.0.1",
|
||||
|
||||
@@ -6,21 +6,23 @@
|
||||
*/
|
||||
|
||||
import * as fs from 'graceful-fs';
|
||||
import {
|
||||
type SnapshotData,
|
||||
getSnapshotData,
|
||||
keyToTestName,
|
||||
saveSnapshotFile,
|
||||
testNameToKey,
|
||||
} from '@jest/snapshot-utils';
|
||||
import type {Config} from '@jest/types';
|
||||
import {getStackTraceLines, getTopFrame} from 'jest-message-util';
|
||||
import {saveInlineSnapshots} from './InlineSnapshots';
|
||||
import type {InlineSnapshot, SnapshotData, SnapshotFormat} from './types';
|
||||
import type {InlineSnapshot, SnapshotFormat} from './types';
|
||||
import {
|
||||
addExtraLineBreaks,
|
||||
getSnapshotData,
|
||||
keyToTestName,
|
||||
removeExtraLineBreaks,
|
||||
removeLinesBeforeExternalMatcherTrap,
|
||||
saveSnapshotFile,
|
||||
serialize,
|
||||
testNameToKey,
|
||||
} from './utils';
|
||||
|
||||
export type SnapshotStateOptions = {
|
||||
readonly updateSnapshot: Config.SnapshotUpdateState;
|
||||
readonly prettierPath?: string | null;
|
||||
|
||||
@@ -11,181 +11,14 @@ jest.mock('graceful-fs', () => ({
|
||||
}));
|
||||
|
||||
import {strict as assert} from 'assert';
|
||||
import * as path from 'path';
|
||||
import chalk = require('chalk');
|
||||
import * as fs from 'graceful-fs';
|
||||
import {
|
||||
SNAPSHOT_GUIDE_LINK,
|
||||
SNAPSHOT_VERSION,
|
||||
SNAPSHOT_VERSION_WARNING,
|
||||
addExtraLineBreaks,
|
||||
deepMerge,
|
||||
getSnapshotData,
|
||||
keyToTestName,
|
||||
removeExtraLineBreaks,
|
||||
removeLinesBeforeExternalMatcherTrap,
|
||||
saveSnapshotFile,
|
||||
serialize,
|
||||
testNameToKey,
|
||||
} from '../utils';
|
||||
|
||||
test('keyToTestName()', () => {
|
||||
expect(keyToTestName('abc cde 12')).toBe('abc cde');
|
||||
expect(keyToTestName('abc cde 12')).toBe('abc cde ');
|
||||
expect(() => keyToTestName('abc cde')).toThrow(
|
||||
'Snapshot keys must end with a number.',
|
||||
);
|
||||
});
|
||||
|
||||
test('testNameToKey', () => {
|
||||
expect(testNameToKey('abc cde', 1)).toBe('abc cde 1');
|
||||
expect(testNameToKey('abc cde ', 12)).toBe('abc cde 12');
|
||||
});
|
||||
|
||||
test('saveSnapshotFile() works with \r\n', () => {
|
||||
const filename = path.join(__dirname, 'remove-newlines.snap');
|
||||
const data = {
|
||||
myKey: '<div>\r\n</div>',
|
||||
};
|
||||
|
||||
saveSnapshotFile(data, filename);
|
||||
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
||||
filename,
|
||||
`// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` +
|
||||
'exports[`myKey`] = `<div>\n</div>`;\n',
|
||||
);
|
||||
});
|
||||
|
||||
test('saveSnapshotFile() works with \r', () => {
|
||||
const filename = path.join(__dirname, 'remove-newlines.snap');
|
||||
const data = {
|
||||
myKey: '<div>\r</div>',
|
||||
};
|
||||
|
||||
saveSnapshotFile(data, filename);
|
||||
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
||||
filename,
|
||||
`// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` +
|
||||
'exports[`myKey`] = `<div>\n</div>`;\n',
|
||||
);
|
||||
});
|
||||
|
||||
test('getSnapshotData() throws when no snapshot version', () => {
|
||||
const filename = path.join(__dirname, 'old-snapshot.snap');
|
||||
jest
|
||||
.mocked(fs.readFileSync)
|
||||
.mockReturnValue('exports[`myKey`] = `<div>\n</div>`;\n');
|
||||
const update = 'none';
|
||||
|
||||
expect(() => getSnapshotData(filename, update)).toThrow(
|
||||
chalk.red(
|
||||
`${chalk.bold('Outdated snapshot')}: No snapshot header found. ` +
|
||||
'Jest 19 introduced versioned snapshots to ensure all developers on ' +
|
||||
'a project are using the same version of Jest. ' +
|
||||
'Please update all snapshots during this upgrade of Jest.\n\n',
|
||||
) + SNAPSHOT_VERSION_WARNING,
|
||||
);
|
||||
});
|
||||
|
||||
test('getSnapshotData() throws for older snapshot version', () => {
|
||||
const filename = path.join(__dirname, 'old-snapshot.snap');
|
||||
jest
|
||||
.mocked(fs.readFileSync)
|
||||
.mockReturnValue(
|
||||
`// Jest Snapshot v0.99, ${SNAPSHOT_GUIDE_LINK}\n\n` +
|
||||
'exports[`myKey`] = `<div>\n</div>`;\n',
|
||||
);
|
||||
const update = 'none';
|
||||
|
||||
expect(() => getSnapshotData(filename, update)).toThrow(
|
||||
`${chalk.red(
|
||||
`${chalk.red.bold('Outdated snapshot')}: The version of the snapshot ` +
|
||||
'file associated with this test is outdated. The snapshot file ' +
|
||||
'version ensures that all developers on a project are using ' +
|
||||
'the same version of Jest. ' +
|
||||
'Please update all snapshots during this upgrade of Jest.',
|
||||
)}\n\nExpected: v${SNAPSHOT_VERSION}\n` +
|
||||
`Received: v0.99\n\n${SNAPSHOT_VERSION_WARNING}`,
|
||||
);
|
||||
});
|
||||
|
||||
test('getSnapshotData() throws for newer snapshot version', () => {
|
||||
const filename = path.join(__dirname, 'old-snapshot.snap');
|
||||
jest
|
||||
.mocked(fs.readFileSync)
|
||||
.mockReturnValue(
|
||||
`// Jest Snapshot v2, ${SNAPSHOT_GUIDE_LINK}\n\n` +
|
||||
'exports[`myKey`] = `<div>\n</div>`;\n',
|
||||
);
|
||||
const update = 'none';
|
||||
|
||||
expect(() => getSnapshotData(filename, update)).toThrow(
|
||||
`${chalk.red(
|
||||
`${chalk.red.bold('Outdated Jest version')}: The version of this ` +
|
||||
'snapshot file indicates that this project is meant to be used ' +
|
||||
'with a newer version of Jest. ' +
|
||||
'The snapshot file version ensures that all developers on a project ' +
|
||||
'are using the same version of Jest. ' +
|
||||
'Please update your version of Jest and re-run the tests.',
|
||||
)}\n\nExpected: v${SNAPSHOT_VERSION}\nReceived: v2`,
|
||||
);
|
||||
});
|
||||
|
||||
test('getSnapshotData() does not throw for when updating', () => {
|
||||
const filename = path.join(__dirname, 'old-snapshot.snap');
|
||||
jest
|
||||
.mocked(fs.readFileSync)
|
||||
.mockReturnValue('exports[`myKey`] = `<div>\n</div>`;\n');
|
||||
const update = 'all';
|
||||
|
||||
expect(() => getSnapshotData(filename, update)).not.toThrow();
|
||||
});
|
||||
|
||||
test('getSnapshotData() marks invalid snapshot dirty when updating', () => {
|
||||
const filename = path.join(__dirname, 'old-snapshot.snap');
|
||||
jest
|
||||
.mocked(fs.readFileSync)
|
||||
.mockReturnValue('exports[`myKey`] = `<div>\n</div>`;\n');
|
||||
const update = 'all';
|
||||
|
||||
expect(getSnapshotData(filename, update)).toMatchObject({dirty: true});
|
||||
});
|
||||
|
||||
test('getSnapshotData() marks valid snapshot not dirty when updating', () => {
|
||||
const filename = path.join(__dirname, 'old-snapshot.snap');
|
||||
jest
|
||||
.mocked(fs.readFileSync)
|
||||
.mockReturnValue(
|
||||
`// Jest Snapshot v${SNAPSHOT_VERSION}, ${SNAPSHOT_GUIDE_LINK}\n\n` +
|
||||
'exports[`myKey`] = `<div>\n</div>`;\n',
|
||||
);
|
||||
const update = 'all';
|
||||
|
||||
expect(getSnapshotData(filename, update)).toMatchObject({dirty: false});
|
||||
});
|
||||
|
||||
test('escaping', () => {
|
||||
const filename = path.join(__dirname, 'escaping.snap');
|
||||
const data = '"\'\\';
|
||||
const writeFileSync = jest.mocked(fs.writeFileSync);
|
||||
|
||||
writeFileSync.mockReset();
|
||||
saveSnapshotFile({key: data}, filename);
|
||||
const writtenData = writeFileSync.mock.calls[0][1];
|
||||
expect(writtenData).toBe(
|
||||
`// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` +
|
||||
'exports[`key`] = `"\'\\\\`;\n',
|
||||
);
|
||||
|
||||
// @ts-expect-error: used in `eval`
|
||||
const exports = {};
|
||||
// eslint-disable-next-line no-eval
|
||||
const readData = eval(`var exports = {}; ${writtenData} exports`);
|
||||
expect(readData).toEqual({key: data});
|
||||
const snapshotData = readData.key;
|
||||
expect(data).toEqual(snapshotData);
|
||||
});
|
||||
|
||||
test('serialize handles \\r\\n', () => {
|
||||
const data = '<div>\r\n</div>';
|
||||
const serializedData = serialize(data);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import {types} from 'util';
|
||||
import * as fs from 'graceful-fs';
|
||||
import {escapeBacktickString} from '@jest/snapshot-utils';
|
||||
import type {Config} from '@jest/types';
|
||||
import type {MatcherFunctionWithContext} from 'expect';
|
||||
import {
|
||||
@@ -32,7 +33,7 @@ import {
|
||||
printSnapshotAndReceived,
|
||||
} from './printSnapshot';
|
||||
import type {Context, FileSystem, MatchSnapshotConfig} from './types';
|
||||
import {deepMerge, escapeBacktickString, serialize} from './utils';
|
||||
import {deepMerge, serialize} from './utils';
|
||||
|
||||
export {addSerializer, getSerializers} from './plugins';
|
||||
export {
|
||||
|
||||
@@ -34,8 +34,6 @@ export type MatchSnapshotConfig = {
|
||||
received: any;
|
||||
};
|
||||
|
||||
export type SnapshotData = Record<string, string>;
|
||||
|
||||
export interface SnapshotMatchers<R extends void | Promise<void>, T> {
|
||||
/**
|
||||
* This ensures that a value matches the most recent snapshot with property matchers.
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import * as path from 'path';
|
||||
import type {ParseResult, PluginItem} from '@babel/core';
|
||||
import type {
|
||||
File,
|
||||
@@ -14,129 +13,19 @@ import type {
|
||||
TemplateLiteral,
|
||||
TraversalAncestors,
|
||||
} from '@babel/types';
|
||||
import chalk = require('chalk');
|
||||
import * as fs from 'graceful-fs';
|
||||
import naturalCompare = require('natural-compare');
|
||||
import type {Config} from '@jest/types';
|
||||
import {escapeBacktickString, normalizeNewlines} from '@jest/snapshot-utils';
|
||||
import {
|
||||
type OptionsReceived as PrettyFormatOptions,
|
||||
format as prettyFormat,
|
||||
} from 'pretty-format';
|
||||
import {getSerializers} from './plugins';
|
||||
import type {InlineSnapshot, SnapshotData} from './types';
|
||||
|
||||
export const SNAPSHOT_VERSION = '1';
|
||||
const SNAPSHOT_VERSION_REGEXP = /^\/\/ Jest Snapshot v(.+),/;
|
||||
export const SNAPSHOT_GUIDE_LINK = 'https://goo.gl/fbAQLP';
|
||||
export const SNAPSHOT_VERSION_WARNING = chalk.yellow(
|
||||
`${chalk.bold('Warning')}: Before you upgrade snapshots, ` +
|
||||
'we recommend that you revert any local changes to tests or other code, ' +
|
||||
'to ensure that you do not store invalid state.',
|
||||
);
|
||||
|
||||
const writeSnapshotVersion = () =>
|
||||
`// Jest Snapshot v${SNAPSHOT_VERSION}, ${SNAPSHOT_GUIDE_LINK}`;
|
||||
|
||||
const validateSnapshotVersion = (snapshotContents: string) => {
|
||||
const versionTest = SNAPSHOT_VERSION_REGEXP.exec(snapshotContents);
|
||||
const version = versionTest && versionTest[1];
|
||||
|
||||
if (!version) {
|
||||
return new Error(
|
||||
chalk.red(
|
||||
`${chalk.bold('Outdated snapshot')}: No snapshot header found. ` +
|
||||
'Jest 19 introduced versioned snapshots to ensure all developers ' +
|
||||
'on a project are using the same version of Jest. ' +
|
||||
'Please update all snapshots during this upgrade of Jest.\n\n',
|
||||
) + SNAPSHOT_VERSION_WARNING,
|
||||
);
|
||||
}
|
||||
|
||||
if (version < SNAPSHOT_VERSION) {
|
||||
return new Error(
|
||||
// eslint-disable-next-line prefer-template
|
||||
chalk.red(
|
||||
`${chalk.red.bold('Outdated snapshot')}: The version of the snapshot ` +
|
||||
'file associated with this test is outdated. The snapshot file ' +
|
||||
'version ensures that all developers on a project are using ' +
|
||||
'the same version of Jest. ' +
|
||||
'Please update all snapshots during this upgrade of Jest.',
|
||||
) +
|
||||
'\n\n' +
|
||||
`Expected: v${SNAPSHOT_VERSION}\n` +
|
||||
`Received: v${version}\n\n` +
|
||||
SNAPSHOT_VERSION_WARNING,
|
||||
);
|
||||
}
|
||||
|
||||
if (version > SNAPSHOT_VERSION) {
|
||||
return new Error(
|
||||
// eslint-disable-next-line prefer-template
|
||||
chalk.red(
|
||||
`${chalk.red.bold('Outdated Jest version')}: The version of this ` +
|
||||
'snapshot file indicates that this project is meant to be used ' +
|
||||
'with a newer version of Jest. The snapshot file version ensures ' +
|
||||
'that all developers on a project are using the same version of ' +
|
||||
'Jest. Please update your version of Jest and re-run the tests.',
|
||||
) +
|
||||
'\n\n' +
|
||||
`Expected: v${SNAPSHOT_VERSION}\n` +
|
||||
`Received: v${version}`,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
import type {InlineSnapshot} from './types';
|
||||
|
||||
function isObject(item: unknown): boolean {
|
||||
return item != null && typeof item === 'object' && !Array.isArray(item);
|
||||
}
|
||||
|
||||
export const testNameToKey = (testName: string, count: number): string =>
|
||||
`${testName} ${count}`;
|
||||
|
||||
export const keyToTestName = (key: string): string => {
|
||||
if (!/ \d+$/.test(key)) {
|
||||
throw new Error('Snapshot keys must end with a number.');
|
||||
}
|
||||
|
||||
return key.replace(/ \d+$/, '');
|
||||
};
|
||||
|
||||
export const getSnapshotData = (
|
||||
snapshotPath: string,
|
||||
update: Config.SnapshotUpdateState,
|
||||
): {
|
||||
data: SnapshotData;
|
||||
dirty: boolean;
|
||||
} => {
|
||||
const data = Object.create(null);
|
||||
let snapshotContents = '';
|
||||
let dirty = false;
|
||||
|
||||
if (fs.existsSync(snapshotPath)) {
|
||||
try {
|
||||
snapshotContents = fs.readFileSync(snapshotPath, 'utf8');
|
||||
// eslint-disable-next-line no-new-func
|
||||
const populate = new Function('exports', snapshotContents);
|
||||
populate(data);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const validationResult = validateSnapshotVersion(snapshotContents);
|
||||
const isInvalid = snapshotContents && validationResult;
|
||||
|
||||
if (update === 'none' && isInvalid) {
|
||||
throw validationResult;
|
||||
}
|
||||
|
||||
if ((update === 'all' || update === 'new') && isInvalid) {
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
return {data, dirty};
|
||||
};
|
||||
|
||||
// Add extra line breaks at beginning and end of multiline snapshot
|
||||
// to make the content easier to read.
|
||||
export const addExtraLineBreaks = (string: string): string =>
|
||||
@@ -194,41 +83,6 @@ export const minify = (val: unknown): string =>
|
||||
export const deserializeString = (stringified: string): string =>
|
||||
stringified.slice(1, -1).replaceAll(/\\("|\\)/g, '$1');
|
||||
|
||||
export const escapeBacktickString = (str: string): string =>
|
||||
str.replaceAll(/`|\\|\${/g, '\\$&');
|
||||
|
||||
const printBacktickString = (str: string): string =>
|
||||
`\`${escapeBacktickString(str)}\``;
|
||||
|
||||
export const ensureDirectoryExists = (filePath: string): void => {
|
||||
try {
|
||||
fs.mkdirSync(path.dirname(filePath), {recursive: true});
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const normalizeNewlines = (string: string) =>
|
||||
string.replaceAll(/\r\n|\r/g, '\n');
|
||||
|
||||
export const saveSnapshotFile = (
|
||||
snapshotData: SnapshotData,
|
||||
snapshotPath: string,
|
||||
): void => {
|
||||
const snapshots = Object.keys(snapshotData)
|
||||
.sort(naturalCompare)
|
||||
.map(
|
||||
key =>
|
||||
`exports[${printBacktickString(key)}] = ${printBacktickString(
|
||||
normalizeNewlines(snapshotData[key]),
|
||||
)};`,
|
||||
);
|
||||
|
||||
ensureDirectoryExists(snapshotPath);
|
||||
fs.writeFileSync(
|
||||
snapshotPath,
|
||||
`${writeSnapshotVersion()}\n\n${snapshots.join('\n\n')}\n`,
|
||||
);
|
||||
};
|
||||
|
||||
const isAnyOrAnything = (input: object) =>
|
||||
'$$typeof' in input &&
|
||||
input.$$typeof === Symbol.for('jest.asymmetricMatcher') &&
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
{"path": "../jest-get-type"},
|
||||
{"path": "../jest-matcher-utils"},
|
||||
{"path": "../jest-message-util"},
|
||||
{"path": "../jest-snapshot-utils"},
|
||||
{"path": "../jest-transform"},
|
||||
{"path": "../jest-types"},
|
||||
{"path": "../jest-util"},
|
||||
|
||||
@@ -58,6 +58,7 @@ const packagesNotToTest = [
|
||||
'jest-runner',
|
||||
'jest-runtime',
|
||||
'jest-snapshot',
|
||||
'jest-snapshot-utils',
|
||||
'jest-util',
|
||||
'jest-validate',
|
||||
'jest-worker',
|
||||
|
||||
@@ -3178,6 +3178,19 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@jest/snapshot-utils@workspace:*, @jest/snapshot-utils@workspace:packages/jest-snapshot-utils":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@jest/snapshot-utils@workspace:packages/jest-snapshot-utils"
|
||||
dependencies:
|
||||
"@jest/types": "workspace:*"
|
||||
"@types/graceful-fs": ^4.1.3
|
||||
"@types/natural-compare": ^1.4.0
|
||||
chalk: ^4.0.0
|
||||
graceful-fs: ^4.2.9
|
||||
natural-compare: ^1.4.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@jest/source-map@workspace:*, @jest/source-map@workspace:packages/jest-source-map":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@jest/source-map@workspace:packages/jest-source-map"
|
||||
@@ -13178,12 +13191,12 @@ __metadata:
|
||||
"@babel/preset-react": ^7.12.1
|
||||
"@babel/types": ^7.3.3
|
||||
"@jest/expect-utils": "workspace:*"
|
||||
"@jest/snapshot-utils": "workspace:*"
|
||||
"@jest/test-utils": "workspace:*"
|
||||
"@jest/transform": "workspace:*"
|
||||
"@jest/types": "workspace:*"
|
||||
"@types/babel__core": ^7.1.14
|
||||
"@types/graceful-fs": ^4.1.3
|
||||
"@types/natural-compare": ^1.4.0
|
||||
"@types/prettier-v2": "npm:@types/prettier@^2.1.5"
|
||||
"@types/semver": ^7.1.0
|
||||
ansi-regex: ^5.0.1
|
||||
@@ -13197,7 +13210,6 @@ __metadata:
|
||||
jest-matcher-utils: "workspace:*"
|
||||
jest-message-util: "workspace:*"
|
||||
jest-util: "workspace:*"
|
||||
natural-compare: ^1.4.0
|
||||
prettier: ^3.0.3
|
||||
prettier-v2: "npm:prettier@^2.1.5"
|
||||
pretty-format: "workspace:*"
|
||||
|
||||
Reference in New Issue
Block a user