diff --git a/CHANGELOG.md b/CHANGELOG.md index 42521e9a13d8..cabe3e1e4403 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ - `[@jest/globals]` Add `jest.Mocked`, `jest.MockedClass`, `jest.MockedFunction` and `jest.MockedObject` utility types ([#12727](https://github.com/facebook/jest/pull/12727)) - `[jest-mock]` [**BREAKING**] Refactor `Mocked*` utility types. `MaybeMockedDeep` and `MaybeMocked` became `Mocked` and `MockedShallow` respectively; only deep mocked variants of `MockedClass`, `MockedFunction` and `MockedObject` are exported ([#13123](https://github.com/facebook/jest/pull/13123), [#13124](https://github.com/facebook/jest/pull/13124)) - `[jest-mock]` [**BREAKING**] Change the default `jest.mocked` helper’s behavior to deep mocked ([#13125](https://github.com/facebook/jest/pull/13125)) -- `[jest-worker]` Adds `workerIdleMemoryLimit` option which is used as a check for worker memory leaks >= Node 16.11.0 and recycles child workers as required. ([#13056](https://github.com/facebook/jest/pull/13056), [#13105](https://github.com/facebook/jest/pull/13105), [#13106](https://github.com/facebook/jest/pull/13106), [#13107](https://github.com/facebook/jest/pull/13107)) +- `[jest-snapshot]` [**BREAKING**] Let `babel` find config when updating inline snapshots ([#13150](https://github.com/facebook/jest/pull/13150)) +- `[jest-worker]` Adds `workerIdleMemoryLimit` option which is used as a check for worker memory leaks >= Node 16.11.0 and recycles child workers as required ([#13056](https://github.com/facebook/jest/pull/13056), [#13105](https://github.com/facebook/jest/pull/13105), [#13106](https://github.com/facebook/jest/pull/13106), [#13107](https://github.com/facebook/jest/pull/13107)) - `[pretty-format]` [**BREAKING**] Remove `ConvertAnsi` plugin in favour of `jest-serializer-ansi-escapes` ([#13040](https://github.com/facebook/jest/pull/13040)) ### Fixes @@ -26,6 +27,7 @@ - `[docs]` Fix webpack name ([#13049](https://github.com/facebook/jest/pull/13049)) - `[docs]` Explicit how to set `n` for `--bail` ([#13128](https://github.com/facebook/jest/pull/13128)) - `[jest-leak-detector]` Remove support for `weak-napi` ([#13035](https://github.com/facebook/jest/pull/13035)) +- `[jest-snapshot]` [**BREAKING**] Require `rootDir` as argument to `SnapshotState` ([#13150](https://github.com/facebook/jest/pull/13150)) ### Performance diff --git a/e2e/Utils.ts b/e2e/Utils.ts index f738bf63dfc7..e7bcfb473fb3 100644 --- a/e2e/Utils.ts +++ b/e2e/Utils.ts @@ -54,7 +54,11 @@ export const runYarnInstall = (cwd: string, env?: Record) => { fs.writeFileSync(lockfilePath, ''); } - return run(exists ? 'yarn install --immutable' : 'yarn install', cwd, env); + return run( + exists ? 'yarn install --immutable' : 'yarn install --no-immutable', + cwd, + env, + ); }; export const linkJestPackage = (packageName: string, cwd: string) => { @@ -168,7 +172,7 @@ export const sortLines = (output: string) => .map(str => str.trim()) .join('\n'); -interface JestPackageJson extends PackageJson { +export interface JestPackageJson extends PackageJson { jest: Config.InitialOptions; } @@ -180,7 +184,7 @@ const DEFAULT_PACKAGE_JSON: JestPackageJson = { export const createEmptyPackage = ( directory: string, - packageJson: PackageJson = DEFAULT_PACKAGE_JSON, + packageJson: JestPackageJson = DEFAULT_PACKAGE_JSON, ) => { const packageJsonWithDefaults = { ...packageJson, diff --git a/e2e/__tests__/toMatchInlineSnapshotWithJSX.test.ts b/e2e/__tests__/toMatchInlineSnapshotWithJSX.test.ts new file mode 100644 index 000000000000..ef1b98b5c35c --- /dev/null +++ b/e2e/__tests__/toMatchInlineSnapshotWithJSX.test.ts @@ -0,0 +1,111 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {tmpdir} from 'os'; +import * as path from 'path'; +import { + JestPackageJson, + cleanup, + createEmptyPackage, + runYarnInstall, + writeFiles, +} from '../Utils'; +import runJest, {json as runWithJson} from '../runJest'; + +const DIR = path.resolve(tmpdir(), 'to-match-inline-snapshot-with-jsx'); + +const babelConfig = { + presets: [ + ['@babel/preset-env', {targets: {node: 'current'}}], + '@babel/preset-react', + ], +}; + +const pkg: JestPackageJson = { + dependencies: { + react: '^17.0.0', + }, + devDependencies: { + '@babel/core': '^7.14.4', + '@babel/preset-env': '^7.14.4', + '@babel/preset-react': '^7.13.13', + 'react-test-renderer': '^17.0.2', + }, + jest: { + testEnvironment: 'jsdom', + }, +}; + +beforeEach(() => { + cleanup(DIR); + + createEmptyPackage(DIR, pkg); + + writeFiles(DIR, { + '__tests__/MismatchingSnapshot.test.js': ` + import React from 'react'; + import renderer from 'react-test-renderer'; + + test('
x
', () => { + expect(renderer.create(
x
).toJSON()).toMatchInlineSnapshot(\` +
+ y +
+ \`); + });`, + }); + + runYarnInstall(DIR, { + YARN_ENABLE_GLOBAL_CACHE: 'true', + YARN_NODE_LINKER: 'node-modules', + }); +}); + +afterAll(() => { + cleanup(DIR); +}); + +it('successfully runs the tests with external babel config', () => { + writeFiles(DIR, { + 'babel.config.js': `module.exports = ${JSON.stringify(babelConfig)};`, + }); + + const normalRun = runWithJson(DIR, []); + expect(normalRun.exitCode).toBe(1); + expect(normalRun.stderr).toContain('1 snapshot failed from 1 test suite.'); + expect(normalRun.json.testResults[0].message).toMatchInlineSnapshot(` + " ●
x
+ + expect(received).toMatchInlineSnapshot(snapshot) + + Snapshot name: \`
x
1\` + + - Snapshot - 1 + + Received + 1 + +
+ - y + + x +
+ + 3 | + 4 | test('
x
', () => { + > 5 | expect(renderer.create(
x
).toJSON()).toMatchInlineSnapshot(\` + | ^ + 6 |
+ 7 | y + 8 |
+ + at Object.toMatchInlineSnapshot (__tests__/MismatchingSnapshot.test.js:5:50) + " + `); + + const updateSnapshotRun = runJest(DIR, ['--updateSnapshot']); + + expect(updateSnapshotRun.exitCode).toBe(0); + expect(updateSnapshotRun.stderr).toContain('1 snapshot updated.'); +}); diff --git a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts index e160c964d226..4556c55185ae 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts @@ -114,6 +114,7 @@ export const initialize = async ({ const snapshotState = new SnapshotState(snapshotPath, { expand: globalConfig.expand, prettierPath: config.prettierPath, + rootDir: config.rootDir, snapshotFormat: config.snapshotFormat, updateSnapshot: globalConfig.updateSnapshot, }); diff --git a/packages/jest-jasmine2/src/setup_jest_globals.ts b/packages/jest-jasmine2/src/setup_jest_globals.ts index 635546f8fbbf..f5d0de8443e1 100644 --- a/packages/jest-jasmine2/src/setup_jest_globals.ts +++ b/packages/jest-jasmine2/src/setup_jest_globals.ts @@ -104,12 +104,13 @@ export default async function setupJestGlobals({ patchJasmine(); const {expand, updateSnapshot} = globalConfig; - const {prettierPath, snapshotFormat} = config; + const {prettierPath, rootDir, snapshotFormat} = config; const snapshotResolver = await buildSnapshotResolver(config, localRequire); const snapshotPath = snapshotResolver.resolveSnapshotPath(testPath); const snapshotState = new SnapshotState(snapshotPath, { expand, prettierPath, + rootDir, snapshotFormat, updateSnapshot, }); diff --git a/packages/jest-snapshot/src/InlineSnapshots.ts b/packages/jest-snapshot/src/InlineSnapshots.ts index 2ee07954f6da..2e5013784b4a 100644 --- a/packages/jest-snapshot/src/InlineSnapshots.ts +++ b/packages/jest-snapshot/src/InlineSnapshots.ts @@ -46,6 +46,7 @@ export type InlineSnapshot = { export function saveInlineSnapshots( snapshots: Array, + rootDir: string, prettierPath: string | null, ): void { let prettier: Prettier | null = null; @@ -64,6 +65,7 @@ export function saveInlineSnapshots( saveSnapshotsForFile( snapshotsByFile[sourceFilePath], sourceFilePath, + rootDir, prettier && semver.gte(prettier.version, '1.5.0') ? prettier : undefined, ); } @@ -72,6 +74,7 @@ export function saveInlineSnapshots( const saveSnapshotsForFile = ( snapshots: Array, sourceFilePath: string, + rootDir: string, prettier: Prettier | undefined, ) => { const sourceFile = fs.readFileSync(sourceFilePath, 'utf8'); @@ -96,7 +99,7 @@ const saveSnapshotsForFile = ( filename: sourceFilePath, plugins, presets, - root: path.dirname(sourceFilePath), + root: rootDir, }); if (!ast) { throw new Error(`jest-snapshot: Failed to parse ${sourceFilePath}`); diff --git a/packages/jest-snapshot/src/State.ts b/packages/jest-snapshot/src/State.ts index a065294b8d9a..021c77c0e5a0 100644 --- a/packages/jest-snapshot/src/State.ts +++ b/packages/jest-snapshot/src/State.ts @@ -27,6 +27,7 @@ export type SnapshotStateOptions = { prettierPath?: string | null; expand?: boolean; snapshotFormat: PrettyFormatOptions; + rootDir: string; }; export type SnapshotMatchOptions = { @@ -64,6 +65,7 @@ export default class SnapshotState { private _uncheckedKeys: Set; private _prettierPath: string | null; private _snapshotFormat: PrettyFormatOptions; + private _rootDir: string; added: number; expand: boolean; @@ -92,6 +94,7 @@ export default class SnapshotState { this._updateSnapshot = options.updateSnapshot; this.updated = 0; this._snapshotFormat = options.snapshotFormat; + this._rootDir = options.rootDir; } markSnapshotsAsCheckedForTest(testName: string): void { @@ -154,7 +157,11 @@ export default class SnapshotState { saveSnapshotFile(this._snapshotData, this._snapshotPath); } if (hasInlineSnapshots) { - saveInlineSnapshots(this._inlineSnapshots, this._prettierPath); + saveInlineSnapshots( + this._inlineSnapshots, + this._rootDir, + this._prettierPath, + ); } status.saved = true; } else if (!hasExternalSnapshots && fs.existsSync(this._snapshotPath)) { diff --git a/packages/jest-snapshot/src/__tests__/InlineSnapshots.test.ts b/packages/jest-snapshot/src/__tests__/InlineSnapshots.test.ts index a1c275177865..74303a23a20e 100644 --- a/packages/jest-snapshot/src/__tests__/InlineSnapshots.test.ts +++ b/packages/jest-snapshot/src/__tests__/InlineSnapshots.test.ts @@ -55,6 +55,7 @@ test('saveInlineSnapshots() replaces empty function call with a template literal snapshot: '1', }, ], + dir, 'prettier', ); @@ -81,6 +82,7 @@ expect(a).toMatchInlineSnapshot(\`[1, 2]\`); frame: {column: 11, file: filename, line} as Frame, snapshot: '[1, 2]', })), + dir, null, ); @@ -111,6 +113,7 @@ expect(a).toMatchInlineSnapshot(\`[1, 2]\`); frame: {column: 11, file: filename, line} as Frame, snapshot: '[1, 2]', })), + dir, 'bad-prettier', ); @@ -143,6 +146,7 @@ expect(a).toMatchInlineSnapshot(); snapshot: "[{ foo: 'one' }, { foo: 'two' }]", }, ], + dir, null, ); @@ -177,6 +181,7 @@ it('foos', async () => { snapshot: '
hello
', }, ], + dir, null, ); @@ -218,6 +223,7 @@ expect(a).toMatchInlineSnapshot(); snapshot: '
hello
', }, ], + dir, null, ); @@ -248,6 +254,7 @@ expect(a).toMatchInlineSnapshot(\`[1, 2]\`); frame: {column: 11, file: filename, line} as Frame, snapshot: '[1, 2]', })), + dir, 'prettier', ); @@ -275,6 +282,7 @@ test.each([['babel'], ['flow'], ['typescript']])( snapshot: '1', }, ], + dir, 'prettier', ); @@ -299,6 +307,7 @@ test('saveInlineSnapshots() replaces existing template literal with property mat snapshot: '1', }, ], + dir, 'prettier', ); @@ -320,6 +329,7 @@ test.each(['prettier', null])( snapshot: '1', }, ], + dir, prettierModule, ); @@ -345,6 +355,7 @@ test('saveInlineSnapshots() throws if frame does not match', () => { snapshot: '1', }, ], + dir, 'prettier', ); @@ -362,6 +373,7 @@ test('saveInlineSnapshots() throws if multiple calls to to the same location', ( {frame, snapshot: '1'}, {frame, snapshot: '2'}, ], + dir, 'prettier', ); @@ -375,7 +387,7 @@ test('saveInlineSnapshots() uses escaped backticks', () => { fs.writeFileSync(filename, 'expect("`").toMatchInlineSnapshot();\n'); const frame = {column: 13, file: filename, line: 1} as Frame; - saveInlineSnapshots([{frame, snapshot: '`'}], 'prettier'); + saveInlineSnapshots([{frame, snapshot: '`'}], dir, 'prettier'); expect(fs.readFileSync(filename, 'utf-8')).toBe( 'expect("`").toMatchInlineSnapshot(`\\``);\n', @@ -397,6 +409,7 @@ test('saveInlineSnapshots() works with non-literals in expect call', () => { snapshot: "{a: 'a'}", }, ], + dir, 'prettier', ); @@ -425,6 +438,7 @@ test('saveInlineSnapshots() indents multi-line snapshots with spaces', () => { snapshot: "\nObject {\n a: 'a'\n}\n", }, ], + dir, 'prettier', ); @@ -467,6 +481,7 @@ test('saveInlineSnapshots() does not re-indent error snapshots', () => { snapshot: "\nObject {\n a: 'a'\n}\n", }, ], + dir, 'prettier', ); @@ -516,6 +531,7 @@ test('saveInlineSnapshots() does not re-indent already indented snapshots', () = snapshot: "\nObject {\n a: 'a'\n}\n", }, ], + dir, 'prettier', ); @@ -558,6 +574,7 @@ test('saveInlineSnapshots() indents multi-line snapshots with tabs', () => { snapshot: "\nObject {\n a: 'a'\n}\n", }, ], + dir, 'prettier', ); @@ -590,6 +607,7 @@ test('saveInlineSnapshots() indents snapshots after prettier reformats', () => { snapshot: "\nObject {\n a: 'a'\n}\n", }, ], + dir, 'prettier', ); @@ -621,6 +639,7 @@ test('saveInlineSnapshots() does not indent empty lines', () => { snapshot: '\nhello\n\nworld\n', }, ], + dir, 'prettier', ); @@ -655,6 +674,7 @@ test('saveInlineSnapshots() indents awaited snapshots with spaces', () => { snapshot: "\nObject {\n a: 'a'\n}\n", }, ], + dir, 'prettier', );