From fe6a6cdab0e8379469e213773d98c8c2e54e2dec Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Fri, 14 Jun 2019 12:10:27 -0400 Subject: [PATCH 01/17] expect: Highlight substring differences when matcher fails, part 3 --- .../__snapshots__/failures.test.ts.snap | 14 +++----- .../watchModeUpdateSnapshot.test.ts.snap | 6 ++-- e2e/__tests__/toMatchSnapshot.test.ts | 2 +- packages/jest-snapshot/src/index.ts | 32 +++++++++++++++++-- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/e2e/__tests__/__snapshots__/failures.test.ts.snap b/e2e/__tests__/__snapshots__/failures.test.ts.snap index f527fca5e837..6e2ea6c5dafd 100644 --- a/e2e/__tests__/__snapshots__/failures.test.ts.snap +++ b/e2e/__tests__/__snapshots__/failures.test.ts.snap @@ -790,11 +790,8 @@ FAIL __tests__/snapshot.test.js Snapshot name: \`failing snapshot 1\` - - Snapshot - + Received - - - "bar" - + "foo" + Snapshot: "bar" + Received: "foo" 9 | 10 | test('failing snapshot', () => { @@ -819,11 +816,8 @@ FAIL __tests__/snapshotWithHint.test.js Snapshot name: \`failing snapshot with hint: descriptive hint 1\` - - Snapshot - + Received - - - "bar" - + "foo" + Snapshot: "bar" + Received: "foo" 9 | 10 | test('failing snapshot with hint', () => { diff --git a/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap b/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap index db4e6ef9b129..2ad72b2cb373 100644 --- a/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap +++ b/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap @@ -6,10 +6,8 @@ FAIL __tests__/bar.spec.js ● bar expect(received).toMatchSnapshot() Snapshot name: \`bar 1\` - - Snapshot - + Received - - "foo" - + "bar" + Snapshot: "foo" + Received: "bar" 1 | > 2 | test('bar', () => { expect('bar').toMatchSnapshot(); }); | ^ diff --git a/e2e/__tests__/toMatchSnapshot.test.ts b/e2e/__tests__/toMatchSnapshot.test.ts index 9ee514bcd5a0..803c3ae70387 100644 --- a/e2e/__tests__/toMatchSnapshot.test.ts +++ b/e2e/__tests__/toMatchSnapshot.test.ts @@ -108,7 +108,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 name: `snapshots 1`'); - expect(stderr).toMatch('- "apple"\n + "kiwi"'); + expect(stderr).toMatch('Snapshot: "apple"\n Received: "kiwi"'); expect(stderr).not.toMatch('1 obsolete snapshot found'); expect(status).toBe(1); } diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index b579e279e7a8..7685c9125f6a 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -16,6 +16,7 @@ import { EXPECTED_COLOR, matcherHint, MatcherHintOptions, + printDiffOrStringify, RECEIVED_COLOR, } from 'jest-matcher-utils'; import { @@ -48,6 +49,12 @@ const NOT_SNAPSHOT_MATCHERS = `.${BOLD_WEIGHT( 'not', )} cannot be used with snapshot matchers`; +const SNAPSHOT_LABEL = 'Snapshot'; +const RECEIVED_LABEL = 'Received'; + +// The optional property of matcher context is true if undefined. +const isExpand = (expand?: boolean): boolean => expand !== false; + const HINT_ARG = BOLD_WEIGHT('hint'); const INLINE_SNAPSHOT_ARG = 'snapshot'; const PROPERTY_MATCHERS_ARG = 'properties'; @@ -324,12 +331,33 @@ const _toMatchSnapshot = ({ `(CI) environment in which snapshots are not written by default.\n\n` + `${RECEIVED_COLOR('Received value')} ` + `${actual}`; + } else if ( + typeof received === 'string' && + typeof expected === 'string' && + // Does expected snapshot look like a stringified string: + expected.length >= 2 && + ((expected.startsWith('"') && expected.endsWith('"')) || // single line + (expected.startsWith('\n"') && expected.endsWith('"\n'))) // // multi line + ) { + // Assign to local variable because of declaration let expected: + // TypeScript thinks it could change before report function is called. + const reported = + `Snapshot name: ${printName(currentTestName, hint, count)}\n\n` + + printDiffOrStringify( + expected.trim().slice(1, -1), + received, + SNAPSHOT_LABEL, + RECEIVED_LABEL, + isExpand(snapshotState.expand), + ); + + report = () => reported; } else { expected = (expected || '').trim(); actual = (actual || '').trim(); const diffMessage = diff(expected, actual, { - aAnnotation: 'Snapshot', - bAnnotation: 'Received', + aAnnotation: SNAPSHOT_LABEL, + bAnnotation: RECEIVED_LABEL, expand: snapshotState.expand, }); From e7c53990aee4360d05d002d4558e6b4b4ab9f0d2 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Fri, 14 Jun 2019 12:14:33 -0400 Subject: [PATCH 02/17] Delete duplicate line comment slashes --- 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 7685c9125f6a..6d4fb7288356 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -337,7 +337,7 @@ const _toMatchSnapshot = ({ // Does expected snapshot look like a stringified string: expected.length >= 2 && ((expected.startsWith('"') && expected.endsWith('"')) || // single line - (expected.startsWith('\n"') && expected.endsWith('"\n'))) // // multi line + (expected.startsWith('\n"') && expected.endsWith('"\n'))) // multi line ) { // Assign to local variable because of declaration let expected: // TypeScript thinks it could change before report function is called. From 3c91975ed7c2402c5ee3ba95859e9d4de2575027 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Fri, 14 Jun 2019 12:16:06 -0400 Subject: [PATCH 03/17] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fabc33277c85..02421c384687 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - `[expect]` Highlight substring differences when matcher fails, part 1 ([#8448](https://github.com/facebook/jest/pull/8448)) - `[expect]` Highlight substring differences when matcher fails, part 2 ([#8528](https://github.com/facebook/jest/pull/8528)) +- `[expect]` Highlight substring differences when matcher fails, part 3 ([#8569](https://github.com/facebook/jest/pull/8569)) - `[jest-cli]` Improve chai support (with detailed output, to match jest exceptions) ([#8454](https://github.com/facebook/jest/pull/8454)) - `[*]` Manage the global timeout with `--testTimeout` command line argument. ([#8456](https://github.com/facebook/jest/pull/8456)) - `[pretty-format]` Render custom displayName of memoized components From b9897cb55b5dec4c2d968dd480c1f9335deb07b8 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Fri, 14 Jun 2019 16:53:24 -0400 Subject: [PATCH 04/17] Remove backslash escape preceding backslash --- packages/jest-snapshot/src/index.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index 6d4fb7288356..64ec96d0d3a2 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -344,7 +344,14 @@ const _toMatchSnapshot = ({ const reported = `Snapshot name: ${printName(currentTestName, hint, count)}\n\n` + printDiffOrStringify( - expected.trim().slice(1, -1), + // 1. Remove leading and trailing newline if multiple line string. + // 2. Remove enclosing double quote marks. + // 3. Remove backslash escape preceding backslash here, + // because unescape replaced it only preceding double quote mark. + expected + .trim() + .slice(1, -1) + .replace(/\\\\/g, '\\'), received, SNAPSHOT_LABEL, RECEIVED_LABEL, From 5e3009a3f443b77a0da7865903d6eba065cb0bb0 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Sat, 15 Jun 2019 11:41:41 -0400 Subject: [PATCH 05/17] Replace expect with jest-snapshot in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02421c384687..1d775180362e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - `[expect]` Highlight substring differences when matcher fails, part 1 ([#8448](https://github.com/facebook/jest/pull/8448)) - `[expect]` Highlight substring differences when matcher fails, part 2 ([#8528](https://github.com/facebook/jest/pull/8528)) -- `[expect]` Highlight substring differences when matcher fails, part 3 ([#8569](https://github.com/facebook/jest/pull/8569)) +- `[jest-snapshot]` Highlight substring differences when matcher fails, part 3 ([#8569](https://github.com/facebook/jest/pull/8569)) - `[jest-cli]` Improve chai support (with detailed output, to match jest exceptions) ([#8454](https://github.com/facebook/jest/pull/8454)) - `[*]` Manage the global timeout with `--testTimeout` command line argument. ([#8456](https://github.com/facebook/jest/pull/8456)) - `[pretty-format]` Render custom displayName of memoized components From 31b142ea1895000450cbaa3ec29fe110444e81f4 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Sat, 15 Jun 2019 11:57:39 -0400 Subject: [PATCH 06/17] Delete unnecessary isExpand function --- packages/jest-snapshot/src/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index 64ec96d0d3a2..083fb40f7094 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -52,9 +52,6 @@ const NOT_SNAPSHOT_MATCHERS = `.${BOLD_WEIGHT( const SNAPSHOT_LABEL = 'Snapshot'; const RECEIVED_LABEL = 'Received'; -// The optional property of matcher context is true if undefined. -const isExpand = (expand?: boolean): boolean => expand !== false; - const HINT_ARG = BOLD_WEIGHT('hint'); const INLINE_SNAPSHOT_ARG = 'snapshot'; const PROPERTY_MATCHERS_ARG = 'properties'; @@ -355,7 +352,7 @@ const _toMatchSnapshot = ({ received, SNAPSHOT_LABEL, RECEIVED_LABEL, - isExpand(snapshotState.expand), + snapshotState.expand, ); report = () => reported; From f3ce45ee23fe84a8c6d0ac11cc0c4c01dd2d95a6 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Sat, 15 Jun 2019 14:03:56 -0400 Subject: [PATCH 07/17] Factor out printDiffOrStringified into added print.ts file --- packages/jest-snapshot/package.json | 1 + packages/jest-snapshot/src/index.ts | 58 +++++-------------- packages/jest-snapshot/src/print.ts | 83 ++++++++++++++++++++++++++++ packages/jest-snapshot/tsconfig.json | 1 + 4 files changed, 99 insertions(+), 44 deletions(-) create mode 100644 packages/jest-snapshot/src/print.ts diff --git a/packages/jest-snapshot/package.json b/packages/jest-snapshot/package.json index df97bb2bd40a..2a300d51b6ff 100644 --- a/packages/jest-snapshot/package.json +++ b/packages/jest-snapshot/package.json @@ -15,6 +15,7 @@ "chalk": "^2.0.1", "expect": "^24.8.0", "jest-diff": "^24.8.0", + "jest-get-type": "^24.8.0", "jest-matcher-utils": "^24.8.0", "jest-message-util": "^24.8.0", "jest-resolve": "^24.8.0", diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index 083fb40f7094..dd9978d1acb5 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -10,13 +10,10 @@ import {Config} from '@jest/types'; import {FS as HasteFS} from 'jest-haste-map'; // eslint-disable-line import/no-extraneous-dependencies import {MatcherState} from 'expect'; -import diff from 'jest-diff'; import { BOLD_WEIGHT, - EXPECTED_COLOR, matcherHint, MatcherHintOptions, - printDiffOrStringify, RECEIVED_COLOR, } from 'jest-matcher-utils'; import { @@ -27,6 +24,7 @@ import { } from './snapshot_resolver'; import SnapshotState from './State'; import {addSerializer, getSerializers} from './plugins'; +import {printDiffOrStringified} from './print'; import * as utils from './utils'; type Context = MatcherState & { @@ -49,9 +47,6 @@ const NOT_SNAPSHOT_MATCHERS = `.${BOLD_WEIGHT( 'not', )} cannot be used with snapshot matchers`; -const SNAPSHOT_LABEL = 'Snapshot'; -const RECEIVED_LABEL = 'Received'; - const HINT_ARG = BOLD_WEIGHT('hint'); const INLINE_SNAPSHOT_ARG = 'snapshot'; const PROPERTY_MATCHERS_ARG = 'properties'; @@ -328,50 +323,25 @@ const _toMatchSnapshot = ({ `(CI) environment in which snapshots are not written by default.\n\n` + `${RECEIVED_COLOR('Received value')} ` + `${actual}`; - } else if ( - typeof received === 'string' && - typeof expected === 'string' && - // Does expected snapshot look like a stringified string: - expected.length >= 2 && - ((expected.startsWith('"') && expected.endsWith('"')) || // single line - (expected.startsWith('\n"') && expected.endsWith('"\n'))) // multi line - ) { - // Assign to local variable because of declaration let expected: - // TypeScript thinks it could change before report function is called. - const reported = - `Snapshot name: ${printName(currentTestName, hint, count)}\n\n` + - printDiffOrStringify( - // 1. Remove leading and trailing newline if multiple line string. - // 2. Remove enclosing double quote marks. - // 3. Remove backslash escape preceding backslash here, - // because unescape replaced it only preceding double quote mark. - expected - .trim() - .slice(1, -1) - .replace(/\\\\/g, '\\'), - received, - SNAPSHOT_LABEL, - RECEIVED_LABEL, - snapshotState.expand, - ); - - report = () => reported; } else { expected = (expected || '').trim(); actual = (actual || '').trim(); - const diffMessage = diff(expected, actual, { - aAnnotation: SNAPSHOT_LABEL, - bAnnotation: RECEIVED_LABEL, - expand: snapshotState.expand, - }); + + // Assign to local variable because of declaration let expected: + // TypeScript thinks it could change before report function is called. + const printed = printDiffOrStringified( + expected, + actual, + received, + 'Snapshot', + 'Received', + snapshotState.expand, + ); report = () => - `Snapshot name: ${printName(currentTestName, hint, count)}\n\n` + - (diffMessage || - EXPECTED_COLOR('- ' + (expected || '')) + - '\n' + - RECEIVED_COLOR('+ ' + actual)); + `Snapshot name: ${printName(currentTestName, hint, count)}\n\n` + printed; } + // Passing the actual and expected objects so that a custom reporter // could access them, for example in order to display a custom visual diff, // or create a different error message diff --git a/packages/jest-snapshot/src/print.ts b/packages/jest-snapshot/src/print.ts new file mode 100644 index 000000000000..29a36477baf8 --- /dev/null +++ b/packages/jest-snapshot/src/print.ts @@ -0,0 +1,83 @@ +import diff from 'jest-diff'; +import getType, {isPrimitive} from 'jest-get-type'; +import { + EXPECTED_COLOR, + RECEIVED_COLOR, + getLabelPrinter, + printDiffOrStringify, +} from 'jest-matcher-utils'; + +const isLineDiffable = (received: any): boolean => { + const receivedType = getType(received); + + if (isPrimitive(received)) { + return false; + } + + if ( + receivedType === 'date' || + receivedType === 'function' || + receivedType === 'regexp' + ) { + return false; + } + + if (received instanceof Error) { + return false; + } + + if ( + receivedType === 'object' && + typeof (received as any).asymmetricMatch === 'function' + ) { + return false; + } + + return true; +}; + +export const printDiffOrStringified = ( + expectedSerializedTrimmed: string, + receivedSerializedTrimmed: string, + received: unknown, + expectedLabel: string, + receivedLabel: string, + expand: boolean, // CLI options: true if `--expand` or false if `--no-expand` +): string => { + if ( + typeof received === 'string' && + // Does expected snapshot look like a stringified string: + expectedSerializedTrimmed.length >= 2 && + expectedSerializedTrimmed.startsWith('"') && + expectedSerializedTrimmed.endsWith('"') + ) { + // 0. Assume leading and trailing newline has been trimmed. + // 1. Remove enclosing double quote marks. + // 2. Remove backslash escape preceding backslash here, + // because unescape replaced it only preceding double quote mark. + return printDiffOrStringify( + expectedSerializedTrimmed.slice(1, -1).replace(/\\\\/g, '\\'), + received, + expectedLabel, + receivedLabel, + expand, + ); + } + + if (isLineDiffable(received)) { + return diff(expectedSerializedTrimmed, receivedSerializedTrimmed, { + aAnnotation: expectedLabel, + bAnnotation: receivedLabel, + expand, + }) as string; + } + + const printLabel = getLabelPrinter(expectedLabel, receivedLabel); + return ( + printLabel(expectedLabel) + + EXPECTED_COLOR(expectedSerializedTrimmed) + + '\n' + + printLabel(receivedLabel) + + RECEIVED_COLOR(receivedSerializedTrimmed) + ); +}; diff --git a/packages/jest-snapshot/tsconfig.json b/packages/jest-snapshot/tsconfig.json index dba6475fc19c..f76821875718 100644 --- a/packages/jest-snapshot/tsconfig.json +++ b/packages/jest-snapshot/tsconfig.json @@ -7,6 +7,7 @@ "references": [ {"path": "../expect"}, {"path": "../jest-diff"}, + {"path": "../jest-get-type"}, {"path": "../jest-haste-map"}, {"path": "../jest-matcher-utils"}, {"path": "../jest-message-util"}, From 30722ade4d0cff2b928350f565db7e40b9838681 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Sat, 15 Jun 2019 15:26:49 -0400 Subject: [PATCH 08/17] Add unit tests for printDiffOrStringified function --- .../printDiffOrStringified.test.ts.snap | 180 +++++++++++ .../__tests__/printDiffOrStringified.test.ts | 302 ++++++++++++++++++ 2 files changed, 482 insertions(+) create mode 100644 packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap create mode 100644 packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts diff --git a/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap b/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap new file mode 100644 index 000000000000..2c8f3ac30d4f --- /dev/null +++ b/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap @@ -0,0 +1,180 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`empty string expected and received single line 1`] = ` +Snapshot: "" +Received: "single line string" +`; + +exports[`empty string received and expected multi line 1`] = ` +Snapshot: "multi +line +string" +Received: "" +`; + +exports[`escape backslash in multi line string 1`] = ` +- Snapshot ++ Received + +- Forward / slash and back \\ slash ++ Forward / slash ++ Back \\ slash +`; + +exports[`escape backslash in single line string 1`] = ` +Snapshot: "forward / slash and back \\\\ slash" +Received: "Forward / slash and back \\\\ slash" +`; + +exports[`escape double quote marks in string 1`] = ` +Snapshot: "What does \\"oobleck\\" mean?" +Received: "What does \\"ewbleck\\" mean?" +`; + +exports[`escape regexp 1`] = ` +Snapshot: /\\\\\\\\\\("\\)/g +Received: /\\\\\\\\\\("\\)/ +`; + +exports[`expand false 1`] = ` +- Snapshot ++ Received + +@@ -12,7 +12,9 @@ + ? "number" + : T extends boolean + ? "boolean" + : T extends undefined + ? "undefined" +- : T extends Function ? "function" : "object"; ++ : T extends Function ++ ? "function" ++ : "object"; + ↵ +`; + +exports[`expand true 1`] = ` +- Snapshot ++ Received + + type TypeName = + T extends string ? "string" : + T extends number ? "number" : + T extends boolean ? "boolean" : + T extends undefined ? "undefined" : + T extends Function ? "function" : + "object"; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + type TypeName = T extends string + ? "string" + : T extends number + ? "number" + : T extends boolean + ? "boolean" + : T extends undefined + ? "undefined" +- : T extends Function ? "function" : "object"; ++ : T extends Function ++ ? "function" ++ : "object"; + ↵ +`; + +exports[`fallback to line diff 1`] = ` +- Snapshot ++ Received + ++ ====================================options===================================== ++ parsers: ["flow", "typescript"] ++ printWidth: 80 ++ | printWidth ++ =====================================input====================================== + [...a, ...b,]; + [...a, ...b]; +- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ ++ =====================================output===================================== + [...a, ...b]; + [...a, ...b]; + ++ ================================================================================ +`; + +exports[`isLineDiffable false boolean 1`] = ` +Snapshot: true +Received: false +`; + +exports[`isLineDiffable false number 1`] = ` +Snapshot: -0 +Received: NaN +`; + +exports[`isLineDiffable true array 1`] = ` +- Snapshot ++ Received + + Array [ + Object { ++ "_id": "b14680dec683e744ada1f2fe08614086", + "code": 4011, + "weight": 2.13, + }, + Object { ++ "_id": "7fc63ff01769c4fa7d9279e97e307829", + "code": 4019, + "count": 4, + }, + ] +`; + +exports[`isLineDiffable true object 1`] = ` +- Snapshot ++ Received + + Object { + "props": Object { +- "className": "logo", +- "src": "/img/jest.png", ++ "alt": "Jest logo", ++ "class": "logo", ++ "src": "/img/jest.svg", + }, + "type": "img", + } +`; + +exports[`multi line small change in one line and other is unchanged 1`] = ` +- Snapshot ++ Received + +- There is no route defined for key 'Settings'. ++ There is no route defined for key Settings. + Must be one of: 'Home' +`; + +exports[`multi line small changes 1`] = ` +- Snapshot ++ Received + +- 69 |· ++ 68 |· +- 70 | test('assert.doesNotThrow', () => { ++ 69 | test('assert.doesNotThrow', () => { +- > 71 | assert.doesNotThrow(() => { ++ > 70 | assert.doesNotThrow(() => { + | ^ +- 72 | throw Error('err!'); ++ 71 | throw Error('err!'); +- 73 | }); ++ 72 | }); +- 74 | }); ++ 73 | }); +- at Object.doesNotThrow (__tests__/assertionError.test.js:71:10) ++ at Object.doesNotThrow (__tests__/assertionError.test.js:70:10) +`; + +exports[`single line large changes 1`] = ` +Snapshot: "Array length must be a finite positive integer" +Received: "Invalid array length" +`; diff --git a/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts new file mode 100644 index 000000000000..c6a1f66f47ab --- /dev/null +++ b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts @@ -0,0 +1,302 @@ +import ansiRegex from 'ansi-regex'; +import style from 'ansi-styles'; +import {printDiffOrStringified} from '../print'; +import {serialize, unescape} from '../utils'; + +// This is an experiment to read snapshots more easily: +// * to avoid first line misaligned because of opening double quote mark, +// return string without calling print function to serialize it, +// which also reduces extra escape sequences which is a subject of the tests! +// * to align lines, return tags which have same width +// * to see inline markup, return matching and tags +// * to see unexpected escape codes, do not return empty string as default +expect.addSnapshotSerializer({ + serialize(val: string) { + return val.replace(ansiRegex(), match => { + switch (match) { + case style.inverse.open: + return ''; + case style.inverse.close: + return ''; + + case style.dim.open: + return ''; + case style.green.open: + return ''; + case style.red.open: + return ''; + case style.yellow.open: + return ''; + + case style.dim.close: + case style.green.close: + case style.red.close: + case style.yellow.close: + return ''; + + default: + return match; + } + }); + }, + test(val: any) { + return typeof val === 'string' && !!val.match(ansiRegex()); + }, +}); + +const testDiffOrStringified = ( + expected: any, + received: any, + expand: boolean, +): string => { + // Simulate serializing the expected value as a snapshot, + // and then returning actual and expected when match function fails. + // Assume that the caller of printDiffOrStringified trims the strings. + const expectedSerializedTrimmed = unescape(serialize(expected)).trim(); + const receivedSerializedTrimmed = unescape(serialize(received)).trim(); + + return printDiffOrStringified( + expectedSerializedTrimmed, + receivedSerializedTrimmed, + received, + 'Snapshot', + 'Received', + expand, + ); +}; + +describe('empty string', () => { + test('expected and received single line', () => { + const expected = ''; + const received = 'single line string'; + + expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + }); + + test('received and expected multi line', () => { + const expected = 'multi\nline\nstring'; + const received = ''; + + expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + }); +}); + +describe('escape', () => { + test('double quote marks in string', () => { + const expected = 'What does "oobleck" mean?'; + const received = 'What does "ewbleck" mean?'; + + expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + }); + + test('backslash in multi line string', () => { + const expected = 'Forward / slash and back \\ slash'; + const received = 'Forward / slash\nBack \\ slash'; + + expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + }); + + test('backslash in single line string', () => { + const expected = 'forward / slash and back \\ slash'; + const received = 'Forward / slash and back \\ slash'; + + expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + }); + + test('regexp', () => { + const expected = /\\(")/g; + const received = /\\(")/; + + expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + }); +}); + +describe('expand', () => { + const expected = [ + 'type TypeName =', + 'T extends string ? "string" :', + 'T extends number ? "number" :', + 'T extends boolean ? "boolean" :', + 'T extends undefined ? "undefined" :', + 'T extends Function ? "function" :', + '"object";', + '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', + 'type TypeName = T extends string', + '? "string"', + ': T extends number', + '? "number"', + ': T extends boolean', + '? "boolean"', + ': T extends undefined', + '? "undefined"', + ': T extends Function ? "function" : "object";', + '', + ].join('\n'); + const received = [ + 'type TypeName =', + 'T extends string ? "string" :', + 'T extends number ? "number" :', + 'T extends boolean ? "boolean" :', + 'T extends undefined ? "undefined" :', + 'T extends Function ? "function" :', + '"object";', + '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', + 'type TypeName = T extends string', + '? "string"', + ': T extends number', + '? "number"', + ': T extends boolean', + '? "boolean"', + ': T extends undefined', + '? "undefined"', + ': T extends Function', + '? "function"', + ': "object";', + '', + ].join('\n'); + + test('false', () => { + expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + }); + + test('true', () => { + expect(testDiffOrStringified(expected, received, true)).toMatchSnapshot(); + }); +}); + +test('fallback to line diff', () => { + const expected = [ + '[...a, ...b,];', + '[...a, ...b];', + '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', + '[...a, ...b];', + '[...a, ...b];', + '', + ].join('\n'); + const received = [ + '====================================options=====================================', + 'parsers: ["flow", "typescript"]', + 'printWidth: 80', + ' | printWidth', + '=====================================input======================================', + '[...a, ...b,];', + '[...a, ...b];', + '', + '=====================================output=====================================', + '[...a, ...b];', + '[...a, ...b];', + '', + '================================================================================', + ].join('\n'); + + expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); +}); + +describe('isLineDiffable', () => { + describe('false', () => { + test('boolean', () => { + const expected = true; + const received = false; + + expect( + testDiffOrStringified(expected, received, false), + ).toMatchSnapshot(); + }); + + test('number', () => { + const expected = -0; + const received = NaN; + + expect( + testDiffOrStringified(expected, received, false), + ).toMatchSnapshot(); + }); + }); + + describe('true', () => { + test('array', () => { + const expected0 = { + code: 4011, + weight: 2.13, + }; + const expected1 = { + code: 4019, + count: 4, + }; + + const expected = [expected0, expected1]; + const received = [ + {_id: 'b14680dec683e744ada1f2fe08614086', ...expected0}, + {_id: '7fc63ff01769c4fa7d9279e97e307829', ...expected1}, + ]; + + expect( + testDiffOrStringified(expected, received, false), + ).toMatchSnapshot(); + }); + + test('object', () => { + const type = 'img'; + const expected = { + props: { + className: 'logo', + src: '/img/jest.png', + }, + type, + }; + const received = { + props: { + alt: 'Jest logo', + class: 'logo', + src: '/img/jest.svg', + }, + type, + }; + + expect( + testDiffOrStringified(expected, received, false), + ).toMatchSnapshot(); + }); + }); +}); + +test('multi line small change in one line and other is unchanged', () => { + const expected = + "There is no route defined for key 'Settings'.\nMust be one of: 'Home'"; + const received = + "There is no route defined for key Settings.\nMust be one of: 'Home'"; + + expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); +}); + +test('multi line small changes', () => { + const expected = [ + ' 69 | ', + " 70 | test('assert.doesNotThrow', () => {", + ' > 71 | assert.doesNotThrow(() => {', + ' | ^', + " 72 | throw Error('err!');", + ' 73 | });', + ' 74 | });', + ' at Object.doesNotThrow (__tests__/assertionError.test.js:71:10)', + ].join('\n'); + const received = [ + ' 68 | ', + " 69 | test('assert.doesNotThrow', () => {", + ' > 70 | assert.doesNotThrow(() => {', + ' | ^', + " 71 | throw Error('err!');", + ' 72 | });', + ' 73 | });', + ' at Object.doesNotThrow (__tests__/assertionError.test.js:70:10)', + ].join('\n'); + + expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); +}); + +test('single line large changes', () => { + const expected = 'Array length must be a finite positive integer'; + const received = 'Invalid array length'; + + expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); +}); From 671c633606c212b7ba160ffa12a1e14678d3e339 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Sat, 15 Jun 2019 15:28:20 -0400 Subject: [PATCH 09/17] Add Facebook comment to 2 added files --- .../src/__tests__/printDiffOrStringified.test.ts | 7 +++++++ packages/jest-snapshot/src/print.ts | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts index c6a1f66f47ab..051a18b765c0 100644 --- a/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts +++ b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts @@ -1,3 +1,10 @@ +/** + * 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 ansiRegex from 'ansi-regex'; import style from 'ansi-styles'; import {printDiffOrStringified} from '../print'; diff --git a/packages/jest-snapshot/src/print.ts b/packages/jest-snapshot/src/print.ts index 29a36477baf8..faeae59eacfe 100644 --- a/packages/jest-snapshot/src/print.ts +++ b/packages/jest-snapshot/src/print.ts @@ -1,3 +1,10 @@ +/** + * 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 diff from 'jest-diff'; import getType, {isPrimitive} from 'jest-get-type'; import { From 1da96c15a7d3e4133b58856a058b910ad907b433 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Sat, 15 Jun 2019 16:05:33 -0400 Subject: [PATCH 10/17] Fix two edge cases --- .../__snapshots__/printDiffOrStringified.test.ts.snap | 5 +++++ .../src/__tests__/printDiffOrStringified.test.ts | 9 +++++++++ packages/jest-snapshot/src/print.ts | 8 ++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap b/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap index 2c8f3ac30d4f..ae8659471f4e 100644 --- a/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap +++ b/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap @@ -144,6 +144,11 @@ exports[`isLineDiffable true object 1`] = ` } `; +exports[`isLineDiffable true single line expected and received 1`] = ` +Snapshot: Array [] +Received: Object {} +`; + exports[`multi line small change in one line and other is unchanged 1`] = ` - Snapshot + Received diff --git a/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts index 051a18b765c0..fdb818528699 100644 --- a/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts +++ b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts @@ -264,6 +264,15 @@ describe('isLineDiffable', () => { testDiffOrStringified(expected, received, false), ).toMatchSnapshot(); }); + + test('single line expected and received', () => { + const expected = []; + const received = {}; + + expect( + testDiffOrStringified(expected, received, false), + ).toMatchSnapshot(); + }); }); }); diff --git a/packages/jest-snapshot/src/print.ts b/packages/jest-snapshot/src/print.ts index faeae59eacfe..11a4264123b3 100644 --- a/packages/jest-snapshot/src/print.ts +++ b/packages/jest-snapshot/src/print.ts @@ -18,7 +18,7 @@ const isLineDiffable = (received: any): boolean => { const receivedType = getType(received); if (isPrimitive(received)) { - return false; + return typeof received === 'string' && received.includes('\n'); } if ( @@ -71,7 +71,11 @@ export const printDiffOrStringified = ( ); } - if (isLineDiffable(received)) { + if ( + expectedSerializedTrimmed.includes('\n') && + receivedSerializedTrimmed.includes('\n') && + isLineDiffable(received) + ) { return diff(expectedSerializedTrimmed, receivedSerializedTrimmed, { aAnnotation: expectedLabel, bAnnotation: receivedLabel, From ce92ea4c0a44e474e326e398d1e17c20bade1879 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Mon, 17 Jun 2019 14:30:16 -0400 Subject: [PATCH 11/17] Fall back to line diff if either serialization has newline --- packages/jest-snapshot/src/print.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/jest-snapshot/src/print.ts b/packages/jest-snapshot/src/print.ts index 11a4264123b3..d40464256f3e 100644 --- a/packages/jest-snapshot/src/print.ts +++ b/packages/jest-snapshot/src/print.ts @@ -72,8 +72,8 @@ export const printDiffOrStringified = ( } if ( - expectedSerializedTrimmed.includes('\n') && - receivedSerializedTrimmed.includes('\n') && + (expectedSerializedTrimmed.includes('\n') || + receivedSerializedTrimmed.includes('\n')) && isLineDiffable(received) ) { return diff(expectedSerializedTrimmed, receivedSerializedTrimmed, { From 830f6af9c592aa655113d404d4d1530cf69a5704 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Mon, 17 Jun 2019 14:52:51 -0400 Subject: [PATCH 12/17] Also display diff if strings have application-specific serialization --- packages/jest-snapshot/src/print.ts | 41 ++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/packages/jest-snapshot/src/print.ts b/packages/jest-snapshot/src/print.ts index d40464256f3e..acf6d4ac3c6f 100644 --- a/packages/jest-snapshot/src/print.ts +++ b/packages/jest-snapshot/src/print.ts @@ -13,6 +13,8 @@ import { getLabelPrinter, printDiffOrStringify, } from 'jest-matcher-utils'; +import prettyFormat from 'pretty-format'; +import {unescape} from './utils'; const isLineDiffable = (received: any): boolean => { const receivedType = getType(received); @@ -51,20 +53,33 @@ export const printDiffOrStringified = ( receivedLabel: string, expand: boolean, // CLI options: true if `--expand` or false if `--no-expand` ): string => { - if ( - typeof received === 'string' && - // Does expected snapshot look like a stringified string: - expectedSerializedTrimmed.length >= 2 && - expectedSerializedTrimmed.startsWith('"') && - expectedSerializedTrimmed.endsWith('"') - ) { - // 0. Assume leading and trailing newline has been trimmed. - // 1. Remove enclosing double quote marks. - // 2. Remove backslash escape preceding backslash here, - // because unescape replaced it only preceding double quote mark. + if (typeof received === 'string') { + if ( + expectedSerializedTrimmed.length >= 2 && + expectedSerializedTrimmed.startsWith('"') && + expectedSerializedTrimmed.endsWith('"') && + receivedSerializedTrimmed === unescape(prettyFormat(received)) + ) { + // The expected snapshot looks like a stringified string. + // The received string has default serialization. + + // Undo default serialization of expected snapshot: + // Remove enclosing double quote marks. + // Remove backslash escape preceding backslash here, + // because unescape replaced it only preceding double quote mark. + return printDiffOrStringify( + expectedSerializedTrimmed.slice(1, -1).replace(/\\\\/g, '\\'), + received, + expectedLabel, + receivedLabel, + expand, + ); + } + + // Also display diff if strings have application-specific serialization: return printDiffOrStringify( - expectedSerializedTrimmed.slice(1, -1).replace(/\\\\/g, '\\'), - received, + expectedSerializedTrimmed, + receivedSerializedTrimmed, expectedLabel, receivedLabel, expand, From 71dc81fbb1ab5bb2d58dc05cff22ba739a5eb372 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Mon, 17 Jun 2019 15:09:31 -0400 Subject: [PATCH 13/17] Add test without serialize --- .../printDiffOrStringified.test.ts.snap | 16 ++ .../__tests__/printDiffOrStringified.test.ts | 174 +++++++++++------- 2 files changed, 126 insertions(+), 64 deletions(-) diff --git a/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap b/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap index ae8659471f4e..ced5c91aeed3 100644 --- a/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap +++ b/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap @@ -183,3 +183,19 @@ exports[`single line large changes 1`] = ` Snapshot: "Array length must be a finite positive integer" Received: "Invalid array length" `; + +exports[`without serialize prettier/pull/5590 1`] = ` +- Snapshot ++ Received + +@@ -4,8 +4,8 @@ + | printWidth + =====================================input====================================== + John "ShotGun" Nelson + + =====================================output===================================== +- <i"John "ShotGun" Nelson" /> ++ <i'John "ShotGun" Nelson' /> + + ================================================================================ +`; diff --git a/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts index fdb818528699..7aeef80864bc 100644 --- a/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts +++ b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts @@ -17,41 +17,45 @@ import {serialize, unescape} from '../utils'; // * to align lines, return tags which have same width // * to see inline markup, return matching and tags // * to see unexpected escape codes, do not return empty string as default + +const convertStyles = (val: string): string => + val.replace(ansiRegex(), match => { + switch (match) { + case style.inverse.open: + return ''; + case style.inverse.close: + return ''; + + case style.dim.open: + return ''; + case style.green.open: + return ''; + case style.red.open: + return ''; + case style.yellow.open: + return ''; + + case style.dim.close: + case style.green.close: + case style.red.close: + case style.yellow.close: + return ''; + + default: + return match; + } + }); + expect.addSnapshotSerializer({ - serialize(val: string) { - return val.replace(ansiRegex(), match => { - switch (match) { - case style.inverse.open: - return ''; - case style.inverse.close: - return ''; - - case style.dim.open: - return ''; - case style.green.open: - return ''; - case style.red.open: - return ''; - case style.yellow.open: - return ''; - - case style.dim.close: - case style.green.close: - case style.red.close: - case style.yellow.close: - return ''; - - default: - return match; - } - }); + serialize(val: string): string { + return val; }, - test(val: any) { - return typeof val === 'string' && !!val.match(ansiRegex()); + test(val: any): val is string { + return typeof val === 'string'; }, }); -const testDiffOrStringified = ( +const testWithSerialize = ( expected: any, received: any, expand: boolean, @@ -62,29 +66,47 @@ const testDiffOrStringified = ( const expectedSerializedTrimmed = unescape(serialize(expected)).trim(); const receivedSerializedTrimmed = unescape(serialize(received)).trim(); - return printDiffOrStringified( - expectedSerializedTrimmed, - receivedSerializedTrimmed, - received, - 'Snapshot', - 'Received', - expand, + return convertStyles( + printDiffOrStringified( + expectedSerializedTrimmed, + receivedSerializedTrimmed, + received, + 'Snapshot', + 'Received', + expand, + ), ); }; +const testWithoutSerialize = ( + expected: string, + received: string, + expand: boolean, +): string => + convertStyles( + printDiffOrStringified( + expected, + received, + received, + 'Snapshot', + 'Received', + expand, + ), + ); + describe('empty string', () => { test('expected and received single line', () => { const expected = ''; const received = 'single line string'; - expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); test('received and expected multi line', () => { const expected = 'multi\nline\nstring'; const received = ''; - expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); }); @@ -93,32 +115,33 @@ describe('escape', () => { const expected = 'What does "oobleck" mean?'; const received = 'What does "ewbleck" mean?'; - expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); test('backslash in multi line string', () => { const expected = 'Forward / slash and back \\ slash'; const received = 'Forward / slash\nBack \\ slash'; - expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); test('backslash in single line string', () => { const expected = 'forward / slash and back \\ slash'; const received = 'Forward / slash and back \\ slash'; - expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); test('regexp', () => { const expected = /\\(")/g; const received = /\\(")/; - expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); }); describe('expand', () => { + // prettier/pull/5272 const expected = [ 'type TypeName =', 'T extends string ? "string" :', @@ -163,11 +186,11 @@ describe('expand', () => { ].join('\n'); test('false', () => { - expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); test('true', () => { - expect(testDiffOrStringified(expected, received, true)).toMatchSnapshot(); + expect(testWithSerialize(expected, received, true)).toMatchSnapshot(); }); }); @@ -196,7 +219,7 @@ test('fallback to line diff', () => { '================================================================================', ].join('\n'); - expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); describe('isLineDiffable', () => { @@ -205,18 +228,14 @@ describe('isLineDiffable', () => { const expected = true; const received = false; - expect( - testDiffOrStringified(expected, received, false), - ).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); test('number', () => { const expected = -0; const received = NaN; - expect( - testDiffOrStringified(expected, received, false), - ).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); }); @@ -237,9 +256,7 @@ describe('isLineDiffable', () => { {_id: '7fc63ff01769c4fa7d9279e97e307829', ...expected1}, ]; - expect( - testDiffOrStringified(expected, received, false), - ).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); test('object', () => { @@ -260,18 +277,14 @@ describe('isLineDiffable', () => { type, }; - expect( - testDiffOrStringified(expected, received, false), - ).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); test('single line expected and received', () => { const expected = []; const received = {}; - expect( - testDiffOrStringified(expected, received, false), - ).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); }); }); @@ -282,7 +295,7 @@ test('multi line small change in one line and other is unchanged', () => { const received = "There is no route defined for key Settings.\nMust be one of: 'Home'"; - expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); test('multi line small changes', () => { @@ -307,12 +320,45 @@ test('multi line small changes', () => { ' at Object.doesNotThrow (__tests__/assertionError.test.js:70:10)', ].join('\n'); - expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); test('single line large changes', () => { const expected = 'Array length must be a finite positive integer'; const received = 'Invalid array length'; - expect(testDiffOrStringified(expected, received, false)).toMatchSnapshot(); + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); +}); + +describe('without serialize', () => { + test('prettier/pull/5590', () => { + const expected = [ + '====================================options=====================================', + 'parsers: ["html"]', + 'printWidth: 80', + ' | printWidth', + '=====================================input======================================', + `John "ShotGun" Nelson`, + '', + '=====================================output=====================================', + 'John "ShotGun" Nelson', + '', + '================================================================================', + ].join('\n'); + const received = [ + '====================================options=====================================', + 'parsers: ["html"]', + 'printWidth: 80', + ' | printWidth', + '=====================================input======================================', + `John "ShotGun" Nelson`, + '', + '=====================================output=====================================', + `John "ShotGun" Nelson`, + '', + '================================================================================', + ].join('\n'); + + expect(testWithoutSerialize(expected, received, false)).toMatchSnapshot(); + }); }); From 76205f9d01467512e10e528b95cc1a6c0b0e4728 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Mon, 17 Jun 2019 15:23:52 -0400 Subject: [PATCH 14/17] Edit comments for consistent use of serialization versus stringified --- packages/jest-snapshot/src/print.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-snapshot/src/print.ts b/packages/jest-snapshot/src/print.ts index acf6d4ac3c6f..9f01aa5a3af1 100644 --- a/packages/jest-snapshot/src/print.ts +++ b/packages/jest-snapshot/src/print.ts @@ -61,7 +61,7 @@ export const printDiffOrStringified = ( receivedSerializedTrimmed === unescape(prettyFormat(received)) ) { // The expected snapshot looks like a stringified string. - // The received string has default serialization. + // The received serialization is default stringified string. // Undo default serialization of expected snapshot: // Remove enclosing double quote marks. From 32aedff9ce5291cc8b8c98a2412e306cbfc7d3ca Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Mon, 17 Jun 2019 16:40:06 -0400 Subject: [PATCH 15/17] Call getStringDiff when strings have custom serialization --- packages/jest-snapshot/src/print.ts | 31 +++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/jest-snapshot/src/print.ts b/packages/jest-snapshot/src/print.ts index 9f01aa5a3af1..a8fe717477fa 100644 --- a/packages/jest-snapshot/src/print.ts +++ b/packages/jest-snapshot/src/print.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import diff from 'jest-diff'; +import diff, {getStringDiff} from 'jest-diff'; import getType, {isPrimitive} from 'jest-get-type'; import { EXPECTED_COLOR, @@ -76,14 +76,33 @@ export const printDiffOrStringified = ( ); } - // Also display diff if strings have application-specific serialization: - return printDiffOrStringify( + // Display substring highlight even when strings have custom serialization. + const result = getStringDiff( expectedSerializedTrimmed, receivedSerializedTrimmed, - expectedLabel, - receivedLabel, - expand, + { + aAnnotation: expectedLabel, + bAnnotation: receivedLabel, + expand, + }, ); + + if (result !== null) { + if (result.isMultiline) { + return result.annotatedDiff; + } + + // Because not default stringify, call EXPECTED_COLOR and RECEIVED_COLOR + // This is reason to call getStringDiff instead of printDiffOrStringify + const printLabel = getLabelPrinter(expectedLabel, receivedLabel); + return ( + printLabel(expectedLabel) + + EXPECTED_COLOR(result.a) + + '\n' + + printLabel(receivedLabel) + + RECEIVED_COLOR(result.b) + ); + } } if ( From d3f0791718579c28be1bd15b5c7f3fb2039f38f6 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Tue, 18 Jun 2019 13:51:15 -0400 Subject: [PATCH 16/17] Edit comment --- packages/jest-snapshot/src/print.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/jest-snapshot/src/print.ts b/packages/jest-snapshot/src/print.ts index a8fe717477fa..bd4aacd7cf38 100644 --- a/packages/jest-snapshot/src/print.ts +++ b/packages/jest-snapshot/src/print.ts @@ -94,6 +94,8 @@ export const printDiffOrStringified = ( // Because not default stringify, call EXPECTED_COLOR and RECEIVED_COLOR // This is reason to call getStringDiff instead of printDiffOrStringify + // Because there is no closing double quote mark at end of single lines, + // future improvement is to call replaceSpacesAtEnd if it becomes public. const printLabel = getLabelPrinter(expectedLabel, receivedLabel); return ( printLabel(expectedLabel) + From fc564ac15a1315d214309dd4b71a03309c7b4471 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Tue, 18 Jun 2019 14:59:41 -0400 Subject: [PATCH 17/17] Add tests for backtick and isLineDiffable true single-multi --- .../printDiffOrStringified.test.ts.snap | 29 ++++++++++++++++++ .../__tests__/printDiffOrStringified.test.ts | 30 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap b/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap index ced5c91aeed3..8c7ff72d1c25 100644 --- a/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap +++ b/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap @@ -1,5 +1,10 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`backtick single line expected and received 1`] = ` +Snapshot: "var foo = \`backtick\`;" +Received: "var foo = tag\`backtick\`;" +`; + exports[`empty string expected and received single line 1`] = ` Snapshot: "" Received: "single line string" @@ -144,6 +149,16 @@ exports[`isLineDiffable true object 1`] = ` } `; +exports[`isLineDiffable true single line expected and multi line received 1`] = ` +- Snapshot ++ Received + +- Array [] ++ Array [ ++ 0, ++ ] +`; + exports[`isLineDiffable true single line expected and received 1`] = ` Snapshot: Array [] Received: Object {} @@ -184,6 +199,20 @@ Snapshot: "Array length must be a finite positive integer" Received: "Invalid array length" `; +exports[`without serialize backtick single line expected and multi line received 1`] = ` +- Snapshot ++ Received + +- var foo = \`backtick\`; ++ var foo = \`back ++ tick\`; +`; + +exports[`without serialize backtick single line expected and received 1`] = ` +Snapshot: var foo = \`backtick\`; +Received: var foo = \`back\${x}tick\`; +`; + exports[`without serialize prettier/pull/5590 1`] = ` - Snapshot + Received diff --git a/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts index 7aeef80864bc..3907dc1b9564 100644 --- a/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts +++ b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts @@ -94,6 +94,15 @@ const testWithoutSerialize = ( ), ); +describe('backtick', () => { + test('single line expected and received', () => { + const expected = 'var foo = `backtick`;'; + const received = 'var foo = tag`backtick`;'; + + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + }); +}); + describe('empty string', () => { test('expected and received single line', () => { const expected = ''; @@ -286,6 +295,13 @@ describe('isLineDiffable', () => { expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); }); + + test('single line expected and multi line received', () => { + const expected = []; + const received = [0]; + + expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + }); }); }); @@ -331,6 +347,20 @@ test('single line large changes', () => { }); describe('without serialize', () => { + test('backtick single line expected and received', () => { + const expected = 'var foo = `backtick`;'; + const received = 'var foo = `back${x}tick`;'; + + expect(testWithoutSerialize(expected, received, false)).toMatchSnapshot(); + }); + + test('backtick single line expected and multi line received', () => { + const expected = 'var foo = `backtick`;'; + const received = 'var foo = `back\ntick`;'; + + expect(testWithoutSerialize(expected, received, false)).toMatchSnapshot(); + }); + test('prettier/pull/5590', () => { const expected = [ '====================================options=====================================',