Skip to content

Commit 4482e71

Browse files
authored
jest-snapshot: Distinguish empty string from external snapshot not written (#8880)
* jest-snapshot: Distinguish empty string from external snapshot not written * Update CHANGELOG.md * Replace null with undefined * Add e2e test toMatchSnapshotWithStringSerializer
1 parent 45c57c5 commit 4482e71

File tree

6 files changed

+92
-6
lines changed

6 files changed

+92
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- `[jest-leak-detector]` [**BREAKING**] Use `weak-napi` instead of `weak` package ([#8686](https://github.com/facebook/jest/pull/8686))
1919
- `[jest-mock]` Fix for mockReturnValue overriding mockImplementationOnce ([#8398](https://github.com/facebook/jest/pull/8398))
2020
- `[jest-snapshot]` Remove only the added newlines in multiline snapshots ([#8859](https://github.com/facebook/jest/pull/8859))
21+
- `[jest-snapshot]` Distinguish empty string from external snapshot not written ([#8880](https://github.com/facebook/jest/pull/8880))
2122

2223
### Chore & Maintenance
2324

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import * as path from 'path';
9+
import {cleanup, makeTemplate, writeFiles} from '../Utils';
10+
import runJest from '../runJest';
11+
12+
const DIR = path.resolve(
13+
__dirname,
14+
'../to-match-snapshot-with-string-serializer',
15+
);
16+
const TESTS_DIR = path.resolve(DIR, '__tests__');
17+
18+
beforeEach(() => cleanup(TESTS_DIR));
19+
afterAll(() => cleanup(TESTS_DIR));
20+
21+
test('empty external', () => {
22+
// Make sure empty string as expected value of external snapshot
23+
// is not confused with new snapshot not written because of --ci option.
24+
const filename = 'empty-external.test.js';
25+
const template = makeTemplate(
26+
`test('string serializer', () => { expect($1).toMatchSnapshot(); })`,
27+
);
28+
29+
{
30+
writeFiles(TESTS_DIR, {
31+
[filename]: template(['""']), // empty string
32+
});
33+
const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]);
34+
expect(stderr).toMatch('1 snapshot written from 1 test suite.');
35+
expect(status).toBe(0);
36+
}
37+
38+
{
39+
const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]);
40+
expect(stderr).toMatch('Snapshots: 1 passed, 1 total');
41+
expect(stderr).not.toMatch('1 snapshot written from 1 test suite.');
42+
expect(status).toBe(0);
43+
}
44+
45+
{
46+
writeFiles(TESTS_DIR, {
47+
[filename]: template(['"non-empty"']),
48+
});
49+
const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]);
50+
expect(stderr).toMatch('Snapshots: 1 failed, 1 total');
51+
expect(stderr).not.toMatch('not written'); // not confused with --ci option
52+
expect(stderr).toMatch(/- Snapshot|Snapshot:/); // ordinary report
53+
expect(status).toBe(1);
54+
}
55+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"jest": {
3+
"testEnvironment": "node",
4+
"snapshotSerializers": [
5+
"./serializers/string"
6+
]
7+
}
8+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
'use strict';
9+
10+
// Serialize string (especially empty) without enclosing punctuation.
11+
module.exports = {
12+
print: val => val,
13+
test: val => typeof val === 'string',
14+
};

packages/jest-snapshot/src/State.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ export type SnapshotMatchOptions = {
3535
error?: Error;
3636
};
3737

38+
type SnapshotReturnOptions = {
39+
actual: string;
40+
count: number;
41+
expected?: string;
42+
key: string;
43+
pass: boolean;
44+
};
45+
3846
export default class SnapshotState {
3947
private _counters: Map<string, number>;
4048
private _dirty: boolean;
@@ -173,7 +181,7 @@ export default class SnapshotState {
173181
key,
174182
inlineSnapshot,
175183
error,
176-
}: SnapshotMatchOptions) {
184+
}: SnapshotMatchOptions): SnapshotReturnOptions {
177185
this._counters.set(testName, (this._counters.get(testName) || 0) + 1);
178186
const count = Number(this._counters.get(testName));
179187
const isInline = inlineSnapshot !== undefined;
@@ -185,7 +193,7 @@ export default class SnapshotState {
185193
// Do not mark the snapshot as "checked" if the snapshot is inline and
186194
// there's an external snapshot. This way the external snapshot can be
187195
// removed with `--updateSnapshot`.
188-
if (!(isInline && this._snapshotData[key])) {
196+
if (!(isInline && this._snapshotData[key] !== undefined)) {
189197
this._uncheckedKeys.delete(key);
190198
}
191199

@@ -248,7 +256,7 @@ export default class SnapshotState {
248256
return {
249257
actual: unescape(receivedSerialized),
250258
count,
251-
expected: expected ? unescape(expected) : null,
259+
expected: expected !== undefined ? unescape(expected) : undefined,
252260
key,
253261
pass: false,
254262
};

packages/jest-snapshot/src/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ const _toMatchSnapshot = ({
340340
let report: () => string;
341341
if (pass) {
342342
return {message: () => '', pass: true};
343-
} else if (!expected) {
343+
} else if (expected === undefined) {
344344
report = () =>
345345
`New snapshot was ${RECEIVED_COLOR('not written')}. The update flag ` +
346346
`must be explicitly passed to write a new snapshot.\n\n` +
@@ -349,8 +349,8 @@ const _toMatchSnapshot = ({
349349
`${RECEIVED_COLOR('Received value')} ` +
350350
`${actual}`;
351351
} else {
352-
expected = utils.removeExtraLineBreaks(expected || '');
353-
actual = utils.removeExtraLineBreaks(actual || '');
352+
expected = utils.removeExtraLineBreaks(expected);
353+
actual = utils.removeExtraLineBreaks(actual);
354354

355355
// Assign to local variable because of declaration let expected:
356356
// TypeScript thinks it could change before report function is called.

0 commit comments

Comments
 (0)