From 5d971b06659b5f8eb7af3217df5c135f3fda3ba9 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Sat, 26 Oct 2019 14:43:14 -0400 Subject: [PATCH 1/4] jest-snapshot: Improve report when the matcher has properties --- e2e/__tests__/toMatchSnapshot.test.ts | 4 +- packages/jest-snapshot/src/State.ts | 3 +- .../__snapshots__/printSnapshot.test.ts.snap | 114 ++++++++++++------ .../src/__tests__/printSnapshot.test.ts | 78 ++++++++---- .../jest-snapshot/src/__tests__/utils.test.ts | 2 +- packages/jest-snapshot/src/index.ts | 32 +++-- packages/jest-snapshot/src/printSnapshot.ts | 45 ++++++- packages/jest-snapshot/src/utils.ts | 20 ++- 8 files changed, 213 insertions(+), 85 deletions(-) diff --git a/e2e/__tests__/toMatchSnapshot.test.ts b/e2e/__tests__/toMatchSnapshot.test.ts index 59336c948bce..2c5c1dce8bdc 100644 --- a/e2e/__tests__/toMatchSnapshot.test.ts +++ b/e2e/__tests__/toMatchSnapshot.test.ts @@ -255,7 +255,7 @@ test('handles property matchers with hint', () => { expect(stderr).toMatch( 'Snapshot name: `handles property matchers with hint: descriptive hint 1`', ); - expect(stderr).toMatch('Expected properties:'); + expect(stderr).toMatch('Expected properties'); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); expect(exitCode).toBe(1); } @@ -287,7 +287,7 @@ test('handles property matchers with deep properties', () => { expect(stderr).toMatch( 'Snapshot name: `handles property matchers with deep properties 1`', ); - expect(stderr).toMatch('Expected properties:'); + expect(stderr).toMatch('Expected properties'); expect(stderr).toMatch('Snapshots: 1 failed, 1 total'); expect(exitCode).toBe(1); } diff --git a/packages/jest-snapshot/src/State.ts b/packages/jest-snapshot/src/State.ts index ea0e886d9a0f..af3e659ab54f 100644 --- a/packages/jest-snapshot/src/State.ts +++ b/packages/jest-snapshot/src/State.ts @@ -10,6 +10,7 @@ import {Config} from '@jest/types'; import {getStackTraceLines, getTopFrame} from 'jest-message-util'; import { + addExtraLineBreaks, getSnapshotData, keyToTestName, removeExtraLineBreaks, @@ -198,7 +199,7 @@ export default class SnapshotState { this._uncheckedKeys.delete(key); } - const receivedSerialized = serialize(received); + const receivedSerialized = addExtraLineBreaks(serialize(received)); const expected = isInline ? inlineSnapshot : this._snapshotData[key]; const pass = expected === receivedSerialized; const hasSnapshot = expected !== undefined; diff --git a/packages/jest-snapshot/src/__tests__/__snapshots__/printSnapshot.test.ts.snap b/packages/jest-snapshot/src/__tests__/__snapshots__/printSnapshot.test.ts.snap index 9d9621f48717..0cba2b186529 100644 --- a/packages/jest-snapshot/src/__tests__/__snapshots__/printSnapshot.test.ts.snap +++ b/packages/jest-snapshot/src/__tests__/__snapshots__/printSnapshot.test.ts.snap @@ -38,7 +38,7 @@ exports[`matcher error toMatchSnapshot Expected properties must be an object (no Matcher error: Expected properties must be an object Expected properties has type: function -Expected properties has value: [Function properties] +Expected properties has value: [Function] `; exports[`matcher error toMatchSnapshot Expected properties must be an object (null) with hint 1`] = ` @@ -67,6 +67,14 @@ Snapshot state must be initialized Snapshot state has value: undefined `; +exports[`matcher error toMatchSnapshot received value must be an object when the matcher has properties 1`] = ` +expect(received).toMatchSnapshot(properties) + +Snapshot state must be initialized + +Snapshot state has value: undefined +`; + exports[`matcher error toThrowErrorMatchingInlineSnapshot Inline snapshot must be a string 1`] = ` expect(received).toThrowErrorMatchingInlineSnapshot(snapshot) @@ -110,8 +118,15 @@ exports[`pass false toMatchInlineSnapshot with properties equals false with snap Snapshot name: \`with properties 1\` -Expected properties: {"id": "abcdef"} -Received value: {"id": "abcdefg", "text": "Increase code coverage", "type": "ADD_ITEM"} +- Expected properties - 1 ++ Received value + 3 + + Object { +- "id": "abcdef", ++ "id": "abcdefg", ++ "text": "Increase code coverage", ++ "type": "ADD_ITEM", + } `; exports[`pass false toMatchInlineSnapshot with properties equals false without snapshot 1`] = ` @@ -119,8 +134,15 @@ exports[`pass false toMatchInlineSnapshot with properties equals false without s Snapshot name: \`with properties 1\` -Expected properties: {"id": "abcdef"} -Received value: {"id": "abcdefg", "text": "Increase code coverage", "type": "ADD_ITEM"} +- Expected properties - 1 ++ Received value + 3 + + Object { +- "id": "abcdef", ++ "id": "abcdefg", ++ "text": "Increase code coverage", ++ "type": "ADD_ITEM", + } `; exports[`pass false toMatchInlineSnapshot with properties equals true 1`] = ` @@ -165,13 +187,29 @@ This is likely because this test is run in a continuous integration (CI) environ Received: "Write me if you can!" `; -exports[`pass false toMatchSnapshot with properties equals false 1`] = ` +exports[`pass false toMatchSnapshot with properties equals false isLineDiffable false 1`] = ` expect(received).toMatchSnapshot(properties) Snapshot name: \`with properties 1\` -Expected properties: {"id": "abcdef"} -Received value: {"id": "abcdefg", "text": "Increase code coverage", "type": "ADD_ITEM"} +Expected properties: {"name": "Error"} +Received value: [RangeError: Invalid array length] +`; + +exports[`pass false toMatchSnapshot with properties equals false isLineDiffable true 1`] = ` +expect(received).toMatchSnapshot(properties) + +Snapshot name: \`with properties 1\` + +- Expected properties - 1 ++ Received value + 3 + + Object { +- "id": "abcdef", ++ "id": "abcdefg", ++ "text": "Increase code coverage", ++ "type": "ADD_ITEM", + } `; exports[`pass false toMatchSnapshot with properties equals true 1`] = ` @@ -199,17 +237,17 @@ Snapshot: "inline snapshot" Received: "received" `; -exports[`printDiffOrStringified backtick single line expected and received 1`] = ` +exports[`printSnapshotAndReceived backtick single line expected and received 1`] = ` Snapshot: "var foo = \`backtick\`;" Received: "var foo = tag\`backtick\`;" `; -exports[`printDiffOrStringified empty string expected and received single line 1`] = ` +exports[`printSnapshotAndReceived empty string expected and received single line 1`] = ` Snapshot: "" Received: "single line string" `; -exports[`printDiffOrStringified empty string received and expected multi line 1`] = ` +exports[`printSnapshotAndReceived empty string received and expected multi line 1`] = ` - Snapshot - 3 + Received + 0 @@ -218,7 +256,7 @@ exports[`printDiffOrStringified empty string received and expected multi line 1` - string `; -exports[`printDiffOrStringified escape backslash in multi line string 1`] = ` +exports[`printSnapshotAndReceived escape backslash in multi line string 1`] = ` - Snapshot - 1 + Received + 2 @@ -227,22 +265,22 @@ exports[`printDiffOrStringified escape backslash in multi line string 1`] = ` + Back \\ slash `; -exports[`printDiffOrStringified escape backslash in single line string 1`] = ` +exports[`printSnapshotAndReceived escape backslash in single line string 1`] = ` Snapshot: "forward / slash and back \\\\ slash" Received: "Forward / slash and back \\\\ slash" `; -exports[`printDiffOrStringified escape double quote marks in string 1`] = ` +exports[`printSnapshotAndReceived escape double quote marks in string 1`] = ` Snapshot: "What does \\"oobleck\\" mean?" Received: "What does \\"ewbleck\\" mean?" `; -exports[`printDiffOrStringified escape regexp 1`] = ` +exports[`printSnapshotAndReceived escape regexp 1`] = ` Snapshot: /\\\\\\\\\\("\\)/g Received: /\\\\\\\\\\("\\)/ `; -exports[`printDiffOrStringified expand false 1`] = ` +exports[`printSnapshotAndReceived expand false 1`] = ` - Snapshot - 1 + Received + 3 @@ -259,7 +297,7 @@ exports[`printDiffOrStringified expand false 1`] = ` ↵ `; -exports[`printDiffOrStringified expand true 1`] = ` +exports[`printSnapshotAndReceived expand true 1`] = ` - Snapshot - 1 + Received + 3 @@ -286,7 +324,7 @@ exports[`printDiffOrStringified expand true 1`] = ` ↵ `; -exports[`printDiffOrStringified fallback to line diff 1`] = ` +exports[`printSnapshotAndReceived fallback to line diff 1`] = ` - Snapshot - 1 + Received + 8 @@ -306,7 +344,7 @@ exports[`printDiffOrStringified fallback to line diff 1`] = ` + ================================================================================ `; -exports[`printDiffOrStringified has no common after clean up chaff array 1`] = ` +exports[`printSnapshotAndReceived has no common after clean up chaff array 1`] = ` - Snapshot - 2 + Received + 2 @@ -318,44 +356,44 @@ exports[`printDiffOrStringified has no common after clean up chaff array 1`] = ` ] `; -exports[`printDiffOrStringified has no common after clean up chaff string single line 1`] = ` +exports[`printSnapshotAndReceived has no common after clean up chaff string single line 1`] = ` Snapshot: "delete" Received: "insert" `; -exports[`printDiffOrStringified isLineDiffable false asymmetric matcher 1`] = ` +exports[`printSnapshotAndReceived isLineDiffable false asymmetric matcher 1`] = ` Snapshot: null Received: Object { "asymmetricMatch": [Function], } `; -exports[`printDiffOrStringified isLineDiffable false boolean 1`] = ` +exports[`printSnapshotAndReceived isLineDiffable false boolean 1`] = ` Snapshot: true Received: false `; -exports[`printDiffOrStringified isLineDiffable false date 1`] = ` +exports[`printSnapshotAndReceived isLineDiffable false date 1`] = ` Snapshot: 2019-09-19T00:00:00.000Z Received: 2019-09-20T00:00:00.000Z `; -exports[`printDiffOrStringified isLineDiffable false error 1`] = ` +exports[`printSnapshotAndReceived isLineDiffable false error 1`] = ` Snapshot: [Error: Cannot spread fragment "NameAndAppearances" within itself.] Received: [Error: Cannot spread fragment "NameAndAppearancesAndFriends" within itself.] `; -exports[`printDiffOrStringified isLineDiffable false function 1`] = ` +exports[`printSnapshotAndReceived isLineDiffable false function 1`] = ` Snapshot: undefined Received: [Function] `; -exports[`printDiffOrStringified isLineDiffable false number 1`] = ` +exports[`printSnapshotAndReceived isLineDiffable false number 1`] = ` Snapshot: -0 Received: NaN `; -exports[`printDiffOrStringified isLineDiffable true array 1`] = ` +exports[`printSnapshotAndReceived isLineDiffable true array 1`] = ` - Snapshot - 0 + Received + 2 @@ -373,7 +411,7 @@ exports[`printDiffOrStringified isLineDiffable true array 1`] = ` ] `; -exports[`printDiffOrStringified isLineDiffable true object 1`] = ` +exports[`printSnapshotAndReceived isLineDiffable true object 1`] = ` - Snapshot - 2 + Received + 3 @@ -389,7 +427,7 @@ exports[`printDiffOrStringified isLineDiffable true object 1`] = ` } `; -exports[`printDiffOrStringified isLineDiffable true single line expected and multi line received 1`] = ` +exports[`printSnapshotAndReceived isLineDiffable true single line expected and multi line received 1`] = ` - Snapshot - 1 + Received + 3 @@ -399,7 +437,7 @@ exports[`printDiffOrStringified isLineDiffable true single line expected and mul + ] `; -exports[`printDiffOrStringified isLineDiffable true single line expected and received 1`] = ` +exports[`printSnapshotAndReceived isLineDiffable true single line expected and received 1`] = ` - Snapshot - 1 + Received + 1 @@ -407,7 +445,7 @@ exports[`printDiffOrStringified isLineDiffable true single line expected and rec + Object {} `; -exports[`printDiffOrStringified multi line small change in one line and other is unchanged 1`] = ` +exports[`printSnapshotAndReceived multi line small change in one line and other is unchanged 1`] = ` - Snapshot - 1 + Received + 1 @@ -416,7 +454,7 @@ exports[`printDiffOrStringified multi line small change in one line and other is Must be one of: 'Home' `; -exports[`printDiffOrStringified multi line small changes 1`] = ` +exports[`printSnapshotAndReceived multi line small changes 1`] = ` - Snapshot - 7 + Received + 7 @@ -437,12 +475,12 @@ exports[`printDiffOrStringified multi line small changes 1`] = ` + at Object.doesNotThrow (__tests__/assertionError.test.js:70:10) `; -exports[`printDiffOrStringified single line large changes 1`] = ` +exports[`printSnapshotAndReceived single line large changes 1`] = ` Snapshot: "Array length must be a finite positive integer" Received: "Invalid array length" `; -exports[`printDiffOrStringified without serialize backtick single line expected and multi line received 1`] = ` +exports[`printSnapshotAndReceived without serialize backtick single line expected and multi line received 1`] = ` - Snapshot - 1 + Received + 2 @@ -451,7 +489,7 @@ exports[`printDiffOrStringified without serialize backtick single line expected + tick\`; `; -exports[`printDiffOrStringified without serialize backtick single line expected and received 1`] = ` +exports[`printSnapshotAndReceived without serialize backtick single line expected and received 1`] = ` - Snapshot - 1 + Received + 1 @@ -459,7 +497,7 @@ exports[`printDiffOrStringified without serialize backtick single line expected + var foo = \`back\${x}tick\`; `; -exports[`printDiffOrStringified without serialize has no common after clean up chaff multi line 1`] = ` +exports[`printSnapshotAndReceived without serialize has no common after clean up chaff multi line 1`] = ` - Snapshot - 2 + Received + 2 @@ -469,7 +507,7 @@ exports[`printDiffOrStringified without serialize has no common after clean up c + 2 `; -exports[`printDiffOrStringified without serialize has no common after clean up chaff single line 1`] = ` +exports[`printSnapshotAndReceived without serialize has no common after clean up chaff single line 1`] = ` - Snapshot - 1 + Received + 1 @@ -477,7 +515,7 @@ exports[`printDiffOrStringified without serialize has no common after clean up c + insert `; -exports[`printDiffOrStringified without serialize prettier/pull/5590 1`] = ` +exports[`printSnapshotAndReceived without serialize prettier/pull/5590 1`] = ` - Snapshot - 1 + Received + 1 diff --git a/packages/jest-snapshot/src/__tests__/printSnapshot.test.ts b/packages/jest-snapshot/src/__tests__/printSnapshot.test.ts index 7f4898a756bd..04b3ff89c8d1 100644 --- a/packages/jest-snapshot/src/__tests__/printSnapshot.test.ts +++ b/packages/jest-snapshot/src/__tests__/printSnapshot.test.ts @@ -11,8 +11,8 @@ import chalk from 'chalk'; import format = require('pretty-format'); import jestSnapshot = require('../index'); -import {printDiffOrStringified} from '../printSnapshot'; -import {stringify} from '../utils'; +import {printSnapshotAndReceived} from '../printSnapshot'; +import {serialize} from '../utils'; const convertAnsi = (val: string): string => val.replace(ansiRegex(), match => { @@ -168,6 +168,19 @@ describe('matcher error', () => { }).toThrowErrorMatchingSnapshot(); }); + test('received value must be an object when the matcher has properties', () => { + const context = { + isNot: false, + promise: '', + }; + const received = 'string'; + const properties = {}; + + expect(() => { + toMatchSnapshot.call(context, received, properties); + }).toThrowErrorMatchingSnapshot(); + }); + // Future test: Snapshot hint must be a string test('Snapshot state must be initialized', () => { @@ -433,7 +446,7 @@ describe('pass false', () => { const properties = {id}; const type = 'ADD_ITEM'; - test('equals false', () => { + describe('equals false', () => { const context = { currentTestName: 'with properties', equals: () => false, @@ -447,19 +460,32 @@ describe('pass false', () => { subsetEquality: () => {}, }, }; - const received = { - id: 'abcdefg', - text: 'Increase code coverage', - type, - }; - const {message, pass} = toMatchSnapshot.call( - context, - received, - properties, - ); - expect(pass).toBe(false); - expect(message()).toMatchSnapshot(); + test('isLineDiffable false', () => { + const {message, pass} = toMatchSnapshot.call( + context, + new RangeError('Invalid array length'), + {name: 'Error'}, + ); + expect(pass).toBe(false); + expect(message()).toMatchSnapshot(); + }); + + test('isLineDiffable true', () => { + const received = { + id: 'abcdefg', + text: 'Increase code coverage', + type, + }; + + const {message, pass} = toMatchSnapshot.call( + context, + received, + properties, + ); + expect(pass).toBe(false); + expect(message()).toMatchSnapshot(); + }); }); test('equals true', () => { @@ -564,16 +590,16 @@ describe('pass true', () => { }); }); -describe('printDiffOrStringified', () => { +describe('printSnapshotAndReceived', () => { // Simulate default serialization. const testWithStringify = ( expected: unknown, received: unknown, expand: boolean, ): string => - printDiffOrStringified( - stringify(expected), - stringify(received), + printSnapshotAndReceived( + serialize(expected), + serialize(received), received, expand, ); @@ -583,7 +609,7 @@ describe('printDiffOrStringified', () => { expected: string, received: string, expand: boolean, - ): string => printDiffOrStringified(expected, received, received, expand); + ): string => printSnapshotAndReceived(expected, received, received, expand); describe('backtick', () => { test('single line expected and received', () => { @@ -747,7 +773,7 @@ describe('printDiffOrStringified', () => { test('both are less', () => { const less2 = 'multi\nline'; - const difference = printDiffOrStringified(less2, less, less, true); + const difference = printSnapshotAndReceived(less2, less, less, true); expect(difference).toMatch('- multi'); expect(difference).toMatch('- line'); @@ -756,7 +782,7 @@ describe('printDiffOrStringified', () => { }); test('expected is more', () => { - const difference = printDiffOrStringified(more, less, less, true); + const difference = printSnapshotAndReceived(more, less, less, true); expect(difference).toMatch('- multi line'); expect(difference).toMatch('+ single line'); @@ -764,7 +790,7 @@ describe('printDiffOrStringified', () => { }); test('received is more', () => { - const difference = printDiffOrStringified(less, more, more, true); + const difference = printSnapshotAndReceived(less, more, more, true); expect(difference).toMatch('- single line'); expect(difference).toMatch('+ multi line'); @@ -782,7 +808,7 @@ describe('printDiffOrStringified', () => { test('both are less', () => { const lessQuoted2 = '"0 numbers"'; - const stringified = printDiffOrStringified( + const stringified = printSnapshotAndReceived( lessQuoted2, lessQuoted, less, @@ -795,7 +821,7 @@ describe('printDiffOrStringified', () => { }); test('expected is more', () => { - const stringified = printDiffOrStringified( + const stringified = printSnapshotAndReceived( moreQuoted, lessQuoted, less, @@ -809,7 +835,7 @@ describe('printDiffOrStringified', () => { }); test('received is more', () => { - const stringified = printDiffOrStringified( + const stringified = printSnapshotAndReceived( lessQuoted, moreQuoted, more, diff --git a/packages/jest-snapshot/src/__tests__/utils.test.ts b/packages/jest-snapshot/src/__tests__/utils.test.ts index f3db3a0ac438..f2c12fb2b1d9 100644 --- a/packages/jest-snapshot/src/__tests__/utils.test.ts +++ b/packages/jest-snapshot/src/__tests__/utils.test.ts @@ -191,7 +191,7 @@ test('serialize handles \\r\\n', () => { const data = '
\r\n
'; const serializedData = serialize(data); - expect(serializedData).toBe('\n"
\n
"\n'); + expect(serializedData).toBe('"
\n
"'); }); describe('ExtraLineBreaks', () => { diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index b83d1b1bd93b..33b5c9176429 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -16,8 +16,6 @@ import { RECEIVED_COLOR, matcherErrorMessage, matcherHint, - printExpected, - printReceived, printWithType, stringify, } from 'jest-matcher-utils'; @@ -34,7 +32,10 @@ import { SNAPSHOT_ARG, matcherHintFromConfig, noColor, - printDiffOrStringified, + printExpected, + printPropertiesAndReceived, + printReceived, + printSnapshotAndReceived, } from './printSnapshot'; import {Context, MatchSnapshotConfig} from './types'; import * as utils from './utils'; @@ -254,7 +255,7 @@ const toMatchInlineSnapshot = function( matcherErrorMessage( matcherHint(matcherName, undefined, PROPERTIES_ARG, options), `Inline snapshot must be a string`, - printWithType('Inline snapshot', inlineSnapshot, stringify), + printWithType('Inline snapshot', inlineSnapshot, utils.serialize), ), ); } @@ -301,6 +302,8 @@ const _toMatchSnapshot = (config: MatchSnapshotConfig) => { if (snapshotState == null) { // Because the state is the problem, this is not a matcher error. + // Call generic stringify from jest-matcher-utils package + // because uninitialized snapshot state does not need snapshot serializers. throw new Error( matcherHintFromConfig(config, false) + '\n\n' + @@ -316,6 +319,20 @@ const _toMatchSnapshot = (config: MatchSnapshotConfig) => { : currentTestName || ''; // future BREAKING change: || hint if (typeof properties === 'object') { + if (typeof received !== 'object' || received === null) { + throw new Error( + matcherErrorMessage( + matcherHintFromConfig(config, false), + `${RECEIVED_COLOR( + 'received', + )} value must be an object when the matcher has ${EXPECTED_COLOR( + 'properties', + )}`, + printWithType('Received', received, printReceived), + ), + ); + } + const propertyPass = context.equals(received, properties, [ context.utils.iterableEquality, context.utils.subsetEquality, @@ -331,8 +348,7 @@ const _toMatchSnapshot = (config: MatchSnapshotConfig) => { '\n\n' + printSnapshotName(currentTestName, hint, count) + '\n\n' + - `Expected properties: ${printExpected(properties)}\n` + - `Received value: ${printReceived(received)}`; + printPropertiesAndReceived(properties, received, snapshotState.expand); return { message, @@ -376,7 +392,7 @@ const _toMatchSnapshot = (config: MatchSnapshotConfig) => { '\n\n' + printSnapshotName(currentTestName, hint, count) + '\n\n' + - printDiffOrStringified( + printSnapshotAndReceived( expected, actual, received, @@ -437,7 +453,7 @@ const toThrowErrorMatchingInlineSnapshot = function( matcherErrorMessage( matcherHint(matcherName, undefined, SNAPSHOT_ARG, options), `Inline snapshot must be a string`, - printWithType('Inline snapshot', inlineSnapshot, stringify), + printWithType('Inline snapshot', inlineSnapshot, utils.serialize), ), ); } diff --git a/packages/jest-snapshot/src/printSnapshot.ts b/packages/jest-snapshot/src/printSnapshot.ts index bc7836ed043c..40de416e60a8 100644 --- a/packages/jest-snapshot/src/printSnapshot.ts +++ b/packages/jest-snapshot/src/printSnapshot.ts @@ -28,7 +28,7 @@ import { } from 'jest-matcher-utils'; import prettyFormat = require('pretty-format'); import {MatchSnapshotConfig} from './types'; -import {unstringifyString} from './utils'; +import {deserializeString, minify, serialize} from './utils'; export const noColor = (string: string) => string; @@ -132,9 +132,48 @@ const isLineDiffable = (received: any): boolean => { return true; }; +export const printExpected = (val: unknown) => EXPECTED_COLOR(minify(val)); +export const printReceived = (val: unknown) => RECEIVED_COLOR(minify(val)); + +export const printPropertiesAndReceived = ( + properties: object, + received: object, + expand: boolean, // CLI options: true if `--expand` or false if `--no-expand` +): string => { + const aAnnotation = 'Expected properties'; + const bAnnotation = 'Received value'; + + if (isLineDiffable(properties) && isLineDiffable(received)) { + return diffLinesUnified( + splitLines0(serialize(properties)), + splitLines0(serialize(received)), + { + aAnnotation, + aColor: EXPECTED_COLOR, + bAnnotation, + bColor: RECEIVED_COLOR, + changeLineTrailingSpaceColor: chalk.bgYellow, + commonLineTrailingSpaceColor: chalk.bgYellow, + emptyFirstOrLastLinePlaceholder: '↵', // U+21B5 + expand, + includeChangeCounts: true, + }, + ); + } + + const printLabel = getLabelPrinter(aAnnotation, bAnnotation); + return ( + printLabel(aAnnotation) + + printExpected(properties) + + '\n' + + printLabel(bAnnotation) + + printReceived(received) + ); +}; + const MAX_DIFF_STRING_LENGTH = 20000; -export const printDiffOrStringified = ( +export const printSnapshotAndReceived = ( a: string, // snapshot without extra line breaks b: string, // received serialized but without extra line breaks received: unknown, @@ -193,7 +232,7 @@ export const printDiffOrStringified = ( } // Else either string is multiline, so display as unquoted strings. - a = unstringifyString(a); // hypothetical unserialized expected string + a = deserializeString(a); // hypothetical expected string b = received; // not serialized } // Else expected had custom serialization or was not a string diff --git a/packages/jest-snapshot/src/utils.ts b/packages/jest-snapshot/src/utils.ts index eb97da5c3edc..af0d059476e1 100644 --- a/packages/jest-snapshot/src/utils.ts +++ b/packages/jest-snapshot/src/utils.ts @@ -136,20 +136,28 @@ export const removeExtraLineBreaks = (string: string): string => ? string.slice(1, -1) : string; -export const serialize = (val: unknown): string => - addExtraLineBreaks(stringify(val)); +const escapeRegex = true; +const printFunctionName = false; -export const stringify = (val: unknown): string => +export const serialize = (val: unknown): string => normalizeNewlines( prettyFormat(val, { - escapeRegex: true, + escapeRegex, plugins: getSerializers(), - printFunctionName: false, + printFunctionName, }), ); +export const minify = (val: unknown): string => + prettyFormat(val, { + escapeRegex, + min: true, + plugins: getSerializers(), + printFunctionName, + }); + // Remove double quote marks and unescape double quotes and backslashes. -export const unstringifyString = (stringified: string): string => +export const deserializeString = (stringified: string): string => stringified.slice(1, -1).replace(/\\("|\\)/g, '$1'); export const escapeBacktickString = (str: string): string => From e3eba706222bda5a881e9ddf7787d96de0c25dfd Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Sat, 26 Oct 2019 14:53:34 -0400 Subject: [PATCH 2/4] Fix context in new matcher error test --- .../src/__tests__/__snapshots__/printSnapshot.test.ts.snap | 5 +++-- packages/jest-snapshot/src/__tests__/printSnapshot.test.ts | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/jest-snapshot/src/__tests__/__snapshots__/printSnapshot.test.ts.snap b/packages/jest-snapshot/src/__tests__/__snapshots__/printSnapshot.test.ts.snap index 0cba2b186529..d327348fba8e 100644 --- a/packages/jest-snapshot/src/__tests__/__snapshots__/printSnapshot.test.ts.snap +++ b/packages/jest-snapshot/src/__tests__/__snapshots__/printSnapshot.test.ts.snap @@ -70,9 +70,10 @@ Snapshot state has value: undefined exports[`matcher error toMatchSnapshot received value must be an object when the matcher has properties 1`] = ` expect(received).toMatchSnapshot(properties) -Snapshot state must be initialized +Matcher error: received value must be an object when the matcher has properties -Snapshot state has value: undefined +Received has type: string +Received has value: "string" `; exports[`matcher error toThrowErrorMatchingInlineSnapshot Inline snapshot must be a string 1`] = ` diff --git a/packages/jest-snapshot/src/__tests__/printSnapshot.test.ts b/packages/jest-snapshot/src/__tests__/printSnapshot.test.ts index 04b3ff89c8d1..d867be1ce52d 100644 --- a/packages/jest-snapshot/src/__tests__/printSnapshot.test.ts +++ b/packages/jest-snapshot/src/__tests__/printSnapshot.test.ts @@ -170,8 +170,10 @@ describe('matcher error', () => { test('received value must be an object when the matcher has properties', () => { const context = { + currentTestName: '', isNot: false, promise: '', + snapshotState: {}, }; const received = 'string'; const properties = {}; From 134a4acbf0e3c792c10a5e9efa0d24c1fbae561e Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Sat, 26 Oct 2019 15:00:23 -0400 Subject: [PATCH 3/4] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1775c014981..ea2447dc955a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - `[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-snapshot]` [**BREAKING**] Improve report when the matcher has properties ([#9104](https://github.com/facebook/jest/pull/9104)) - `[@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)) - `[jest-reporters]` Transform file paths into hyperlinks ([#8980](https://github.com/facebook/jest/pull/8980)) From 8a0dc85fff4fc021791274ca946dd21efb0d9162 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Sat, 26 Oct 2019 15:19:59 -0400 Subject: [PATCH 4/4] Add test for received null when the matcher has properties --- .../__snapshots__/printSnapshot.test.ts.snap | 10 +++++++++- .../src/__tests__/printSnapshot.test.ts | 17 ++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/jest-snapshot/src/__tests__/__snapshots__/printSnapshot.test.ts.snap b/packages/jest-snapshot/src/__tests__/__snapshots__/printSnapshot.test.ts.snap index d327348fba8e..b32622fee2b7 100644 --- a/packages/jest-snapshot/src/__tests__/__snapshots__/printSnapshot.test.ts.snap +++ b/packages/jest-snapshot/src/__tests__/__snapshots__/printSnapshot.test.ts.snap @@ -67,7 +67,7 @@ Snapshot state must be initialized Snapshot state has value: undefined `; -exports[`matcher error toMatchSnapshot received value must be an object when the matcher has properties 1`] = ` +exports[`matcher error toMatchSnapshot received value must be an object (non-null) 1`] = ` expect(received).toMatchSnapshot(properties) Matcher error: received value must be an object when the matcher has properties @@ -76,6 +76,14 @@ Received has type: string Received has value: "string" `; +exports[`matcher error toMatchSnapshot received value must be an object (null) 1`] = ` +expect(received).toMatchSnapshot(properties) + +Matcher error: received value must be an object when the matcher has properties + +Received has value: null +`; + exports[`matcher error toThrowErrorMatchingInlineSnapshot Inline snapshot must be a string 1`] = ` expect(received).toThrowErrorMatchingInlineSnapshot(snapshot) diff --git a/packages/jest-snapshot/src/__tests__/printSnapshot.test.ts b/packages/jest-snapshot/src/__tests__/printSnapshot.test.ts index d867be1ce52d..6df0bab18d65 100644 --- a/packages/jest-snapshot/src/__tests__/printSnapshot.test.ts +++ b/packages/jest-snapshot/src/__tests__/printSnapshot.test.ts @@ -168,19 +168,26 @@ describe('matcher error', () => { }).toThrowErrorMatchingSnapshot(); }); - test('received value must be an object when the matcher has properties', () => { + describe('received value must be an object', () => { const context = { currentTestName: '', isNot: false, promise: '', snapshotState: {}, }; - const received = 'string'; const properties = {}; - expect(() => { - toMatchSnapshot.call(context, received, properties); - }).toThrowErrorMatchingSnapshot(); + test('(non-null)', () => { + expect(() => { + toMatchSnapshot.call(context, 'string', properties); + }).toThrowErrorMatchingSnapshot(); + }); + + test('(null)', () => { + expect(() => { + toMatchSnapshot.call(context, null, properties); + }).toThrowErrorMatchingSnapshot(); + }); }); // Future test: Snapshot hint must be a string