From c8e7667656d84189adfb7a60a514171cf7ed4234 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Wed, 25 Sep 2019 18:52:54 -0400 Subject: [PATCH 1/7] jest-snapshot: Display change counts in annotation lines --- .../__snapshots__/failures.test.ts.snap | 14 +- .../watchModeUpdateSnapshot.test.ts.snap | 6 +- e2e/__tests__/toMatchSnapshot.test.ts | 6 +- .../__tests__/printDiffOrStringify.test.ts | 42 ++++- packages/jest-snapshot/src/State.ts | 11 +- .../printDiffOrStringified.test.ts.snap | 140 +++++++++------ .../__tests__/printDiffOrStringified.test.ts | 162 ++++++++++++------ packages/jest-snapshot/src/index.ts | 7 +- packages/jest-snapshot/src/print.ts | 99 +++++------ packages/jest-snapshot/src/utils.ts | 24 +-- 10 files changed, 313 insertions(+), 198 deletions(-) diff --git a/e2e/__tests__/__snapshots__/failures.test.ts.snap b/e2e/__tests__/__snapshots__/failures.test.ts.snap index 9d29554a17bc..d96ed4be83ac 100644 --- a/e2e/__tests__/__snapshots__/failures.test.ts.snap +++ b/e2e/__tests__/__snapshots__/failures.test.ts.snap @@ -786,8 +786,11 @@ FAIL __tests__/snapshot.test.js Snapshot name: \`failing snapshot 1\` - Snapshot: "bar" - Received: "foo" + - Snapshot 1 - + + Received 1 + + + - bar + + foo 9 | 10 | test('failing snapshot', () => { @@ -812,8 +815,11 @@ FAIL __tests__/snapshotWithHint.test.js Snapshot name: \`failing snapshot with hint: descriptive hint 1\` - Snapshot: "bar" - Received: "foo" + - Snapshot 1 - + + Received 1 + + + - bar + + 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 2ad72b2cb373..7304af94309b 100644 --- a/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap +++ b/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap @@ -6,8 +6,10 @@ FAIL __tests__/bar.spec.js ● bar expect(received).toMatchSnapshot() Snapshot name: \`bar 1\` - Snapshot: "foo" - Received: "bar" + - Snapshot 1 - + + Received 1 + + - foo + + bar 1 | > 2 | test('bar', () => { expect('bar').toMatchSnapshot(); }); | ^ diff --git a/e2e/__tests__/toMatchSnapshot.test.ts b/e2e/__tests__/toMatchSnapshot.test.ts index e2c3f7fb1f2e..ae3957e0934b 100644 --- a/e2e/__tests__/toMatchSnapshot.test.ts +++ b/e2e/__tests__/toMatchSnapshot.test.ts @@ -108,7 +108,11 @@ test('first snapshot fails, second passes', () => { writeFiles(TESTS_DIR, {[filename]: template([`'kiwi'`, `'banana'`])}); const {stderr, exitCode} = runJest(DIR, ['-w=1', '--ci=false', filename]); expect(stderr).toMatch('Snapshot name: `snapshots 1`'); - expect(stderr).toMatch('Snapshot: "apple"\n Received: "kiwi"'); + // Match lines separately because empty line has been replaced with space: + expect(stderr).toMatch('- Snapshot 1 -'); + expect(stderr).toMatch('+ Received 1 +'); + expect(stderr).toMatch('- apple'); + expect(stderr).toMatch('+ kiwi'); expect(stderr).not.toMatch('1 obsolete snapshot found'); expect(exitCode).toBe(1); } diff --git a/packages/jest-matcher-utils/src/__tests__/printDiffOrStringify.test.ts b/packages/jest-matcher-utils/src/__tests__/printDiffOrStringify.test.ts index adaf7d7608ea..df1bf6051714 100644 --- a/packages/jest-matcher-utils/src/__tests__/printDiffOrStringify.test.ts +++ b/packages/jest-matcher-utils/src/__tests__/printDiffOrStringify.test.ts @@ -6,7 +6,7 @@ */ import {alignedAnsiStyleSerializer} from '@jest/test-utils'; -import {EXPECTED_COLOR, INVERTED_COLOR, printDiffOrStringify} from '../index'; +import {INVERTED_COLOR, printDiffOrStringify} from '../index'; expect.addSnapshotSerializer(alignedAnsiStyleSerializer); @@ -50,16 +50,40 @@ describe('printDiffOrStringify', () => { expect(testDiffOrStringify(expected, received)).toMatchSnapshot(); }); - test('received is multiline longer than max', () => { - const expected = 'multi\nline'; - const received = 'multi' + '\n123456789'.repeat(2000); // 5 + 20K chars + describe('MAX_DIFF_STRING_LENGTH', () => { + const lessChange = INVERTED_COLOR('single '); + const less = 'single line'; + const more = 'multi line' + '\n123456789'.repeat(2000); // 10 + 20K chars + + test('both are less', () => { + const difference = testDiffOrStringify('multi\nline', less); + + expect(difference).toMatch('- multi'); + expect(difference).toMatch('- line'); + + // diffStringsUnified has substring change + expect(difference).not.toMatch('+ single line'); + expect(difference).toMatch(lessChange); + }); + + test('expected is more', () => { + const difference = testDiffOrStringify(more, less); + + expect(difference).toMatch('- multi line'); + expect(difference).toMatch('+ single line'); + + // diffLinesUnified does not have substring change + expect(difference).not.toMatch(lessChange); + }); - const test = testDiffOrStringify(expected, received); + test('received is more', () => { + const difference = testDiffOrStringify(less, more); - // It is a generic line diff: - expect(test).toContain(EXPECTED_COLOR('- line')); + expect(difference).toMatch('- single line'); + expect(difference).toMatch('+ multi line'); - // It is not a specific substring diff - expect(test).not.toContain(EXPECTED_COLOR('- ' + INVERTED_COLOR('line'))); + // diffLinesUnified does not have substring change + expect(difference).not.toMatch(lessChange); + }); }); }); diff --git a/packages/jest-snapshot/src/State.ts b/packages/jest-snapshot/src/State.ts index db7868b03cb7..ea0e886d9a0f 100644 --- a/packages/jest-snapshot/src/State.ts +++ b/packages/jest-snapshot/src/State.ts @@ -12,10 +12,10 @@ import {getStackTraceLines, getTopFrame} from 'jest-message-util'; import { getSnapshotData, keyToTestName, + removeExtraLineBreaks, saveSnapshotFile, serialize, testNameToKey, - unescape, } from './utils'; import {InlineSnapshot, saveInlineSnapshots} from './inline_snapshots'; import {SnapshotData} from './types'; @@ -29,7 +29,7 @@ export type SnapshotStateOptions = { export type SnapshotMatchOptions = { testName: string; - received: any; + received: unknown; key?: string; inlineSnapshot?: string; isInline: boolean; @@ -253,9 +253,12 @@ export default class SnapshotState { if (!pass) { this.unmatched++; return { - actual: unescape(receivedSerialized), + actual: removeExtraLineBreaks(receivedSerialized), count, - expected: expected !== undefined ? unescape(expected) : undefined, + expected: + expected !== undefined + ? removeExtraLineBreaks(expected) + : undefined, key, pass: false, }; 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 d26c80eb8b56..44663b2af89e 100644 --- a/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap +++ b/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap @@ -1,25 +1,32 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`backtick single line expected and received 1`] = ` -Snapshot: "var foo = \`backtick\`;" -Received: "var foo = tag\`backtick\`;" +- Snapshot 1 - ++ Received 1 + + +- var foo = \`backtick\`; ++ var foo = tag\`backtick\`; `; exports[`empty string expected and received single line 1`] = ` -Snapshot: "" -Received: "single line string" +- Snapshot 0 - ++ Received 1 + + ++ single line string `; exports[`empty string received and expected multi line 1`] = ` -Snapshot: "multi -line -string" -Received: "" +- Snapshot 3 - ++ Received 0 + + +- multi +- line +- string `; exports[`escape backslash in multi line string 1`] = ` -- Snapshot -+ Received +- Snapshot 1 - ++ Received 2 + - Forward / slash and back \\ slash + Forward / slash @@ -27,13 +34,19 @@ exports[`escape backslash in multi line string 1`] = ` `; exports[`escape backslash in single line string 1`] = ` -Snapshot: "forward / slash and back \\\\ slash" -Received: "Forward / slash and back \\\\ slash" +- Snapshot 1 - ++ Received 1 + + +- forward / slash and back \\ slash ++ Forward / slash and back \\ slash `; exports[`escape double quote marks in string 1`] = ` -Snapshot: "What does \\"oobleck\\" mean?" -Received: "What does \\"ewbleck\\" mean?" +- Snapshot 1 - ++ Received 1 + + +- What does "oobleck" mean? ++ What does "ewbleck" mean? `; exports[`escape regexp 1`] = ` @@ -42,8 +55,8 @@ Received: /\\\\\\\\\\("\\)/ `; exports[`expand false 1`] = ` -- Snapshot -+ Received +- Snapshot 1 - ++ Received 3 + @@ -12,7 +12,9 @@ ? "number" @@ -59,8 +72,8 @@ exports[`expand false 1`] = ` `; exports[`expand true 1`] = ` -- Snapshot -+ Received +- Snapshot 1 - ++ Received 3 + type TypeName = T extends string ? "string" : @@ -86,8 +99,8 @@ exports[`expand true 1`] = ` `; exports[`fallback to line diff 1`] = ` -- Snapshot -+ Received +- Snapshot 1 - ++ Received 8 + + ====================================options===================================== + parsers: ["flow", "typescript"] @@ -106,8 +119,8 @@ exports[`fallback to line diff 1`] = ` `; exports[`has no common after clean up chaff array 1`] = ` -- Snapshot -+ Received +- Snapshot 2 - ++ Received 2 + Array [ - "delete", @@ -118,8 +131,18 @@ exports[`has no common after clean up chaff array 1`] = ` `; exports[`has no common after clean up chaff string single line 1`] = ` -Snapshot: "delete" -Received: "insert" +- Snapshot 1 - ++ Received 1 + + +- delete ++ insert +`; + +exports[`isLineDiffable false asymmetric matcher 1`] = ` +Snapshot: null +Received: Object { + "asymmetricMatch": [Function], +} `; exports[`isLineDiffable false boolean 1`] = ` @@ -127,14 +150,29 @@ Snapshot: true Received: false `; +exports[`isLineDiffable false date 1`] = ` +Snapshot: 2019-09-19T00:00:00.000Z +Received: 2019-09-20T00:00:00.000Z +`; + +exports[`isLineDiffable false error 1`] = ` +Snapshot: [Error: Cannot spread fragment "NameAndAppearances" within itself.] +Received: [Error: Cannot spread fragment "NameAndAppearancesAndFriends" within itself.] +`; + +exports[`isLineDiffable false function 1`] = ` +Snapshot: undefined +Received: [Function] +`; + exports[`isLineDiffable false number 1`] = ` Snapshot: -0 Received: NaN `; exports[`isLineDiffable true array 1`] = ` -- Snapshot -+ Received +- Snapshot 0 - ++ Received 2 + Array [ Object { @@ -151,8 +189,8 @@ exports[`isLineDiffable true array 1`] = ` `; exports[`isLineDiffable true object 1`] = ` -- Snapshot -+ Received +- Snapshot 2 - ++ Received 3 + Object { "props": Object { @@ -167,8 +205,8 @@ exports[`isLineDiffable true object 1`] = ` `; exports[`isLineDiffable true single line expected and multi line received 1`] = ` -- Snapshot -+ Received +- Snapshot 1 - ++ Received 3 + - Array [] + Array [ @@ -177,13 +215,16 @@ exports[`isLineDiffable true single line expected and multi line received 1`] = `; exports[`isLineDiffable true single line expected and received 1`] = ` -Snapshot: Array [] -Received: Object {} +- Snapshot 1 - ++ Received 1 + + +- Array [] ++ Object {} `; exports[`multi line small change in one line and other is unchanged 1`] = ` -- Snapshot -+ Received +- Snapshot 1 - ++ Received 1 + - There is no route defined for key 'Settings'. + There is no route defined for key Settings. @@ -191,8 +232,8 @@ exports[`multi line small change in one line and other is unchanged 1`] = ` `; exports[`multi line small changes 1`] = ` -- Snapshot -+ Received +- Snapshot 7 - ++ Received 7 + - 69 | + 68 | @@ -212,13 +253,16 @@ exports[`multi line small changes 1`] = ` `; exports[`single line large changes 1`] = ` -Snapshot: "Array length must be a finite positive integer" -Received: "Invalid array length" +- Snapshot 1 - ++ Received 1 + + +- Array length must be a finite positive integer ++ Invalid array length `; exports[`without serialize backtick single line expected and multi line received 1`] = ` -- Snapshot -+ Received +- Snapshot 1 - ++ Received 2 + - var foo = \`backtick\`; + var foo = \`back @@ -226,16 +270,16 @@ exports[`without serialize backtick single line expected and multi line received `; exports[`without serialize backtick single line expected and received 1`] = ` -- Snapshot -+ Received +- Snapshot 1 - ++ Received 1 + - var foo = \`backtick\`; + var foo = \`back\${x}tick\`; `; exports[`without serialize has no common after clean up chaff multi line 1`] = ` -- Snapshot -+ Received +- Snapshot 2 - ++ Received 2 + - delete - two @@ -244,16 +288,16 @@ exports[`without serialize has no common after clean up chaff multi line 1`] = ` `; exports[`without serialize has no common after clean up chaff single line 1`] = ` -- Snapshot -+ Received +- Snapshot 1 - ++ Received 1 + - delete + insert `; exports[`without serialize prettier/pull/5590 1`] = ` -- Snapshot -+ Received +- Snapshot 1 - ++ Received 1 + @@ -4,8 +4,8 @@ | printWidth diff --git a/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts index 862e5a52e9a7..be0ffa1e983d 100644 --- a/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts +++ b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts @@ -7,8 +7,9 @@ import ansiRegex = require('ansi-regex'); import * as style from 'ansi-styles'; +import chalk from 'chalk'; import {printDiffOrStringified} from '../print'; -import {serialize, unescape} from '../utils'; +import {stringify} from '../utils'; // This is an experiment to read snapshots more easily: // * to avoid first line misaligned because of opening double quote mark, @@ -58,51 +59,35 @@ expect.addSnapshotSerializer({ }, }); -const testWithSerialize = ( - expected: any, - received: any, +// Simulate default serialization. +const testWithStringify = ( + expected: unknown, + received: unknown, 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 convertStyles( +): string => + convertStyles( printDiffOrStringified( - expectedSerializedTrimmed, - receivedSerializedTrimmed, + stringify(expected), + stringify(received), received, - 'Snapshot', - 'Received', expand, ), ); -}; -const testWithoutSerialize = ( +// Simulate custom raw string serialization. +const testWithoutStringify = ( expected: string, received: string, expand: boolean, ): string => - convertStyles( - printDiffOrStringified( - expected, - received, - received, - 'Snapshot', - 'Received', - expand, - ), - ); + convertStyles(printDiffOrStringified(expected, received, received, expand)); 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(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); }); @@ -111,14 +96,14 @@ describe('empty string', () => { const expected = ''; const received = 'single line string'; - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); test('received and expected multi line', () => { const expected = 'multi\nline\nstring'; const received = ''; - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); }); @@ -127,28 +112,28 @@ describe('escape', () => { const expected = 'What does "oobleck" mean?'; const received = 'What does "ewbleck" mean?'; - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); test('backslash in multi line string', () => { const expected = 'Forward / slash and back \\ slash'; const received = 'Forward / slash\nBack \\ slash'; - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(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(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); test('regexp', () => { const expected = /\\(")/g; const received = /\\(")/; - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); }); @@ -198,11 +183,11 @@ describe('expand', () => { ].join('\n'); test('false', () => { - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); test('true', () => { - expect(testWithSerialize(expected, received, true)).toMatchSnapshot(); + expect(testWithStringify(expected, received, true)).toMatchSnapshot(); }); }); @@ -231,7 +216,7 @@ test('fallback to line diff', () => { '================================================================================', ].join('\n'); - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); describe('has no common after clean up chaff', () => { @@ -239,31 +224,100 @@ describe('has no common after clean up chaff', () => { const expected = ['delete', 'two']; const received = ['insert', '2']; - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); test('string single line', () => { const expected = 'delete'; const received = 'insert'; - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); + }); +}); + +describe('MAX_DIFF_STRING_LENGTH', () => { + const lessChange = chalk.inverse('single '); + const less = 'single line'; + const more = 'multi line' + '\n123456789'.repeat(2000); // 10 + 20K chars + + test('both are less', () => { + const difference = printDiffOrStringified('multi\nline', less, less, true); + + expect(difference).toMatch('- multi'); + expect(difference).toMatch('- line'); + + // diffStringsUnified has substring change + expect(difference).not.toMatch('+ single line'); + expect(difference).toMatch(lessChange); + }); + + test('expected is more', () => { + const difference = printDiffOrStringified(more, less, less, true); + + expect(difference).toMatch('- multi line'); + expect(difference).toMatch('+ single line'); + + // diffLinesUnified does not have substring change + expect(difference).not.toMatch(lessChange); + }); + + test('received is more', () => { + const difference = printDiffOrStringified(less, more, more, true); + + expect(difference).toMatch('- single line'); + expect(difference).toMatch('+ multi line'); + + // diffLinesUnified does not have substring change + expect(difference).not.toMatch(lessChange); }); }); describe('isLineDiffable', () => { describe('false', () => { + test('asymmetric matcher', () => { + const expected = null; + const received = {asymmetricMatch: () => {}}; + + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); + }); + test('boolean', () => { const expected = true; const received = false; - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); + }); + + test('date', () => { + const expected = new Date('2019-09-19'); + const received = new Date('2019-09-20'); + + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); + }); + + test('error', () => { + const expected = new Error( + 'Cannot spread fragment "NameAndAppearances" within itself.', + ); + const received = new Error( + 'Cannot spread fragment "NameAndAppearancesAndFriends" within itself.', + ); + + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); + }); + + test('function', () => { + const expected = undefined; + const received = () => {}; + + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); test('number', () => { const expected = -0; const received = NaN; - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); }); @@ -284,7 +338,7 @@ describe('isLineDiffable', () => { {_id: '7fc63ff01769c4fa7d9279e97e307829', ...expected1}, ]; - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); test('object', () => { @@ -305,21 +359,21 @@ describe('isLineDiffable', () => { type, }; - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); test('single line expected and received', () => { const expected = []; const received = {}; - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); test('single line expected and multi line received', () => { const expected = []; const received = [0]; - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); }); }); @@ -330,7 +384,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(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); test('multi line small changes', () => { @@ -355,14 +409,14 @@ test('multi line small changes', () => { ' at Object.doesNotThrow (__tests__/assertionError.test.js:70:10)', ].join('\n'); - expect(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(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(testWithSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithStringify(expected, received, false)).toMatchSnapshot(); }); describe('without serialize', () => { @@ -370,28 +424,28 @@ describe('without serialize', () => { const expected = 'var foo = `backtick`;'; const received = 'var foo = `back${x}tick`;'; - expect(testWithoutSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithoutStringify(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(); + expect(testWithoutStringify(expected, received, false)).toMatchSnapshot(); }); test('has no common after clean up chaff multi line', () => { const expected = 'delete\ntwo'; const received = 'insert\n2'; - expect(testWithoutSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithoutStringify(expected, received, false)).toMatchSnapshot(); }); test('has no common after clean up chaff single line', () => { const expected = 'delete'; const received = 'insert'; - expect(testWithoutSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithoutStringify(expected, received, false)).toMatchSnapshot(); }); test('prettier/pull/5590', () => { @@ -422,6 +476,6 @@ describe('without serialize', () => { '================================================================================', ].join('\n'); - expect(testWithoutSerialize(expected, received, false)).toMatchSnapshot(); + expect(testWithoutStringify(expected, received, false)).toMatchSnapshot(); }); }); diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index e898b94630a9..5fa6a86e77e7 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -343,7 +343,7 @@ const _toMatchSnapshot = ({ testName: fullTestName, }); const {count, pass} = result; - let {actual, expected} = result; + const {actual, expected} = result; let report: () => string; if (pass) { @@ -357,17 +357,12 @@ const _toMatchSnapshot = ({ `${RECEIVED_COLOR('Received value')} ` + `${actual}`; } else { - expected = utils.removeExtraLineBreaks(expected); - actual = utils.removeExtraLineBreaks(actual); - // 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, ); diff --git a/packages/jest-snapshot/src/print.ts b/packages/jest-snapshot/src/print.ts index c59df18106f5..5add30c8a9b2 100644 --- a/packages/jest-snapshot/src/print.ts +++ b/packages/jest-snapshot/src/print.ts @@ -5,22 +5,21 @@ * LICENSE file in the root directory of this source tree. */ -import diff, {diffStringsUnified} from 'jest-diff'; +import {diffLinesUnified, diffStringsUnified, splitLines0} from 'jest-diff'; import getType = require('jest-get-type'); import { EXPECTED_COLOR, RECEIVED_COLOR, getLabelPrinter, - printDiffOrStringify, } from 'jest-matcher-utils'; import prettyFormat = require('pretty-format'); -import {unescape} from './utils'; +import {unstringifyString} from './utils'; const isLineDiffable = (received: any): boolean => { const receivedType = getType(received); if (getType.isPrimitive(received)) { - return typeof received === 'string' && received.includes('\n'); + return typeof received === 'string'; } if ( @@ -48,74 +47,56 @@ const isLineDiffable = (received: any): boolean => { const MAX_DIFF_STRING_LENGTH = 20000; export const printDiffOrStringified = ( - expectedSerializedTrimmed: string, - receivedSerializedTrimmed: string, + a: string, // snapshot without extra line breaks + b: string, // received serialized but without extra line breaks received: unknown, - expectedLabel: string, - receivedLabel: string, expand: boolean, // CLI options: true if `--expand` or false if `--no-expand` ): string => { + const aAnnotation = 'Snapshot'; + const bAnnotation = 'Received'; + const aColor = EXPECTED_COLOR; + const bColor = RECEIVED_COLOR; + const options = { + aAnnotation, + aColor, + bAnnotation, + bColor, + expand, + includeChangeCounts: true, + }; + if (typeof received === 'string') { if ( - expectedSerializedTrimmed.length >= 2 && - expectedSerializedTrimmed.startsWith('"') && - expectedSerializedTrimmed.endsWith('"') && - receivedSerializedTrimmed === unescape(prettyFormat(received)) + a.length >= 2 && + a.startsWith('"') && + a.endsWith('"') && + b === prettyFormat(received) ) { - // The expected snapshot looks like a stringified string. - // The received serialization is default stringified string. - - // 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, - ); + // If snapshot looks like default serialization of a string + // and received is string which has default serialization, then replace: + a = unstringifyString(a); // hypothetical unserialized expected string + b = received; // not serialized } + // else expected had custom serialization or was not a string + // or received has custom serialization - // Display substring highlight even when strings have custom serialization. - if ( - expectedSerializedTrimmed.length !== 0 && - receivedSerializedTrimmed.length !== 0 && - expectedSerializedTrimmed.length <= MAX_DIFF_STRING_LENGTH && - receivedSerializedTrimmed.length <= MAX_DIFF_STRING_LENGTH && - expectedSerializedTrimmed !== receivedSerializedTrimmed - ) { - return diffStringsUnified( - expectedSerializedTrimmed, - receivedSerializedTrimmed, - { - aAnnotation: expectedLabel, - bAnnotation: receivedLabel, - expand, - }, - ); - } + return a.length <= MAX_DIFF_STRING_LENGTH && + b.length <= MAX_DIFF_STRING_LENGTH + ? diffStringsUnified(a, b, options) + : diffLinesUnified(splitLines0(a), splitLines0(b), options); } - if ( - (expectedSerializedTrimmed.includes('\n') || - receivedSerializedTrimmed.includes('\n')) && - isLineDiffable(received) - ) { - return diff(expectedSerializedTrimmed, receivedSerializedTrimmed, { - aAnnotation: expectedLabel, - bAnnotation: receivedLabel, - expand, - }) as string; + if (isLineDiffable(received)) { + // TODO replace with diffLinesUnified2 to ignore indenation + return diffLinesUnified(splitLines0(a), splitLines0(b), options); } - const printLabel = getLabelPrinter(expectedLabel, receivedLabel); + const printLabel = getLabelPrinter(aAnnotation, bAnnotation); return ( - printLabel(expectedLabel) + - EXPECTED_COLOR(expectedSerializedTrimmed) + + printLabel(aAnnotation) + + aColor(a) + '\n' + - printLabel(receivedLabel) + - RECEIVED_COLOR(receivedSerializedTrimmed) + printLabel(bAnnotation) + + bColor(b) ); }; diff --git a/packages/jest-snapshot/src/utils.ts b/packages/jest-snapshot/src/utils.ts index 3449492afe8c..eb97da5c3edc 100644 --- a/packages/jest-snapshot/src/utils.ts +++ b/packages/jest-snapshot/src/utils.ts @@ -136,19 +136,21 @@ export const removeExtraLineBreaks = (string: string): string => ? string.slice(1, -1) : string; -export const serialize = (data: string): string => - addExtraLineBreaks( - normalizeNewlines( - prettyFormat(data, { - escapeRegex: true, - plugins: getSerializers(), - printFunctionName: false, - }), - ), +export const serialize = (val: unknown): string => + addExtraLineBreaks(stringify(val)); + +export const stringify = (val: unknown): string => + normalizeNewlines( + prettyFormat(val, { + escapeRegex: true, + plugins: getSerializers(), + printFunctionName: false, + }), ); -// unescape double quotes -export const unescape = (data: string): string => data.replace(/\\(")/g, '$1'); +// Remove double quote marks and unescape double quotes and backslashes. +export const unstringifyString = (stringified: string): string => + stringified.slice(1, -1).replace(/\\("|\\)/g, '$1'); export const escapeBacktickString = (str: string): string => str.replace(/`|\\|\${/g, '\\$&'); From b39fb4deea8fcb39b0093d886ab96f5990080a3f Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Wed, 25 Sep 2019 19:16:42 -0400 Subject: [PATCH 2/7] Edit comment --- 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 5add30c8a9b2..94e5ad8281dc 100644 --- a/packages/jest-snapshot/src/print.ts +++ b/packages/jest-snapshot/src/print.ts @@ -87,7 +87,7 @@ export const printDiffOrStringified = ( } if (isLineDiffable(received)) { - // TODO replace with diffLinesUnified2 to ignore indenation + // TODO future PR will replace with diffLinesUnified2 to ignore indentation return diffLinesUnified(splitLines0(a), splitLines0(b), options); } From 3a96054f0943dbb8b10f57b4c8fdf0b89b33a101 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Wed, 25 Sep 2019 19:22:10 -0400 Subject: [PATCH 3/7] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1bf053cde4a..a654ea7f9591 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - `[jest-get-type]` Add `BigInt` support. ([#8382](https://github.com/facebook/jest/pull/8382)) - `[jest-matcher-utils]` Add `BigInt` support to `ensureNumbers` `ensureActualIsNumber`, `ensureExpectedIsNumber` ([#8382](https://github.com/facebook/jest/pull/8382)) - `[jest-runner]` Warn if a worker had to be force exited ([#8206](https://github.com/facebook/jest/pull/8206)) +- `[jest-snapshot]` Display change counts in annotation lines ([#8982](https://github.com/facebook/jest/pull/8982)) - `[@jest/test-result]` Create method to create empty `TestResult` ([#8867](https://github.com/facebook/jest/pull/8867)) - `[jest-worker]` [**BREAKING**] Return a promise from `end()`, resolving with the information whether workers exited gracefully ([#8206](https://github.com/facebook/jest/pull/8206)) From 9d13183761a5033362855bd0df41c61be6b32222 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Thu, 26 Sep 2019 16:47:40 -0400 Subject: [PATCH 4/7] Move indicators for change counts from postfix to prefix --- .../__snapshots__/failures.test.ts.snap | 8 +- .../watchModeUpdateSnapshot.test.ts.snap | 4 +- e2e/__tests__/toMatchSnapshot.test.ts | 4 +- packages/jest-diff/README.md | 32 +++---- .../__tests__/__snapshots__/diff.test.ts.snap | 84 ++++++++--------- packages/jest-diff/src/printDiffs.ts | 22 +++-- .../printDiffOrStringified.test.ts.snap | 92 +++++++++---------- 7 files changed, 124 insertions(+), 122 deletions(-) diff --git a/e2e/__tests__/__snapshots__/failures.test.ts.snap b/e2e/__tests__/__snapshots__/failures.test.ts.snap index d96ed4be83ac..b12395723fef 100644 --- a/e2e/__tests__/__snapshots__/failures.test.ts.snap +++ b/e2e/__tests__/__snapshots__/failures.test.ts.snap @@ -786,8 +786,8 @@ FAIL __tests__/snapshot.test.js Snapshot name: \`failing snapshot 1\` - - Snapshot 1 - - + Received 1 + + - Snapshot - 1 + + Received + 1 - bar + foo @@ -815,8 +815,8 @@ FAIL __tests__/snapshotWithHint.test.js Snapshot name: \`failing snapshot with hint: descriptive hint 1\` - - Snapshot 1 - - + Received 1 + + - Snapshot - 1 + + Received + 1 - bar + foo diff --git a/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap b/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap index 7304af94309b..1fa8776062d8 100644 --- a/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap +++ b/e2e/__tests__/__snapshots__/watchModeUpdateSnapshot.test.ts.snap @@ -6,8 +6,8 @@ FAIL __tests__/bar.spec.js ● bar expect(received).toMatchSnapshot() Snapshot name: \`bar 1\` - - Snapshot 1 - - + Received 1 + + - Snapshot - 1 + + Received + 1 - foo + bar 1 | diff --git a/e2e/__tests__/toMatchSnapshot.test.ts b/e2e/__tests__/toMatchSnapshot.test.ts index ae3957e0934b..736aa42767e9 100644 --- a/e2e/__tests__/toMatchSnapshot.test.ts +++ b/e2e/__tests__/toMatchSnapshot.test.ts @@ -109,8 +109,8 @@ test('first snapshot fails, second passes', () => { const {stderr, exitCode} = runJest(DIR, ['-w=1', '--ci=false', filename]); expect(stderr).toMatch('Snapshot name: `snapshots 1`'); // Match lines separately because empty line has been replaced with space: - expect(stderr).toMatch('- Snapshot 1 -'); - expect(stderr).toMatch('+ Received 1 +'); + expect(stderr).toMatch('- Snapshot - 1'); + expect(stderr).toMatch('+ Received + 1'); expect(stderr).toMatch('- apple'); expect(stderr).toMatch('+ kiwi'); expect(stderr).not.toMatch('1 obsolete snapshot found'); diff --git a/packages/jest-diff/README.md b/packages/jest-diff/README.md index 108fbd673aa6..a1c9d1c68861 100644 --- a/packages/jest-diff/README.md +++ b/packages/jest-diff/README.md @@ -173,8 +173,8 @@ const difference = diffLinesUnified(splitLines0(a), splitLines0(b), options); Given an empty string, `splitLines0(b)` returns `[]` an empty array, formatted as no `Received` lines: ```diff -- Expected 3 -+ Received 0 +- Expected - 3 ++ Received + 0 - multi - line @@ -194,8 +194,8 @@ const difference = diffLinesUnified(a.split('\n'), b.split('\n'), options); Given an empty string, `b.split('\n')` returns `['']` an array that contains an empty string, formatted as one empty `Received` line, which is **ambiguous** with an empty line: ```diff -- Expected 3 -+ Received 1 +- Expected - 3 ++ Received + 1 - multi - line @@ -220,16 +220,16 @@ You might call this function for case insensitive or Unicode equivalence compari import format from 'pretty-format'; const a = { - action: 'MOVE_TO', - x: 1, - y: 2, + text: 'Ignore indentation in serialized object', + time: '2019-09-19T12:34:56.000Z', + type: 'CREATE_ITEM', }; const b = { - action: 'MOVE_TO', payload: { - x: 1, - y: 2, + text: 'Ignore indentation in serialized object', + time: '2019-09-19T12:34:56.000Z', }, + type: 'CREATE_ITEM', }; const difference = diffLinesUnified2( @@ -242,18 +242,18 @@ const difference = diffLinesUnified2( ); ``` -The `x` and `y` properties are common, because their only difference is indentation: +The `text` and `time` properties are common, because their only difference is indentation: ```diff - Expected + Received Object { - action: 'MOVE_TO', + payload: Object { - x: 1, - y: 2, + text: 'Ignore indentation in serialized object', + time: '2019-09-19T12:34:56.000Z', + }, + type: 'CREATE_ITEM', } ``` @@ -519,8 +519,8 @@ const difference = diffDefault(a, b, options); ``` ```diff -- Expected 1 - -+ Received 2 + +- Expected - 1 ++ Received + 2 Array [ "common", diff --git a/packages/jest-diff/src/__tests__/__snapshots__/diff.test.ts.snap b/packages/jest-diff/src/__tests__/__snapshots__/diff.test.ts.snap index 981f946af1cc..7cc376692267 100644 --- a/packages/jest-diff/src/__tests__/__snapshots__/diff.test.ts.snap +++ b/packages/jest-diff/src/__tests__/__snapshots__/diff.test.ts.snap @@ -34,8 +34,8 @@ exports[`color of text (expanded) 1`] = ` `; exports[`context number of lines: -1 (5 default) 1`] = ` -- Expected 1 - -+ Received 1 + +- Expected - 1 ++ Received + 1 @@ -6,9 +6,9 @@ 4, @@ -51,8 +51,8 @@ exports[`context number of lines: -1 (5 default) 1`] = ` `; exports[`context number of lines: 0 1`] = ` -- Expected 1 - -+ Received 1 + +- Expected - 1 ++ Received + 1 @@ -11,1 +11,0 @@ - 9, @@ -61,8 +61,8 @@ exports[`context number of lines: 0 1`] = ` `; exports[`context number of lines: 1 1`] = ` -- Expected 1 - -+ Received 1 + +- Expected - 1 ++ Received + 1 @@ -10,4 +10,4 @@ 8, @@ -73,8 +73,8 @@ exports[`context number of lines: 1 1`] = ` `; exports[`context number of lines: 2 1`] = ` -- Expected 1 - -+ Received 1 + +- Expected - 1 ++ Received + 1 @@ -9,6 +9,6 @@ 7, @@ -87,8 +87,8 @@ exports[`context number of lines: 2 1`] = ` `; exports[`context number of lines: 3.1 (5 default) 1`] = ` -- Expected 1 - -+ Received 1 + +- Expected - 1 ++ Received + 1 @@ -6,9 +6,9 @@ 4, @@ -104,8 +104,8 @@ exports[`context number of lines: 3.1 (5 default) 1`] = ` `; exports[`context number of lines: undefined (5 default) 1`] = ` -- Expected 1 - -+ Received 1 + +- Expected - 1 ++ Received + 1 @@ -6,9 +6,9 @@ 4, @@ -121,36 +121,36 @@ exports[`context number of lines: undefined (5 default) 1`] = ` `; exports[`diffStringsUnified edge cases empty both a and b 1`] = ` -- Expected 0 - -+ Received 0 + +- Expected - 0 ++ Received + 0 `; exports[`diffStringsUnified edge cases empty only a 1`] = ` -- Expected 0 - -+ Received 1 + +- Expected - 0 ++ Received + 1 + one-line string `; exports[`diffStringsUnified edge cases empty only b 1`] = ` -- Expected 1 - -+ Received 0 + +- Expected - 1 ++ Received + 0 - one-line string `; exports[`diffStringsUnified edge cases equal both non-empty 1`] = ` -- Expected 0 - -+ Received 0 + +- Expected - 0 ++ Received + 0 one-line string `; exports[`diffStringsUnified edge cases multiline has no common after clean up chaff 1`] = ` -- Expected 2 - -+ Received 2 + +- Expected - 2 ++ Received + 2 - delete - two @@ -159,16 +159,16 @@ exports[`diffStringsUnified edge cases multiline has no common after clean up ch `; exports[`diffStringsUnified edge cases one-line has no common after clean up chaff 1`] = ` -- Expected 1 - -+ Received 1 + +- Expected - 1 ++ Received + 1 - delete + insert `; exports[`falls back to not call toJSON if it throws and then objects have differences 1`] = ` -- Expected 1 - -+ Received 1 + +- Expected - 1 ++ Received + 1 Object { - "line": 1, @@ -181,8 +181,8 @@ exports[`falls back to not call toJSON if serialization has no differences but t Compared values serialize to the same structure. Printing internal object structure without calling \`toJSON\` instead. -- Expected 1 - -+ Received 1 + +- Expected - 1 ++ Received + 1 Object { - "line": 1, @@ -192,24 +192,24 @@ exports[`falls back to not call toJSON if serialization has no differences but t `; exports[`oneline strings 1`] = ` -- Expected 1 - -+ Received 1 + +- Expected - 1 ++ Received + 1 - ab + aa `; exports[`oneline strings 2`] = ` -- Expected 1 - -+ Received 1 + +- Expected - 1 ++ Received + 1 - 123456789 + 234567890 `; exports[`oneline strings 3`] = ` -- Expected 1 - -+ Received 2 + +- Expected - 1 ++ Received + 2 - oneline + multi @@ -217,8 +217,8 @@ exports[`oneline strings 3`] = ` `; exports[`oneline strings 4`] = ` -- Expected 2 - -+ Received 1 + +- Expected - 2 ++ Received + 1 - multi - line @@ -301,8 +301,8 @@ exports[`options includeChangeCounts false diffStringsUnified 1`] = ` `; exports[`options includeChangeCounts true padding diffLinesUnified a has 2 digits 1`] = ` -- Before 10 - -+ After 1 + +- Before - 10 ++ After + 1 common - a @@ -319,8 +319,8 @@ exports[`options includeChangeCounts true padding diffLinesUnified a has 2 digit `; exports[`options includeChangeCounts true padding diffLinesUnified b has 2 digits 1`] = ` -- Before 1 - -+ After 10 + +- Before - 1 ++ After + 10 common - a @@ -337,8 +337,8 @@ exports[`options includeChangeCounts true padding diffLinesUnified b has 2 digit `; exports[`options includeChangeCounts true padding diffStringsUnified 1`] = ` -- Before 1 - -+ After 1 + +- Before - 1 ++ After + 1 - change from + change to diff --git a/packages/jest-diff/src/printDiffs.ts b/packages/jest-diff/src/printDiffs.ts index 1c3c975f15d7..e82d896bc8e1 100644 --- a/packages/jest-diff/src/printDiffs.ts +++ b/packages/jest-diff/src/printDiffs.ts @@ -162,16 +162,18 @@ export const printAnnotation = ( const aCount = String(changeCounts.a); const bCount = String(changeCounts.b); - const aPadding = - Math.max(bAnnotation.length - aAnnotation.length, 0) + - Math.max(bCount.length - aCount.length, 0); - const bPadding = - Math.max(aAnnotation.length - bAnnotation.length, 0) + - Math.max(aCount.length - bCount.length, 0); - - // Separate annotation from count by padding plus margin of 2 spaces. - aRest = ' '.repeat(aPadding + 2) + aCount + ' ' + aIndicator; - bRest = ' '.repeat(bPadding + 2) + bCount + ' ' + bIndicator; + // Padding right aligns the ends of the annotations. + const baAnnotationLengthDiff = bAnnotation.length - aAnnotation.length; + const aPaddingRight = ' '.repeat(Math.max(0, baAnnotationLengthDiff)); + const bPaddingRight = ' '.repeat(Math.max(0, -baAnnotationLengthDiff)); + + // Padding left aligns the ends of the counts. + const baCountLengthDiff = bCount.length - aCount.length; + const aPaddingLeft = ' '.repeat(Math.max(0, baCountLengthDiff)); + const bPaddingLeft = ' '.repeat(Math.max(0, -baCountLengthDiff)); + + aRest = aPaddingRight + ' ' + aIndicator + ' ' + aPaddingLeft + aCount; + bRest = bPaddingRight + ' ' + bIndicator + ' ' + bPaddingLeft + bCount; } return ( 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 44663b2af89e..b07b86fedad8 100644 --- a/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap +++ b/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap @@ -1,23 +1,23 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`backtick single line expected and received 1`] = ` -- Snapshot 1 - -+ Received 1 + +- Snapshot - 1 ++ Received + 1 - var foo = \`backtick\`; + var foo = tag\`backtick\`; `; exports[`empty string expected and received single line 1`] = ` -- Snapshot 0 - -+ Received 1 + +- Snapshot - 0 ++ Received + 1 + single line string `; exports[`empty string received and expected multi line 1`] = ` -- Snapshot 3 - -+ Received 0 + +- Snapshot - 3 ++ Received + 0 - multi - line @@ -25,8 +25,8 @@ exports[`empty string received and expected multi line 1`] = ` `; exports[`escape backslash in multi line string 1`] = ` -- Snapshot 1 - -+ Received 2 + +- Snapshot - 1 ++ Received + 2 - Forward / slash and back \\ slash + Forward / slash @@ -34,16 +34,16 @@ exports[`escape backslash in multi line string 1`] = ` `; exports[`escape backslash in single line string 1`] = ` -- Snapshot 1 - -+ Received 1 + +- Snapshot - 1 ++ Received + 1 - forward / slash and back \\ slash + Forward / slash and back \\ slash `; exports[`escape double quote marks in string 1`] = ` -- Snapshot 1 - -+ Received 1 + +- Snapshot - 1 ++ Received + 1 - What does "oobleck" mean? + What does "ewbleck" mean? @@ -55,8 +55,8 @@ Received: /\\\\\\\\\\("\\)/ `; exports[`expand false 1`] = ` -- Snapshot 1 - -+ Received 3 + +- Snapshot - 1 ++ Received + 3 @@ -12,7 +12,9 @@ ? "number" @@ -72,8 +72,8 @@ exports[`expand false 1`] = ` `; exports[`expand true 1`] = ` -- Snapshot 1 - -+ Received 3 + +- Snapshot - 1 ++ Received + 3 type TypeName = T extends string ? "string" : @@ -99,8 +99,8 @@ exports[`expand true 1`] = ` `; exports[`fallback to line diff 1`] = ` -- Snapshot 1 - -+ Received 8 + +- Snapshot - 1 ++ Received + 8 + ====================================options===================================== + parsers: ["flow", "typescript"] @@ -119,8 +119,8 @@ exports[`fallback to line diff 1`] = ` `; exports[`has no common after clean up chaff array 1`] = ` -- Snapshot 2 - -+ Received 2 + +- Snapshot - 2 ++ Received + 2 Array [ - "delete", @@ -131,8 +131,8 @@ exports[`has no common after clean up chaff array 1`] = ` `; exports[`has no common after clean up chaff string single line 1`] = ` -- Snapshot 1 - -+ Received 1 + +- Snapshot - 1 ++ Received + 1 - delete + insert @@ -171,8 +171,8 @@ Received: NaN `; exports[`isLineDiffable true array 1`] = ` -- Snapshot 0 - -+ Received 2 + +- Snapshot - 0 ++ Received + 2 Array [ Object { @@ -189,8 +189,8 @@ exports[`isLineDiffable true array 1`] = ` `; exports[`isLineDiffable true object 1`] = ` -- Snapshot 2 - -+ Received 3 + +- Snapshot - 2 ++ Received + 3 Object { "props": Object { @@ -205,8 +205,8 @@ exports[`isLineDiffable true object 1`] = ` `; exports[`isLineDiffable true single line expected and multi line received 1`] = ` -- Snapshot 1 - -+ Received 3 + +- Snapshot - 1 ++ Received + 3 - Array [] + Array [ @@ -215,16 +215,16 @@ exports[`isLineDiffable true single line expected and multi line received 1`] = `; exports[`isLineDiffable true single line expected and received 1`] = ` -- Snapshot 1 - -+ Received 1 + +- Snapshot - 1 ++ Received + 1 - Array [] + Object {} `; exports[`multi line small change in one line and other is unchanged 1`] = ` -- Snapshot 1 - -+ Received 1 + +- Snapshot - 1 ++ Received + 1 - There is no route defined for key 'Settings'. + There is no route defined for key Settings. @@ -232,8 +232,8 @@ exports[`multi line small change in one line and other is unchanged 1`] = ` `; exports[`multi line small changes 1`] = ` -- Snapshot 7 - -+ Received 7 + +- Snapshot - 7 ++ Received + 7 - 69 | + 68 | @@ -253,16 +253,16 @@ exports[`multi line small changes 1`] = ` `; exports[`single line large changes 1`] = ` -- Snapshot 1 - -+ Received 1 + +- Snapshot - 1 ++ Received + 1 - Array length must be a finite positive integer + Invalid array length `; exports[`without serialize backtick single line expected and multi line received 1`] = ` -- Snapshot 1 - -+ Received 2 + +- Snapshot - 1 ++ Received + 2 - var foo = \`backtick\`; + var foo = \`back @@ -270,16 +270,16 @@ exports[`without serialize backtick single line expected and multi line received `; exports[`without serialize backtick single line expected and received 1`] = ` -- Snapshot 1 - -+ Received 1 + +- Snapshot - 1 ++ Received + 1 - var foo = \`backtick\`; + var foo = \`back\${x}tick\`; `; exports[`without serialize has no common after clean up chaff multi line 1`] = ` -- Snapshot 2 - -+ Received 2 + +- Snapshot - 2 ++ Received + 2 - delete - two @@ -288,16 +288,16 @@ exports[`without serialize has no common after clean up chaff multi line 1`] = ` `; exports[`without serialize has no common after clean up chaff single line 1`] = ` -- Snapshot 1 - -+ Received 1 + +- Snapshot - 1 ++ Received + 1 - delete + insert `; exports[`without serialize prettier/pull/5590 1`] = ` -- Snapshot 1 - -+ Received 1 + +- Snapshot - 1 ++ Received + 1 @@ -4,8 +4,8 @@ | printWidth From db494cb791f8f9f0aee0e162fc853a1608ee13b0 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Fri, 27 Sep 2019 15:23:29 -0400 Subject: [PATCH 5/7] Display with labels if neither quoted string is multiline --- .../__snapshots__/failures.test.ts.snap | 14 +-- .../watchModeUpdateSnapshot.test.ts.snap | 6 +- e2e/__tests__/toMatchSnapshot.test.ts | 6 +- .../printDiffOrStringified.test.ts.snap | 41 +++----- .../__tests__/printDiffOrStringified.test.ts | 95 ++++++++++++++----- packages/jest-snapshot/src/print.ts | 69 +++++++++++++- 6 files changed, 157 insertions(+), 74 deletions(-) diff --git a/e2e/__tests__/__snapshots__/failures.test.ts.snap b/e2e/__tests__/__snapshots__/failures.test.ts.snap index b12395723fef..9d29554a17bc 100644 --- a/e2e/__tests__/__snapshots__/failures.test.ts.snap +++ b/e2e/__tests__/__snapshots__/failures.test.ts.snap @@ -786,11 +786,8 @@ FAIL __tests__/snapshot.test.js Snapshot name: \`failing snapshot 1\` - - Snapshot - 1 - + Received + 1 - - - bar - + foo + Snapshot: "bar" + Received: "foo" 9 | 10 | test('failing snapshot', () => { @@ -815,11 +812,8 @@ FAIL __tests__/snapshotWithHint.test.js Snapshot name: \`failing snapshot with hint: descriptive hint 1\` - - Snapshot - 1 - + Received + 1 - - - 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 1fa8776062d8..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 - 1 - + Received + 1 - - 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 736aa42767e9..a051fd99db72 100644 --- a/e2e/__tests__/toMatchSnapshot.test.ts +++ b/e2e/__tests__/toMatchSnapshot.test.ts @@ -109,10 +109,8 @@ test('first snapshot fails, second passes', () => { const {stderr, exitCode} = runJest(DIR, ['-w=1', '--ci=false', filename]); expect(stderr).toMatch('Snapshot name: `snapshots 1`'); // Match lines separately because empty line has been replaced with space: - expect(stderr).toMatch('- Snapshot - 1'); - expect(stderr).toMatch('+ Received + 1'); - expect(stderr).toMatch('- apple'); - expect(stderr).toMatch('+ kiwi'); + expect(stderr).toMatch('Snapshot: "apple"'); + expect(stderr).toMatch('Received: "kiwi"'); expect(stderr).not.toMatch('1 obsolete snapshot found'); expect(exitCode).toBe(1); } 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 b07b86fedad8..1a5d74cefabb 100644 --- a/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap +++ b/packages/jest-snapshot/src/__tests__/__snapshots__/printDiffOrStringified.test.ts.snap @@ -1,18 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`backtick single line expected and received 1`] = ` -- Snapshot - 1 -+ Received + 1 - -- var foo = \`backtick\`; -+ var foo = tag\`backtick\`; +Snapshot: "var foo = \`backtick\`;" +Received: "var foo = tag\`backtick\`;" `; exports[`empty string expected and received single line 1`] = ` -- Snapshot - 0 -+ Received + 1 - -+ single line string +Snapshot: "" +Received: "single line string" `; exports[`empty string received and expected multi line 1`] = ` @@ -34,19 +29,13 @@ exports[`escape backslash in multi line string 1`] = ` `; exports[`escape backslash in single line string 1`] = ` -- Snapshot - 1 -+ Received + 1 - -- forward / slash and back \\ slash -+ Forward / slash and back \\ slash +Snapshot: "forward / slash and back \\\\ slash" +Received: "Forward / slash and back \\\\ slash" `; exports[`escape double quote marks in string 1`] = ` -- Snapshot - 1 -+ Received + 1 - -- What does "oobleck" mean? -+ What does "ewbleck" mean? +Snapshot: "What does \\"oobleck\\" mean?" +Received: "What does \\"ewbleck\\" mean?" `; exports[`escape regexp 1`] = ` @@ -131,11 +120,8 @@ exports[`has no common after clean up chaff array 1`] = ` `; exports[`has no common after clean up chaff string single line 1`] = ` -- Snapshot - 1 -+ Received + 1 - -- delete -+ insert +Snapshot: "delete" +Received: "insert" `; exports[`isLineDiffable false asymmetric matcher 1`] = ` @@ -253,11 +239,8 @@ exports[`multi line small changes 1`] = ` `; exports[`single line large changes 1`] = ` -- Snapshot - 1 -+ Received + 1 - -- Array length must be a finite positive integer -+ Invalid array length +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`] = ` diff --git a/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts index be0ffa1e983d..3651bd201834 100644 --- a/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts +++ b/packages/jest-snapshot/src/__tests__/printDiffOrStringified.test.ts @@ -236,39 +236,88 @@ describe('has no common after clean up chaff', () => { }); describe('MAX_DIFF_STRING_LENGTH', () => { - const lessChange = chalk.inverse('single '); - const less = 'single line'; - const more = 'multi line' + '\n123456789'.repeat(2000); // 10 + 20K chars + describe('unquoted', () => { + // Do not call diffStringsUnified if either string is longer than max. + const lessChange = chalk.inverse('single '); + const less = 'single line'; + const more = 'multi line' + '\n123456789'.repeat(2000); // 10 + 20K chars + + test('both are less', () => { + const less2 = 'multi\nline'; + const difference = printDiffOrStringified(less2, less, less, true); + + expect(difference).toMatch('- multi'); + expect(difference).toMatch('- line'); + expect(difference).toMatch(lessChange); + expect(difference).not.toMatch('+ single line'); + }); + + test('expected is more', () => { + const difference = printDiffOrStringified(more, less, less, true); - test('both are less', () => { - const difference = printDiffOrStringified('multi\nline', less, less, true); + expect(difference).toMatch('- multi line'); + expect(difference).toMatch('+ single line'); + expect(difference).not.toMatch(lessChange); + }); - expect(difference).toMatch('- multi'); - expect(difference).toMatch('- line'); + test('received is more', () => { + const difference = printDiffOrStringified(less, more, more, true); - // diffStringsUnified has substring change - expect(difference).not.toMatch('+ single line'); - expect(difference).toMatch(lessChange); + expect(difference).toMatch('- single line'); + expect(difference).toMatch('+ multi line'); + expect(difference).not.toMatch(lessChange); + }); }); - test('expected is more', () => { - const difference = printDiffOrStringified(more, less, less, true); + describe('quoted', () => { + // Do not call diffStringsRaw if either string is longer than max. + const lessChange = chalk.inverse('no'); + const less = 'no numbers'; + const more = 'many numbers' + ' 123456789'.repeat(2000); // 12 + 20K chars + const lessQuoted = '"' + less + '"'; + const moreQuoted = '"' + more + '"'; + + test('both are less', () => { + const lessQuoted2 = '"0 numbers"'; + const stringified = printDiffOrStringified( + lessQuoted2, + lessQuoted, + less, + true, + ); - expect(difference).toMatch('- multi line'); - expect(difference).toMatch('+ single line'); + expect(stringified).toMatch('Received:'); + expect(stringified).toMatch(lessChange); + expect(stringified).not.toMatch('+ Received'); + }); - // diffLinesUnified does not have substring change - expect(difference).not.toMatch(lessChange); - }); + test('expected is more', () => { + const stringified = printDiffOrStringified( + moreQuoted, + lessQuoted, + less, + true, + ); - test('received is more', () => { - const difference = printDiffOrStringified(less, more, more, true); + expect(stringified).toMatch('Received:'); + expect(stringified).toMatch(less); + expect(stringified).not.toMatch('+ Received'); + expect(stringified).not.toMatch(lessChange); + }); - expect(difference).toMatch('- single line'); - expect(difference).toMatch('+ multi line'); + test('received is more', () => { + const stringified = printDiffOrStringified( + lessQuoted, + moreQuoted, + more, + true, + ); - // diffLinesUnified does not have substring change - expect(difference).not.toMatch(lessChange); + expect(stringified).toMatch('Snapshot:'); + expect(stringified).toMatch(less); + expect(stringified).not.toMatch('- Snapshot'); + expect(stringified).not.toMatch(lessChange); + }); }); }); diff --git a/packages/jest-snapshot/src/print.ts b/packages/jest-snapshot/src/print.ts index 94e5ad8281dc..b277c5fd51a8 100644 --- a/packages/jest-snapshot/src/print.ts +++ b/packages/jest-snapshot/src/print.ts @@ -5,16 +5,49 @@ * LICENSE file in the root directory of this source tree. */ -import {diffLinesUnified, diffStringsUnified, splitLines0} from 'jest-diff'; +import { + DIFF_DELETE, + DIFF_EQUAL, + DIFF_INSERT, + Diff, + diffLinesUnified, + diffStringsRaw, + diffStringsUnified, + splitLines0, +} from 'jest-diff'; import getType = require('jest-get-type'); import { EXPECTED_COLOR, + INVERTED_COLOR, RECEIVED_COLOR, getLabelPrinter, } from 'jest-matcher-utils'; import prettyFormat = require('pretty-format'); import {unstringifyString} from './utils'; +// Given array of diffs, return string: +// * include common substrings +// * exclude change substrings which have opposite op +// * include change substrings which have argument op +// with change color only if there is a common substring +const joinDiffs = ( + diffs: Array, + op: number, + hasCommon: boolean, +): string => + diffs.reduce( + (reduced: string, diff: Diff): string => + reduced + + (diff[0] === DIFF_EQUAL + ? diff[1] + : diff[0] !== op + ? '' + : hasCommon + ? INVERTED_COLOR(diff[1]) + : diff[1]), + '', + ); + const isLineDiffable = (received: any): boolean => { const receivedType = getType(received); @@ -73,12 +106,40 @@ export const printDiffOrStringified = ( b === prettyFormat(received) ) { // If snapshot looks like default serialization of a string - // and received is string which has default serialization, then replace: + // and received is string which has default serialization. + + if (!a.includes('\n') && !b.includes('\n')) { + // If neither string is multiline, + // display as labels and quoted strings. + let aQuoted = a; + let bQuoted = b; + + if ( + a.length - 2 <= MAX_DIFF_STRING_LENGTH && + b.length - 2 <= MAX_DIFF_STRING_LENGTH + ) { + const diffs = diffStringsRaw(a.slice(1, -1), b.slice(1, -1), true); + const hasCommon = diffs.some(diff => diff[0] === DIFF_EQUAL); + aQuoted = '"' + joinDiffs(diffs, DIFF_DELETE, hasCommon) + '"'; + bQuoted = '"' + joinDiffs(diffs, DIFF_INSERT, hasCommon) + '"'; + } + + const printLabel = getLabelPrinter(aAnnotation, bAnnotation); + return ( + printLabel(aAnnotation) + + aColor(aQuoted) + + '\n' + + printLabel(bAnnotation) + + bColor(bQuoted) + ); + } + + // Else either string is multiline, so display as unquoted strings. a = unstringifyString(a); // hypothetical unserialized expected string b = received; // not serialized } - // else expected had custom serialization or was not a string - // or received has custom serialization + // Else expected had custom serialization or was not a string + // or received has custom serialization. return a.length <= MAX_DIFF_STRING_LENGTH && b.length <= MAX_DIFF_STRING_LENGTH From 891c9539517932c9e68818f486fbe6c8d3381d4c Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Mon, 30 Sep 2019 09:40:08 -0400 Subject: [PATCH 6/7] Delete obsolete work-around variable assignment --- packages/jest-snapshot/src/index.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index 5fa6a86e77e7..3e587f1a89b3 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -357,17 +357,9 @@ const _toMatchSnapshot = ({ `${RECEIVED_COLOR('Received value')} ` + `${actual}`; } else { - // 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, - snapshotState.expand, - ); - report = () => - `Snapshot name: ${printName(currentTestName, hint, count)}\n\n` + printed; + `Snapshot name: ${printName(currentTestName, hint, count)}\n\n` + + printDiffOrStringified(expected, actual, received, snapshotState.expand); } // Passing the actual and expected objects so that a custom reporter From 96389e767fab5eb7cfc1f4af74031c0410a51e6d Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Mon, 30 Sep 2019 09:46:03 -0400 Subject: [PATCH 7/7] Rename Padding variables --- packages/jest-diff/src/printDiffs.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/jest-diff/src/printDiffs.ts b/packages/jest-diff/src/printDiffs.ts index e82d896bc8e1..f694b36fcb0b 100644 --- a/packages/jest-diff/src/printDiffs.ts +++ b/packages/jest-diff/src/printDiffs.ts @@ -164,16 +164,18 @@ export const printAnnotation = ( // Padding right aligns the ends of the annotations. const baAnnotationLengthDiff = bAnnotation.length - aAnnotation.length; - const aPaddingRight = ' '.repeat(Math.max(0, baAnnotationLengthDiff)); - const bPaddingRight = ' '.repeat(Math.max(0, -baAnnotationLengthDiff)); + const aAnnotationPadding = ' '.repeat(Math.max(0, baAnnotationLengthDiff)); + const bAnnotationPadding = ' '.repeat(Math.max(0, -baAnnotationLengthDiff)); // Padding left aligns the ends of the counts. const baCountLengthDiff = bCount.length - aCount.length; - const aPaddingLeft = ' '.repeat(Math.max(0, baCountLengthDiff)); - const bPaddingLeft = ' '.repeat(Math.max(0, -baCountLengthDiff)); + const aCountPadding = ' '.repeat(Math.max(0, baCountLengthDiff)); + const bCountPadding = ' '.repeat(Math.max(0, -baCountLengthDiff)); - aRest = aPaddingRight + ' ' + aIndicator + ' ' + aPaddingLeft + aCount; - bRest = bPaddingRight + ' ' + bIndicator + ' ' + bPaddingLeft + bCount; + aRest = + aAnnotationPadding + ' ' + aIndicator + ' ' + aCountPadding + aCount; + bRest = + bAnnotationPadding + ' ' + bIndicator + ' ' + bCountPadding + bCount; } return (