From cdb28b42e65fda2e96c41632e2be0b2b43749482 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Fri, 15 Mar 2019 18:24:29 -0400 Subject: [PATCH 01/11] jest-snapshot: Improve report when matcher fails, part 14 --- .../__snapshots__/failures.test.ts.snap | 8 +- .../watchModeUpdateSnapshot.test.ts.snap | 4 +- e2e/__tests__/toMatchInlineSnapshot.test.ts | 6 +- e2e/__tests__/toMatchSnapshot.test.ts | 18 +- ...toThrowErrorMatchingInlineSnapshot.test.ts | 4 +- .../toThrowErrorMatchingSnapshot.test.ts | 6 +- e2e/snapshot/__tests__/snapshot.test.js | 2 +- packages/jest-snapshot/src/index.ts | 232 +++++++++++++----- 8 files changed, 191 insertions(+), 89 deletions(-) diff --git a/e2e/__tests__/__snapshots__/failures.test.ts.snap b/e2e/__tests__/__snapshots__/failures.test.ts.snap index d6cdac0e6940..e5675430a4ac 100644 --- a/e2e/__tests__/__snapshots__/failures.test.ts.snap +++ b/e2e/__tests__/__snapshots__/failures.test.ts.snap @@ -387,9 +387,9 @@ FAIL __tests__/snapshotNamed.test.js ● failing named snapshot - expect(value).toMatchSnapshot() + expect(received).toMatchSnapshot(name) - Received value does not match stored snapshot "failing named snapshot: snapname 1". + Snapshot key: \`failing named snapshot: snapname 1\` - Snapshot + Received @@ -819,9 +819,9 @@ FAIL __tests__/snapshot.test.js ● failing snapshot - expect(value).toMatchSnapshot() + expect(received).toMatchSnapshot() - Received value does not match stored snapshot "failing snapshot 1". + Snapshot key: \`failing snapshot 1\` - Snapshot + Received diff --git a/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap b/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap index 7023c87719c2..a6b4f19add33 100644 --- a/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap +++ b/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap @@ -4,8 +4,8 @@ exports[`can press "u" to update snapshots: test results 1`] = ` FAIL __tests__/bar.spec.js ✕ bar ● bar - expect(value).toMatchSnapshot() - Received value does not match stored snapshot "bar 1". + expect(received).toMatchSnapshot() + Snapshot key: \`bar 1\` - Snapshot + Received - "foo" diff --git a/e2e/__tests__/toMatchInlineSnapshot.test.ts b/e2e/__tests__/toMatchInlineSnapshot.test.ts index 034b928d508d..e366802c960b 100644 --- a/e2e/__tests__/toMatchInlineSnapshot.test.ts +++ b/e2e/__tests__/toMatchInlineSnapshot.test.ts @@ -52,7 +52,7 @@ test('basic support', () => { }); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); const fileAfter = readFile(filename); - expect(stderr).toMatch('Received value does not match stored snapshot'); + expect(stderr).toMatch('Snapshot key: `inline snapshots 1`'); expect(status).toBe(1); expect(wrap(fileAfter)).toMatchSnapshot('snapshot mismatch'); } @@ -101,9 +101,7 @@ test('handles property matchers', () => { }); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); const fileAfter = readFile(filename); - expect(stderr).toMatch( - 'Received value does not match snapshot properties for "handles property matchers 1".', - ); + expect(stderr).toMatch('Snapshot key: `handles property matchers 1`'); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); expect(status).toBe(1); expect(wrap(fileAfter)).toMatchSnapshot('snapshot failed'); diff --git a/e2e/__tests__/toMatchSnapshot.test.ts b/e2e/__tests__/toMatchSnapshot.test.ts index 2f9cdc115a45..47a4c3cb45aa 100644 --- a/e2e/__tests__/toMatchSnapshot.test.ts +++ b/e2e/__tests__/toMatchSnapshot.test.ts @@ -42,7 +42,7 @@ test('basic support', () => { [filename]: template(['{apple: "updated value"}']), }); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); - expect(stderr).toMatch('Received value does not match stored snapshot'); + expect(stderr).toMatch('Snapshot key: `snapshots 1`'); expect(status).toBe(1); } @@ -107,7 +107,7 @@ test('first snapshot fails, second passes', () => { { writeFiles(TESTS_DIR, {[filename]: template([`'kiwi'`, `'banana'`])}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); - expect(stderr).toMatch('Received value does not match stored snapshot'); + expect(stderr).toMatch('Snapshot key: `snapshots 1`'); expect(stderr).toMatch('- "apple"\n + "kiwi"'); expect(stderr).not.toMatch('1 obsolete snapshot found'); expect(status).toBe(1); @@ -178,9 +178,7 @@ test('handles property matchers', () => { { writeFiles(TESTS_DIR, {[filename]: template(['"string"'])}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); - expect(stderr).toMatch( - 'Received value does not match snapshot properties for "handles property matchers 1".', - ); + expect(stderr).toMatch('Snapshot key: `handles property matchers 1`'); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); expect(status).toBe(1); } @@ -253,9 +251,9 @@ test('handles property matchers with custom name', () => { writeFiles(TESTS_DIR, {[filename]: template(['"string"'])}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); expect(stderr).toMatch( - 'Received value does not match snapshot properties for "handles property matchers with name: custom-name 1".', + 'Snapshot key: `handles property matchers with name: custom-name 1`', ); - expect(stderr).toMatch('Expected snapshot to match properties:'); + expect(stderr).toMatch('Expected properties:'); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); expect(status).toBe(1); } @@ -285,9 +283,9 @@ test('handles property matchers with deep properties', () => { writeFiles(TESTS_DIR, {[filename]: template(['"string"', '"Jest"'])}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); expect(stderr).toMatch( - 'Received value does not match snapshot properties for "handles property matchers with deep properties 1".', + 'Snapshot key: `handles property matchers with deep properties 1`', ); - expect(stderr).toMatch('Expected snapshot to match properties:'); + expect(stderr).toMatch('Expected properties:'); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); expect(status).toBe(1); } @@ -296,7 +294,7 @@ test('handles property matchers with deep properties', () => { writeFiles(TESTS_DIR, {[filename]: template(['new Date()', '"CHANGED"'])}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); expect(stderr).toMatch( - 'Received value does not match stored snapshot "handles property matchers with deep properties 1"', + '`handles property matchers with deep properties 1`', ); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); expect(status).toBe(1); diff --git a/e2e/__tests__/toThrowErrorMatchingInlineSnapshot.test.ts b/e2e/__tests__/toThrowErrorMatchingInlineSnapshot.test.ts index 5c69136a6ca9..1f41477c78ce 100644 --- a/e2e/__tests__/toThrowErrorMatchingInlineSnapshot.test.ts +++ b/e2e/__tests__/toThrowErrorMatchingInlineSnapshot.test.ts @@ -81,9 +81,7 @@ test('cannot be used with .not', () => { { writeFiles(TESTS_DIR, {[filename]: template()}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); - expect(stderr).toMatch( - 'Jest: `.not` cannot be used with `.toThrowErrorMatchingInlineSnapshot()`.', - ); + expect(stderr).toMatch('.not cannot be used with snapshot matchers'); expect(status).toBe(1); } }); diff --git a/e2e/__tests__/toThrowErrorMatchingSnapshot.test.ts b/e2e/__tests__/toThrowErrorMatchingSnapshot.test.ts index bd2ea58d67a5..8fbc274711e3 100644 --- a/e2e/__tests__/toThrowErrorMatchingSnapshot.test.ts +++ b/e2e/__tests__/toThrowErrorMatchingSnapshot.test.ts @@ -43,7 +43,7 @@ test(`throws the error if tested function didn't throw error`, () => { { writeFiles(TESTS_DIR, {[filename]: template()}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); - expect(stderr).toMatch(`Expected the function to throw an error.`); + expect(stderr).toMatch('Received function did not throw'); expect(status).toBe(1); } }); @@ -74,9 +74,7 @@ test('cannot be used with .not', () => { { writeFiles(TESTS_DIR, {[filename]: template()}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); - expect(stderr).toMatch( - 'Jest: `.not` cannot be used with `.toThrowErrorMatchingSnapshot()`.', - ); + expect(stderr).toMatch('.not cannot be used with snapshot matchers'); expect(status).toBe(1); } }); diff --git a/e2e/snapshot/__tests__/snapshot.test.js b/e2e/snapshot/__tests__/snapshot.test.js index 48ce35e33937..ec17021fe7f4 100644 --- a/e2e/snapshot/__tests__/snapshot.test.js +++ b/e2e/snapshot/__tests__/snapshot.test.js @@ -31,7 +31,7 @@ describe('snapshot', () => { it('cannot be used with .not', () => { expect(() => expect('').not.toMatchSnapshot()).toThrow( - 'Jest: `.not` cannot be used with `.toMatchSnapshot()`.' + '.not cannot be used with snapshot matchers' ); }); diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index dcb35ebd6daf..894c76e96943 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -11,7 +11,12 @@ import {FS as HasteFS} from 'jest-haste-map'; import {MatcherState} from 'expect'; import diff from 'jest-diff'; -import {EXPECTED_COLOR, matcherHint, RECEIVED_COLOR} from 'jest-matcher-utils'; +import { + EXPECTED_COLOR, + matcherHint, + MatcherHintOptions, + RECEIVED_COLOR, +} from 'jest-matcher-utils'; import { buildSnapshotResolver, isSnapshotPath, @@ -26,6 +31,37 @@ type Context = MatcherState & { snapshotState: SnapshotState; }; +type MatchSnapshotConfig = { + context: Context; + expectedArgument: string; + inlineSnapshot?: string; + matcherName: string; + options: MatcherHintOptions; + propertyMatchers?: any; + received: any; + testName?: string; +}; + +const DID_NOT_THROW = 'Received function did not throw'; +const NOT_SNAPSHOT_MATCHERS = '\n\n.not cannot be used with snapshot matchers'; + +// Display key in report when matcher fails same as in snapshot file, +// but with optional name argument in green. +const printKey = (path = '', name = '', count: number): string => { + const hasPath = path.length !== 0; + const hasName = name.length !== 0; + + return ( + '`' + + (hasPath ? utils.escapeBacktickString(path) : '') + + (hasPath && hasName ? ': ' : '') + + (hasName ? EXPECTED_COLOR(utils.escapeBacktickString(name)) : '') + + ' ' + + count + + '`' + ); +}; + const fileExists = (filePath: Config.Path, hasteFS: HasteFS): boolean => hasteFS.exists(filePath) || fs.existsSync(filePath); @@ -58,6 +94,28 @@ const toMatchSnapshot = function( propertyMatchers?: any, testName?: Config.Path, ) { + const matcherName = 'toMatchSnapshot'; + let expectedArgument = ''; + let secondArgument = ''; + + if (typeof propertyMatchers === 'object' && propertyMatchers !== null) { + expectedArgument = 'properties'; + if (typeof testName === 'string' && testName.length !== 0) { + secondArgument = 'name'; + } + } else if ( + typeof propertyMatchers === 'string' && + propertyMatchers.length !== 0 + ) { + expectedArgument = 'name'; + } + + const options: MatcherHintOptions = { + isNot: this.isNot, + promise: this.promise, + secondArgument, + }; + if (arguments.length === 3 && !propertyMatchers) { throw new Error( 'Property matchers must be an object.\n\nTo provide a snapshot test name without property matchers, use: toMatchSnapshot("name")', @@ -66,6 +124,9 @@ const toMatchSnapshot = function( return _toMatchSnapshot({ context: this, + expectedArgument, + matcherName, + options, propertyMatchers, received, testName, @@ -78,6 +139,28 @@ const toMatchInlineSnapshot = function( propertyMatchersOrInlineSnapshot?: any, inlineSnapshot?: string, ) { + const matcherName = 'toMatchInlineSnapshot'; + let expectedArgument = ''; + let secondArgument = ''; + + if (typeof propertyMatchersOrInlineSnapshot === 'string') { + expectedArgument = 'snapshot'; + } else if ( + typeof propertyMatchersOrInlineSnapshot === 'object' && + propertyMatchersOrInlineSnapshot !== null + ) { + expectedArgument = 'properties'; + if (typeof inlineSnapshot === 'string') { + secondArgument = 'snapshot'; + } + } + + const options: MatcherHintOptions = { + isNot: this.isNot, + promise: this.promise, + secondArgument, + }; + let propertyMatchers; if (typeof propertyMatchersOrInlineSnapshot === 'string') { inlineSnapshot = propertyMatchersOrInlineSnapshot; @@ -86,7 +169,10 @@ const toMatchInlineSnapshot = function( } return _toMatchSnapshot({ context: this, + expectedArgument, inlineSnapshot: inlineSnapshot || '', + matcherName, + options, propertyMatchers, received, }); @@ -94,34 +180,31 @@ const toMatchInlineSnapshot = function( const _toMatchSnapshot = ({ context, - received, + expectedArgument, + inlineSnapshot, + matcherName, + options, propertyMatchers, + received, testName, - inlineSnapshot, -}: { - context: Context; - received: any; - propertyMatchers?: any; - testName?: string; - inlineSnapshot?: string; -}) => { +}: MatchSnapshotConfig) => { context.dontThrow && context.dontThrow(); testName = typeof propertyMatchers === 'string' ? propertyMatchers : testName; const {currentTestName, isNot, snapshotState} = context; if (isNot) { - const matcherName = - typeof inlineSnapshot === 'string' - ? 'toMatchInlineSnapshot' - : 'toMatchSnapshot'; throw new Error( - `Jest: \`.not\` cannot be used with \`.${matcherName}()\`.`, + matcherHint(matcherName, undefined, expectedArgument, options) + + NOT_SNAPSHOT_MATCHERS, ); } if (!snapshotState) { - throw new Error('Jest: snapshot state must be initialized.'); + throw new Error( + matcherHint(matcherName, undefined, expectedArgument, options) + + '\n\nsnapshot state must be initialized', + ); } const fullTestName = @@ -140,21 +223,22 @@ const _toMatchSnapshot = ({ if (!propertyPass) { const key = snapshotState.fail(fullTestName, received); + const matched = /(\d+)$/.exec(key); + const count = matched === null ? 1 : Number(matched[1]); const report = () => - `${RECEIVED_COLOR('Received value')} does not match ` + - `${EXPECTED_COLOR(`snapshot properties for "${key}"`)}.\n\n` + - `Expected snapshot to match properties:\n` + - ` ${context.utils.printExpected(propertyMatchers)}` + - `\nReceived:\n` + - ` ${context.utils.printReceived(received)}`; + `Snapshot key: ${printKey(currentTestName, testName, count)}\n\n` + + `Expected properties: ${context.utils.printExpected( + propertyMatchers, + )}\n` + + `Received value: ${context.utils.printReceived(received)}`; return { message: () => - matcherHint('.toMatchSnapshot', 'value', 'properties') + + matcherHint(matcherName, undefined, expectedArgument, options) + '\n\n' + report(), - name: 'toMatchSnapshot', + name: matcherName, pass: false, report, }; @@ -169,7 +253,7 @@ const _toMatchSnapshot = ({ received, testName: fullTestName, }); - const {pass} = result; + const {count, pass} = result; let {actual, expected} = result; let report: () => string; @@ -193,8 +277,7 @@ const _toMatchSnapshot = ({ }); report = () => - `${RECEIVED_COLOR('Received value')} does not match ` + - `${EXPECTED_COLOR(`stored snapshot "${result.key}"`)}.\n\n` + + `Snapshot key: ${printKey(currentTestName, testName, count)}\n\n` + (diffMessage || EXPECTED_COLOR('- ' + (expected || '')) + '\n' + @@ -207,8 +290,10 @@ const _toMatchSnapshot = ({ actual, expected, message: () => - matcherHint('.toMatchSnapshot', 'value', '') + '\n\n' + report(), - name: 'toMatchSnapshot', + matcherHint(matcherName, undefined, expectedArgument, options) + + '\n\n' + + report(), + name: matcherName, pass: false, report, }; @@ -217,15 +302,29 @@ const _toMatchSnapshot = ({ const toThrowErrorMatchingSnapshot = function( this: Context, received: any, - testName: string | undefined, + testName: string | undefined, // because error TS1016 for testName?: string fromPromise: boolean, ) { - return _toThrowErrorMatchingSnapshot({ - context: this, + const matcherName = 'toThrowErrorMatchingSnapshot'; + const expectedArgument = + typeof testName === 'string' && testName.length !== 0 ? 'name' : ''; + const options = { + isNot: this.isNot, + promise: this.promise, + secondArgument: '', + }; + + return _toThrowErrorMatchingSnapshot( + { + context: this, + expectedArgument, + matcherName, + options, + received, + testName, + }, fromPromise, - received, - testName, - }); + ); }; const toThrowErrorMatchingInlineSnapshot = function( @@ -234,37 +333,46 @@ const toThrowErrorMatchingInlineSnapshot = function( inlineSnapshot?: string, fromPromise?: boolean, ) { - return _toThrowErrorMatchingSnapshot({ - context: this, + const matcherName = 'toThrowErrorMatchingInlineSnapshot'; + const expectedArgument = typeof inlineSnapshot === 'string' ? 'snapshot' : ''; + const options: MatcherHintOptions = { + isNot: this.isNot, + promise: this.promise, + secondArgument: '', + }; + + return _toThrowErrorMatchingSnapshot( + { + context: this, + expectedArgument, + inlineSnapshot: inlineSnapshot || '', + matcherName, + options, + received, + }, fromPromise, - inlineSnapshot: inlineSnapshot || '', - received, - }); + ); }; -const _toThrowErrorMatchingSnapshot = ({ - context, - received, - testName, - fromPromise, - inlineSnapshot, -}: { - context: Context; - received: any; - testName?: string; - fromPromise?: boolean; - inlineSnapshot?: string; -}) => { +const _toThrowErrorMatchingSnapshot = ( + { + context, + expectedArgument, + inlineSnapshot, + matcherName, + options, + received, + testName, + }: MatchSnapshotConfig, + fromPromise?: boolean, +) => { context.dontThrow && context.dontThrow(); const {isNot} = context; - const matcherName = - typeof inlineSnapshot === 'string' - ? 'toThrowErrorMatchingInlineSnapshot' - : 'toThrowErrorMatchingSnapshot'; if (isNot) { throw new Error( - `Jest: \`.not\` cannot be used with \`.${matcherName}()\`.`, + matcherHint(matcherName, undefined, expectedArgument, options) + + NOT_SNAPSHOT_MATCHERS, ); } @@ -282,16 +390,18 @@ const _toThrowErrorMatchingSnapshot = ({ if (error === undefined) { throw new Error( - matcherHint(`.${matcherName}`, '() => {}', '') + + matcherHint(matcherName, undefined, expectedArgument, options) + '\n\n' + - `Expected the function to throw an error.\n` + - `But it didn't throw anything.`, + DID_NOT_THROW, ); } return _toMatchSnapshot({ context, + expectedArgument, inlineSnapshot, + matcherName, + options, received: error.message, testName, }); From 9fd81bd2c0e51cedacee642d278d49d59d4babdf Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Fri, 15 Mar 2019 18:36:07 -0400 Subject: [PATCH 02/11] Add label to match string in toMatchSnapshot test --- e2e/__tests__/toMatchSnapshot.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/__tests__/toMatchSnapshot.test.ts b/e2e/__tests__/toMatchSnapshot.test.ts index 47a4c3cb45aa..11f2c7bd2b18 100644 --- a/e2e/__tests__/toMatchSnapshot.test.ts +++ b/e2e/__tests__/toMatchSnapshot.test.ts @@ -294,7 +294,7 @@ test('handles property matchers with deep properties', () => { writeFiles(TESTS_DIR, {[filename]: template(['new Date()', '"CHANGED"'])}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); expect(stderr).toMatch( - '`handles property matchers with deep properties 1`', + 'Snapshot key: `handles property matchers with deep properties 1`', ); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); expect(status).toBe(1); From f37837816300f70a733c5e87589ba641fd63a354 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Fri, 15 Mar 2019 18:37:40 -0400 Subject: [PATCH 03/11] Add comment about message same as toThrow --- packages/jest-snapshot/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index 894c76e96943..56309fee9e75 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -42,7 +42,7 @@ type MatchSnapshotConfig = { testName?: string; }; -const DID_NOT_THROW = 'Received function did not throw'; +const DID_NOT_THROW = 'Received function did not throw'; // same as toThrow const NOT_SNAPSHOT_MATCHERS = '\n\n.not cannot be used with snapshot matchers'; // Display key in report when matcher fails same as in snapshot file, From 509f2a8029af7d0ba2b1a4b4e77290c7f6c1928f Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Fri, 15 Mar 2019 18:52:07 -0400 Subject: [PATCH 04/11] Move blank line out of constant string --- packages/jest-snapshot/src/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index 56309fee9e75..353f23a9ca86 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -43,7 +43,7 @@ type MatchSnapshotConfig = { }; const DID_NOT_THROW = 'Received function did not throw'; // same as toThrow -const NOT_SNAPSHOT_MATCHERS = '\n\n.not cannot be used with snapshot matchers'; +const NOT_SNAPSHOT_MATCHERS = '.not cannot be used with snapshot matchers'; // Display key in report when matcher fails same as in snapshot file, // but with optional name argument in green. @@ -196,6 +196,7 @@ const _toMatchSnapshot = ({ if (isNot) { throw new Error( matcherHint(matcherName, undefined, expectedArgument, options) + + '\n\n' + NOT_SNAPSHOT_MATCHERS, ); } @@ -372,6 +373,7 @@ const _toThrowErrorMatchingSnapshot = ( if (isNot) { throw new Error( matcherHint(matcherName, undefined, expectedArgument, options) + + '\n\n' + NOT_SNAPSHOT_MATCHERS, ); } From 88ced7330bb3da6032c7b9cf89bc07841ac2f7c7 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Fri, 15 Mar 2019 18:54:18 -0400 Subject: [PATCH 05/11] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 786c91a3a910..95292c02562c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Features +- `[jest-snapshot]` Improve report when matcher fails, part 14 ([#8132](https://github.com/facebook/jest/pull/8132)) + ### Fixes ### Chore & Maintenance From 16f9556abbdb2d62aa3b7d1b6acf652ecf9e9893 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Mon, 18 Mar 2019 12:50:19 -0400 Subject: [PATCH 06/11] Add BOLD_WEIGHT and replace snapshot key/name with name/hint --- .../__snapshots__/failures.test.ts.snap | 6 +++--- .../watchModeUpdateSnapshot.test.ts.snap | 2 +- e2e/__tests__/toMatchInlineSnapshot.test.ts | 4 ++-- e2e/__tests__/toMatchSnapshot.test.ts | 12 +++++------ packages/jest-matcher-utils/src/index.ts | 1 + packages/jest-snapshot/src/index.ts | 20 ++++++++++++------- 6 files changed, 26 insertions(+), 19 deletions(-) diff --git a/e2e/__tests__/__snapshots__/failures.test.ts.snap b/e2e/__tests__/__snapshots__/failures.test.ts.snap index e5675430a4ac..3683008b3d54 100644 --- a/e2e/__tests__/__snapshots__/failures.test.ts.snap +++ b/e2e/__tests__/__snapshots__/failures.test.ts.snap @@ -387,9 +387,9 @@ FAIL __tests__/snapshotNamed.test.js ● failing named snapshot - expect(received).toMatchSnapshot(name) + expect(received).toMatchSnapshot(hint) - Snapshot key: \`failing named snapshot: snapname 1\` + Snapshot name: \`failing named snapshot: snapname 1\` - Snapshot + Received @@ -821,7 +821,7 @@ FAIL __tests__/snapshot.test.js expect(received).toMatchSnapshot() - Snapshot key: \`failing snapshot 1\` + Snapshot name: \`failing snapshot 1\` - Snapshot + Received diff --git a/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap b/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap index a6b4f19add33..db4e6ef9b129 100644 --- a/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap +++ b/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap @@ -5,7 +5,7 @@ FAIL __tests__/bar.spec.js ✕ bar ● bar expect(received).toMatchSnapshot() - Snapshot key: \`bar 1\` + Snapshot name: \`bar 1\` - Snapshot + Received - "foo" diff --git a/e2e/__tests__/toMatchInlineSnapshot.test.ts b/e2e/__tests__/toMatchInlineSnapshot.test.ts index e366802c960b..655a32d42c22 100644 --- a/e2e/__tests__/toMatchInlineSnapshot.test.ts +++ b/e2e/__tests__/toMatchInlineSnapshot.test.ts @@ -52,7 +52,7 @@ test('basic support', () => { }); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); const fileAfter = readFile(filename); - expect(stderr).toMatch('Snapshot key: `inline snapshots 1`'); + expect(stderr).toMatch('Snapshot name: `inline snapshots 1`'); expect(status).toBe(1); expect(wrap(fileAfter)).toMatchSnapshot('snapshot mismatch'); } @@ -101,7 +101,7 @@ test('handles property matchers', () => { }); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); const fileAfter = readFile(filename); - expect(stderr).toMatch('Snapshot key: `handles property matchers 1`'); + expect(stderr).toMatch('Snapshot name: `handles property matchers 1`'); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); expect(status).toBe(1); expect(wrap(fileAfter)).toMatchSnapshot('snapshot failed'); diff --git a/e2e/__tests__/toMatchSnapshot.test.ts b/e2e/__tests__/toMatchSnapshot.test.ts index 11f2c7bd2b18..705db811fe0e 100644 --- a/e2e/__tests__/toMatchSnapshot.test.ts +++ b/e2e/__tests__/toMatchSnapshot.test.ts @@ -42,7 +42,7 @@ test('basic support', () => { [filename]: template(['{apple: "updated value"}']), }); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); - expect(stderr).toMatch('Snapshot key: `snapshots 1`'); + expect(stderr).toMatch('Snapshot name: `snapshots 1`'); expect(status).toBe(1); } @@ -107,7 +107,7 @@ test('first snapshot fails, second passes', () => { { writeFiles(TESTS_DIR, {[filename]: template([`'kiwi'`, `'banana'`])}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); - expect(stderr).toMatch('Snapshot key: `snapshots 1`'); + expect(stderr).toMatch('Snapshot name: `snapshots 1`'); expect(stderr).toMatch('- "apple"\n + "kiwi"'); expect(stderr).not.toMatch('1 obsolete snapshot found'); expect(status).toBe(1); @@ -178,7 +178,7 @@ test('handles property matchers', () => { { writeFiles(TESTS_DIR, {[filename]: template(['"string"'])}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); - expect(stderr).toMatch('Snapshot key: `handles property matchers 1`'); + expect(stderr).toMatch('Snapshot name: `handles property matchers 1`'); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); expect(status).toBe(1); } @@ -251,7 +251,7 @@ test('handles property matchers with custom name', () => { writeFiles(TESTS_DIR, {[filename]: template(['"string"'])}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); expect(stderr).toMatch( - 'Snapshot key: `handles property matchers with name: custom-name 1`', + 'Snapshot name: `handles property matchers with name: custom-name 1`', ); expect(stderr).toMatch('Expected properties:'); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); @@ -283,7 +283,7 @@ test('handles property matchers with deep properties', () => { writeFiles(TESTS_DIR, {[filename]: template(['"string"', '"Jest"'])}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); expect(stderr).toMatch( - 'Snapshot key: `handles property matchers with deep properties 1`', + 'Snapshot name: `handles property matchers with deep properties 1`', ); expect(stderr).toMatch('Expected properties:'); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); @@ -294,7 +294,7 @@ test('handles property matchers with deep properties', () => { writeFiles(TESTS_DIR, {[filename]: template(['new Date()', '"CHANGED"'])}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); expect(stderr).toMatch( - 'Snapshot key: `handles property matchers with deep properties 1`', + 'Snapshot name: `handles property matchers with deep properties 1`', ); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); expect(status).toBe(1); diff --git a/packages/jest-matcher-utils/src/index.ts b/packages/jest-matcher-utils/src/index.ts index 75e4b53bdcf2..89fd60568e6b 100644 --- a/packages/jest-matcher-utils/src/index.ts +++ b/packages/jest-matcher-utils/src/index.ts @@ -40,6 +40,7 @@ export {DiffOptions}; export const EXPECTED_COLOR = chalk.green; export const RECEIVED_COLOR = chalk.red; export const INVERTED_COLOR = chalk.inverse; +export const BOLD_WEIGHT = chalk.bold; const DIM_COLOR = chalk.dim; const NUMBERS = [ diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index 353f23a9ca86..0a2dfb2c18be 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -12,6 +12,7 @@ import {MatcherState} from 'expect'; import diff from 'jest-diff'; import { + BOLD_WEIGHT, EXPECTED_COLOR, matcherHint, MatcherHintOptions, @@ -43,7 +44,9 @@ type MatchSnapshotConfig = { }; const DID_NOT_THROW = 'Received function did not throw'; // same as toThrow -const NOT_SNAPSHOT_MATCHERS = '.not cannot be used with snapshot matchers'; +const NOT_SNAPSHOT_MATCHERS = `.${BOLD_WEIGHT( + 'not', +)} cannot be used with snapshot matchers`; // Display key in report when matcher fails same as in snapshot file, // but with optional name argument in green. @@ -55,7 +58,7 @@ const printKey = (path = '', name = '', count: number): string => { '`' + (hasPath ? utils.escapeBacktickString(path) : '') + (hasPath && hasName ? ': ' : '') + - (hasName ? EXPECTED_COLOR(utils.escapeBacktickString(name)) : '') + + (hasName ? BOLD_WEIGHT(utils.escapeBacktickString(name)) : '') + ' ' + count + '`' @@ -101,13 +104,13 @@ const toMatchSnapshot = function( if (typeof propertyMatchers === 'object' && propertyMatchers !== null) { expectedArgument = 'properties'; if (typeof testName === 'string' && testName.length !== 0) { - secondArgument = 'name'; + secondArgument = BOLD_WEIGHT('hint'); } } else if ( typeof propertyMatchers === 'string' && propertyMatchers.length !== 0 ) { - expectedArgument = 'name'; + expectedArgument = BOLD_WEIGHT('hint'); } const options: MatcherHintOptions = { @@ -228,7 +231,8 @@ const _toMatchSnapshot = ({ const count = matched === null ? 1 : Number(matched[1]); const report = () => - `Snapshot key: ${printKey(currentTestName, testName, count)}\n\n` + + `Snapshot name: ${printKey(currentTestName, testName, count)}\n` + + '\n' + `Expected properties: ${context.utils.printExpected( propertyMatchers, )}\n` + @@ -278,7 +282,7 @@ const _toMatchSnapshot = ({ }); report = () => - `Snapshot key: ${printKey(currentTestName, testName, count)}\n\n` + + `Snapshot name: ${printKey(currentTestName, testName, count)}\n\n` + (diffMessage || EXPECTED_COLOR('- ' + (expected || '')) + '\n' + @@ -308,7 +312,9 @@ const toThrowErrorMatchingSnapshot = function( ) { const matcherName = 'toThrowErrorMatchingSnapshot'; const expectedArgument = - typeof testName === 'string' && testName.length !== 0 ? 'name' : ''; + typeof testName === 'string' && testName.length !== 0 + ? BOLD_WEIGHT('hint') + : ''; const options = { isNot: this.isNot, promise: this.promise, From 7c1e245502d6be2346d1a389ea2c3707560b1a96 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Mon, 18 Mar 2019 12:56:22 -0400 Subject: [PATCH 07/11] Replace snapshot key/name with name/hint in ExpectAPI.md --- docs/ExpectAPI.md | 8 ++++---- website/versioned_docs/version-24.0/ExpectAPI.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/ExpectAPI.md b/docs/ExpectAPI.md index d6c923774093..8548d442ad04 100644 --- a/docs/ExpectAPI.md +++ b/docs/ExpectAPI.md @@ -1113,13 +1113,13 @@ test('this house has my desired features', () => { }); ``` -### `.toMatchSnapshot(propertyMatchers?, snapshotName?)` +### `.toMatchSnapshot(propertyMatchers?, snapshotHint?)` This ensures that a value matches the most recent snapshot. Check out [the Snapshot Testing guide](SnapshotTesting.md) for more information. You can provide an optional `propertyMatchers` object argument, which has asymmetric matchers as values of a subset of expected properties, **if** the received value will be an **object** instance. It is like `toMatchObject` with flexible criteria for a subset of properties, followed by a snapshot test as exact criteria for the rest of the properties. -You can provide an optional `snapshotName` string argument that is appended to the test name. Jest always appends a number at the end of a snapshot key to differentiate snapshots from a single `it` or `test` block. Jest sorts snapshots by key in the corresponding `.snap` file. +You can provide an optional `snapshotHint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. _Note: While snapshot testing is most commonly used with React components, any serializable value can be used as a snapshot._ @@ -1213,11 +1213,11 @@ test('throws on octopus', () => { > Note: You must wrap the code in a function, otherwise the error will not be caught and the assertion will fail. -### `.toThrowErrorMatchingSnapshot(snapshotName?)` +### `.toThrowErrorMatchingSnapshot(snapshotHint?)` Use `.toThrowErrorMatchingSnapshot` to test that a function throws an error matching the most recent snapshot when it is called. -You can provide an optional `snapshotName` string argument that is appended to the test name. Jest always appends a number at the end of a snapshot key to differentiate snapshots from a single `it` or `test` block. Jest sorts snapshots by key in the corresponding `.snap` file. +You can provide an optional `snapshotHint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. For example, let's say you have a `drinkFlavor` function that throws whenever the flavor is `'octopus'`, and is coded like this: diff --git a/website/versioned_docs/version-24.0/ExpectAPI.md b/website/versioned_docs/version-24.0/ExpectAPI.md index 44d213022d83..f6ed31bd6bec 100644 --- a/website/versioned_docs/version-24.0/ExpectAPI.md +++ b/website/versioned_docs/version-24.0/ExpectAPI.md @@ -1114,13 +1114,13 @@ test('this house has my desired features', () => { }); ``` -### `.toMatchSnapshot(propertyMatchers?, snapshotName?)` +### `.toMatchSnapshot(propertyMatchers?, snapshotHint?)` This ensures that a value matches the most recent snapshot. Check out [the Snapshot Testing guide](SnapshotTesting.md) for more information. You can provide an optional `propertyMatchers` object argument, which has asymmetric matchers as values of a subset of expected properties, **if** the received value will be an **object** instance. It is like `toMatchObject` with flexible criteria for a subset of properties, followed by a snapshot test as exact criteria for the rest of the properties. -You can provide an optional `snapshotName` string argument that is appended to the test name. Jest always appends a number at the end of a snapshot key to differentiate snapshots from a single `it` or `test` block. Jest sorts snapshots by key in the corresponding `.snap` file. +You can provide an optional `snapshotHint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. _Note: While snapshot testing is most commonly used with React components, any serializable value can be used as a snapshot._ @@ -1214,11 +1214,11 @@ test('throws on octopus', () => { > Note: You must wrap the code in a function, otherwise the error will not be caught and the assertion will fail. -### `.toThrowErrorMatchingSnapshot(snapshotName?)` +### `.toThrowErrorMatchingSnapshot(snapshotHint?)` Use `.toThrowErrorMatchingSnapshot` to test that a function throws an error matching the most recent snapshot when it is called. -You can provide an optional `snapshotName` string argument that is appended to the test name. Jest always appends a number at the end of a snapshot key to differentiate snapshots from a single `it` or `test` block. Jest sorts snapshots by key in the corresponding `.snap` file. +You can provide an optional `snapshotHint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. For example, let's say you have a `drinkFlavor` function that throws whenever the flavor is `'octopus'`, and is coded like this: From f1eb86841f7e043677ec6244eef3c1c1e8c42b66 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Mon, 18 Mar 2019 15:08:25 -0400 Subject: [PATCH 08/11] Replace snapshotHint with hint in ExpectAPI.md --- docs/ExpectAPI.md | 8 ++++---- website/versioned_docs/version-24.0/ExpectAPI.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/ExpectAPI.md b/docs/ExpectAPI.md index 8548d442ad04..93d8e0c51d90 100644 --- a/docs/ExpectAPI.md +++ b/docs/ExpectAPI.md @@ -1113,13 +1113,13 @@ test('this house has my desired features', () => { }); ``` -### `.toMatchSnapshot(propertyMatchers?, snapshotHint?)` +### `.toMatchSnapshot(propertyMatchers?, hint?)` This ensures that a value matches the most recent snapshot. Check out [the Snapshot Testing guide](SnapshotTesting.md) for more information. You can provide an optional `propertyMatchers` object argument, which has asymmetric matchers as values of a subset of expected properties, **if** the received value will be an **object** instance. It is like `toMatchObject` with flexible criteria for a subset of properties, followed by a snapshot test as exact criteria for the rest of the properties. -You can provide an optional `snapshotHint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. +You can provide an optional `hint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. _Note: While snapshot testing is most commonly used with React components, any serializable value can be used as a snapshot._ @@ -1213,11 +1213,11 @@ test('throws on octopus', () => { > Note: You must wrap the code in a function, otherwise the error will not be caught and the assertion will fail. -### `.toThrowErrorMatchingSnapshot(snapshotHint?)` +### `.toThrowErrorMatchingSnapshot(hint?)` Use `.toThrowErrorMatchingSnapshot` to test that a function throws an error matching the most recent snapshot when it is called. -You can provide an optional `snapshotHint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. +You can provide an optional `hint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. For example, let's say you have a `drinkFlavor` function that throws whenever the flavor is `'octopus'`, and is coded like this: diff --git a/website/versioned_docs/version-24.0/ExpectAPI.md b/website/versioned_docs/version-24.0/ExpectAPI.md index f6ed31bd6bec..49e0d5870348 100644 --- a/website/versioned_docs/version-24.0/ExpectAPI.md +++ b/website/versioned_docs/version-24.0/ExpectAPI.md @@ -1114,13 +1114,13 @@ test('this house has my desired features', () => { }); ``` -### `.toMatchSnapshot(propertyMatchers?, snapshotHint?)` +### `.toMatchSnapshot(propertyMatchers?, hint?)` This ensures that a value matches the most recent snapshot. Check out [the Snapshot Testing guide](SnapshotTesting.md) for more information. You can provide an optional `propertyMatchers` object argument, which has asymmetric matchers as values of a subset of expected properties, **if** the received value will be an **object** instance. It is like `toMatchObject` with flexible criteria for a subset of properties, followed by a snapshot test as exact criteria for the rest of the properties. -You can provide an optional `snapshotHint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. +You can provide an optional `hint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. _Note: While snapshot testing is most commonly used with React components, any serializable value can be used as a snapshot._ @@ -1214,11 +1214,11 @@ test('throws on octopus', () => { > Note: You must wrap the code in a function, otherwise the error will not be caught and the assertion will fail. -### `.toThrowErrorMatchingSnapshot(snapshotHint?)` +### `.toThrowErrorMatchingSnapshot(hint?)` Use `.toThrowErrorMatchingSnapshot` to test that a function throws an error matching the most recent snapshot when it is called. -You can provide an optional `snapshotHint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. +You can provide an optional `hint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. For example, let's say you have a `drinkFlavor` function that throws whenever the flavor is `'octopus'`, and is coded like this: From f7bdf6bd439138e1edc22c7395189aeb8507837c Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Mon, 18 Mar 2019 16:41:18 -0400 Subject: [PATCH 09/11] Rename existing tests of optional hint argument --- .../__snapshots__/failures.test.ts.snap | 58 +++++++++---------- e2e/__tests__/failures.test.ts | 4 +- e2e/__tests__/toMatchSnapshot.test.ts | 10 ++-- .../__snapshots__/snapshotNamed.test.js.snap | 3 - .../snapshotWithHint.test.js.snap | 3 + ...Named.test.js => snapshotWithHint.test.js} | 4 +- 6 files changed, 41 insertions(+), 41 deletions(-) delete mode 100644 e2e/failures/__tests__/__snapshots__/snapshotNamed.test.js.snap create mode 100644 e2e/failures/__tests__/__snapshots__/snapshotWithHint.test.js.snap rename e2e/failures/__tests__/{snapshotNamed.test.js => snapshotWithHint.test.js} (72%) diff --git a/e2e/__tests__/__snapshots__/failures.test.ts.snap b/e2e/__tests__/__snapshots__/failures.test.ts.snap index 3683008b3d54..39f7e80eb37d 100644 --- a/e2e/__tests__/__snapshots__/failures.test.ts.snap +++ b/e2e/__tests__/__snapshots__/failures.test.ts.snap @@ -381,35 +381,6 @@ FAIL __tests__/asyncFailures.test.js at Object.test (__tests__/asyncFailures.test.js:23:1) `; -exports[`works with named snapshot failures 1`] = ` -FAIL __tests__/snapshotNamed.test.js - ✕ failing named snapshot - - ● failing named snapshot - - expect(received).toMatchSnapshot(hint) - - Snapshot name: \`failing named snapshot: snapname 1\` - - - Snapshot - + Received - - - "bar" - + "foo" - - 10 | - 11 | test('failing named snapshot', () => { - > 12 | expect('foo').toMatchSnapshot('snapname'); - | ^ - 13 | }); - 14 | - - at Object.toMatchSnapshot (__tests__/snapshotNamed.test.js:12:17) - - › 1 snapshot failed. - -`; - exports[`works with node assert 1`] = ` FAIL __tests__/assertionError.test.js ✕ assert @@ -841,3 +812,32 @@ FAIL __tests__/snapshot.test.js › 1 snapshot failed. `; + +exports[`works with snapshot failures with hint 1`] = ` +FAIL __tests__/snapshotWithHint.test.js + ✕ failing snapshot with hint + + ● failing snapshot with hint + + expect(received).toMatchSnapshot(hint) + + Snapshot name: \`failing snapshot with hint: descriptive hint 1\` + + - Snapshot + + Received + + - "bar" + + "foo" + + 10 | + 11 | test('failing snapshot with hint', () => { + > 12 | expect('foo').toMatchSnapshot('descriptive hint'); + | ^ + 13 | }); + 14 | + + at Object.toMatchSnapshot (__tests__/snapshotWithHint.test.js:12:17) + + › 1 snapshot failed. + +`; diff --git a/e2e/__tests__/failures.test.ts b/e2e/__tests__/failures.test.ts index 851bb3d1a39b..1fba31a77fae 100644 --- a/e2e/__tests__/failures.test.ts +++ b/e2e/__tests__/failures.test.ts @@ -173,8 +173,8 @@ test('works with snapshot failures', () => { ).toMatchSnapshot(); }); -test('works with named snapshot failures', () => { - const {stderr} = runJest(dir, ['snapshotNamed.test.js']); +test('works with snapshot failures with hint', () => { + const {stderr} = runJest(dir, ['snapshotWithHint.test.js']); const result = normalizeDots(cleanStderr(stderr)); diff --git a/e2e/__tests__/toMatchSnapshot.test.ts b/e2e/__tests__/toMatchSnapshot.test.ts index 705db811fe0e..9ee514bcd5a0 100644 --- a/e2e/__tests__/toMatchSnapshot.test.ts +++ b/e2e/__tests__/toMatchSnapshot.test.ts @@ -227,10 +227,10 @@ test('handles invalid property matchers', () => { } }); -test('handles property matchers with custom name', () => { - const filename = 'handle-property-matchers-with-name.test.js'; - const template = makeTemplate(`test('handles property matchers with name', () => { - expect({createdAt: $1}).toMatchSnapshot({createdAt: expect.any(Date)}, 'custom-name'); +test('handles property matchers with hint', () => { + const filename = 'handle-property-matchers-with-hint.test.js'; + const template = makeTemplate(`test('handles property matchers with hint', () => { + expect({createdAt: $1}).toMatchSnapshot({createdAt: expect.any(Date)}, 'descriptive hint'); }); `); @@ -251,7 +251,7 @@ test('handles property matchers with custom name', () => { writeFiles(TESTS_DIR, {[filename]: template(['"string"'])}); const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false', filename]); expect(stderr).toMatch( - 'Snapshot name: `handles property matchers with name: custom-name 1`', + 'Snapshot name: `handles property matchers with hint: descriptive hint 1`', ); expect(stderr).toMatch('Expected properties:'); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); diff --git a/e2e/failures/__tests__/__snapshots__/snapshotNamed.test.js.snap b/e2e/failures/__tests__/__snapshots__/snapshotNamed.test.js.snap deleted file mode 100644 index 593f8e2e92d5..000000000000 --- a/e2e/failures/__tests__/__snapshots__/snapshotNamed.test.js.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`failing named snapshot: snapname 1`] = `"bar"`; diff --git a/e2e/failures/__tests__/__snapshots__/snapshotWithHint.test.js.snap b/e2e/failures/__tests__/__snapshots__/snapshotWithHint.test.js.snap new file mode 100644 index 000000000000..1d2712c8f4af --- /dev/null +++ b/e2e/failures/__tests__/__snapshots__/snapshotWithHint.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`failing snapshot with hint: descriptive hint 1`] = `"bar"`; diff --git a/e2e/failures/__tests__/snapshotNamed.test.js b/e2e/failures/__tests__/snapshotWithHint.test.js similarity index 72% rename from e2e/failures/__tests__/snapshotNamed.test.js rename to e2e/failures/__tests__/snapshotWithHint.test.js index 8cd4004187a6..be7ee8efdc53 100644 --- a/e2e/failures/__tests__/snapshotNamed.test.js +++ b/e2e/failures/__tests__/snapshotWithHint.test.js @@ -8,6 +8,6 @@ */ 'use strict'; -test('failing named snapshot', () => { - expect('foo').toMatchSnapshot('snapname'); +test('failing snapshot with hint', () => { + expect('foo').toMatchSnapshot('descriptive hint'); }); From 91fb089343e27d89da80c0c8ab14ecf8177eaefa Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Tue, 19 Mar 2019 10:14:30 -0400 Subject: [PATCH 10/11] Rename testName as hint in code --- packages/jest-snapshot/src/index.ts | 74 ++++++++++++++++------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index 0a2dfb2c18be..b7d336ab5418 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -35,12 +35,12 @@ type Context = MatcherState & { type MatchSnapshotConfig = { context: Context; expectedArgument: string; + hint?: string; inlineSnapshot?: string; matcherName: string; options: MatcherHintOptions; propertyMatchers?: any; received: any; - testName?: string; }; const DID_NOT_THROW = 'Received function did not throw'; // same as toThrow @@ -48,17 +48,25 @@ const NOT_SNAPSHOT_MATCHERS = `.${BOLD_WEIGHT( 'not', )} cannot be used with snapshot matchers`; -// Display key in report when matcher fails same as in snapshot file, -// but with optional name argument in green. -const printKey = (path = '', name = '', count: number): string => { - const hasPath = path.length !== 0; - const hasName = name.length !== 0; +const HINT_ARG = BOLD_WEIGHT('hint'); +const INLINE_SNAPSHOT_ARG = 'snapshot'; +const PROPERTY_MATCHERS_ARG = 'properties'; + +// Display name in report when matcher fails same as in snapshot file, +// but with optional hint argument in bold weight. +const printName = ( + concatenatedBlockNames = '', + hint = '', + count: number, +): string => { + const hasNames = concatenatedBlockNames.length !== 0; + const hasHint = hint.length !== 0; return ( '`' + - (hasPath ? utils.escapeBacktickString(path) : '') + - (hasPath && hasName ? ': ' : '') + - (hasName ? BOLD_WEIGHT(utils.escapeBacktickString(name)) : '') + + (hasNames ? utils.escapeBacktickString(concatenatedBlockNames) : '') + + (hasNames && hasHint ? ': ' : '') + + (hasHint ? BOLD_WEIGHT(utils.escapeBacktickString(hint)) : '') + ' ' + count + '`' @@ -95,22 +103,22 @@ const toMatchSnapshot = function( this: Context, received: any, propertyMatchers?: any, - testName?: Config.Path, + hint?: Config.Path, ) { const matcherName = 'toMatchSnapshot'; let expectedArgument = ''; let secondArgument = ''; if (typeof propertyMatchers === 'object' && propertyMatchers !== null) { - expectedArgument = 'properties'; - if (typeof testName === 'string' && testName.length !== 0) { - secondArgument = BOLD_WEIGHT('hint'); + expectedArgument = PROPERTY_MATCHERS_ARG; + if (typeof hint === 'string' && hint.length !== 0) { + secondArgument = HINT_ARG; } } else if ( typeof propertyMatchers === 'string' && propertyMatchers.length !== 0 ) { - expectedArgument = BOLD_WEIGHT('hint'); + expectedArgument = HINT_ARG; } const options: MatcherHintOptions = { @@ -128,11 +136,11 @@ const toMatchSnapshot = function( return _toMatchSnapshot({ context: this, expectedArgument, + hint, matcherName, options, propertyMatchers, received, - testName, }); }; @@ -147,14 +155,14 @@ const toMatchInlineSnapshot = function( let secondArgument = ''; if (typeof propertyMatchersOrInlineSnapshot === 'string') { - expectedArgument = 'snapshot'; + expectedArgument = INLINE_SNAPSHOT_ARG; } else if ( typeof propertyMatchersOrInlineSnapshot === 'object' && propertyMatchersOrInlineSnapshot !== null ) { - expectedArgument = 'properties'; + expectedArgument = PROPERTY_MATCHERS_ARG; if (typeof inlineSnapshot === 'string') { - secondArgument = 'snapshot'; + secondArgument = INLINE_SNAPSHOT_ARG; } } @@ -170,6 +178,7 @@ const toMatchInlineSnapshot = function( } else { propertyMatchers = propertyMatchersOrInlineSnapshot; } + return _toMatchSnapshot({ context: this, expectedArgument, @@ -189,10 +198,10 @@ const _toMatchSnapshot = ({ options, propertyMatchers, received, - testName, + hint, }: MatchSnapshotConfig) => { context.dontThrow && context.dontThrow(); - testName = typeof propertyMatchers === 'string' ? propertyMatchers : testName; + hint = typeof propertyMatchers === 'string' ? propertyMatchers : hint; const {currentTestName, isNot, snapshotState} = context; @@ -212,9 +221,9 @@ const _toMatchSnapshot = ({ } const fullTestName = - testName && currentTestName - ? `${currentTestName}: ${testName}` - : currentTestName || ''; + currentTestName && hint + ? `${currentTestName}: ${hint}` + : currentTestName || ''; // future BREAKING change: || hint if (typeof propertyMatchers === 'object') { if (propertyMatchers === null) { @@ -231,7 +240,7 @@ const _toMatchSnapshot = ({ const count = matched === null ? 1 : Number(matched[1]); const report = () => - `Snapshot name: ${printKey(currentTestName, testName, count)}\n` + + `Snapshot name: ${printName(currentTestName, hint, count)}\n` + '\n' + `Expected properties: ${context.utils.printExpected( propertyMatchers, @@ -282,7 +291,7 @@ const _toMatchSnapshot = ({ }); report = () => - `Snapshot name: ${printKey(currentTestName, testName, count)}\n\n` + + `Snapshot name: ${printName(currentTestName, hint, count)}\n\n` + (diffMessage || EXPECTED_COLOR('- ' + (expected || '')) + '\n' + @@ -307,14 +316,12 @@ const _toMatchSnapshot = ({ const toThrowErrorMatchingSnapshot = function( this: Context, received: any, - testName: string | undefined, // because error TS1016 for testName?: string + hint: string | undefined, // because error TS1016 for hint?: string fromPromise: boolean, ) { const matcherName = 'toThrowErrorMatchingSnapshot'; const expectedArgument = - typeof testName === 'string' && testName.length !== 0 - ? BOLD_WEIGHT('hint') - : ''; + typeof hint === 'string' && hint.length !== 0 ? HINT_ARG : ''; const options = { isNot: this.isNot, promise: this.promise, @@ -325,10 +332,10 @@ const toThrowErrorMatchingSnapshot = function( { context: this, expectedArgument, + hint, matcherName, options, received, - testName, }, fromPromise, ); @@ -341,7 +348,8 @@ const toThrowErrorMatchingInlineSnapshot = function( fromPromise?: boolean, ) { const matcherName = 'toThrowErrorMatchingInlineSnapshot'; - const expectedArgument = typeof inlineSnapshot === 'string' ? 'snapshot' : ''; + const expectedArgument = + typeof inlineSnapshot === 'string' ? INLINE_SNAPSHOT_ARG : ''; const options: MatcherHintOptions = { isNot: this.isNot, promise: this.promise, @@ -369,7 +377,7 @@ const _toThrowErrorMatchingSnapshot = ( matcherName, options, received, - testName, + hint, }: MatchSnapshotConfig, fromPromise?: boolean, ) => { @@ -407,11 +415,11 @@ const _toThrowErrorMatchingSnapshot = ( return _toMatchSnapshot({ context, expectedArgument, + hint, inlineSnapshot, matcherName, options, received: error.message, - testName, }); }; From c49b73e04086c06e5f26b0a913b00244dbfd8c4f Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Tue, 19 Mar 2019 10:41:09 -0400 Subject: [PATCH 11/11] Reorder hint in destructuring --- packages/jest-snapshot/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index b7d336ab5418..d468c7a7dc01 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -193,12 +193,12 @@ const toMatchInlineSnapshot = function( const _toMatchSnapshot = ({ context, expectedArgument, + hint, inlineSnapshot, matcherName, options, propertyMatchers, received, - hint, }: MatchSnapshotConfig) => { context.dontThrow && context.dontThrow(); hint = typeof propertyMatchers === 'string' ? propertyMatchers : hint;