diff --git a/packages/utils/src/diff/index.ts b/packages/utils/src/diff/index.ts index 4fd6ac9b2078..4114c90bcf31 100644 --- a/packages/utils/src/diff/index.ts +++ b/packages/utils/src/diff/index.ts @@ -62,6 +62,17 @@ const FALLBACK_FORMAT_OPTIONS = { plugins: PLUGINS, } satisfies PrettyFormatOptions +export interface StringifiedMemory { + expected?: string + actual?: string +} + +interface Memorize { + (pointer: 'expected' | 'actual', stringifiedValue: string): string +} + +const DEFAULT_MEMORIZE: Memorize = (_, v) => v + // Generate a string that will highlight the difference between two values // with green and red. (similar to how github does code diffing) @@ -71,7 +82,7 @@ const FALLBACK_FORMAT_OPTIONS = { * @param options Diff options * @returns {string | null} a string diff */ -export function diff(a: any, b: any, options?: DiffOptions): string | undefined { +export function diff(a: any, b: any, options?: DiffOptions, memorize: Memorize = DEFAULT_MEMORIZE): string | undefined { if (Object.is(a, b)) { return '' } @@ -108,8 +119,8 @@ export function diff(a: any, b: any, options?: DiffOptions): string | undefined function truncate(s: string) { return s.length <= MAX_LENGTH ? s : (`${s.slice(0, MAX_LENGTH)}...`) } - aDisplay = truncate(aDisplay) - bDisplay = truncate(bDisplay) + aDisplay = memorize('expected', truncate(aDisplay)) + bDisplay = memorize('actual', truncate(bDisplay)) const aDiff = `${aColor(`${aIndicator} ${aAnnotation}:`)}\n${aDisplay}` const bDiff = `${bColor(`${bIndicator} ${bAnnotation}:`)}\n${bDisplay}` return `${aDiff}\n\n${bDiff}` @@ -124,13 +135,20 @@ export function diff(a: any, b: any, options?: DiffOptions): string | undefined return diffLinesUnified(a.split('\n'), b.split('\n'), options) case 'boolean': case 'number': - return comparePrimitive(a, b, options) + return comparePrimitive(a, b, options, memorize) case 'map': - return compareObjects(sortMap(a), sortMap(b), options) + return compareObjects(sortMap(a), sortMap(b), options, memorize) case 'set': - return compareObjects(sortSet(a), sortSet(b), options) + return compareObjects(sortSet(a), sortSet(b), options, memorize) default: - return compareObjects(a, b, options) + return compareObjects(a, b, options, memorize) + } +} + +function createMemorize(memory: StringifiedMemory) { + return (pointer: 'expected' | 'actual', stringifiedValue: string) => { + memory[pointer] = stringifiedValue + return stringifiedValue } } @@ -138,9 +156,10 @@ function comparePrimitive( a: number | boolean, b: number | boolean, options?: DiffOptions, + memorize: Memorize = DEFAULT_MEMORIZE, ) { - const aFormat = prettyFormat(a, FORMAT_OPTIONS) - const bFormat = prettyFormat(b, FORMAT_OPTIONS) + const aFormat = memorize('expected', prettyFormat(a, FORMAT_OPTIONS)) + const bFormat = memorize('actual', prettyFormat(b, FORMAT_OPTIONS)) return aFormat === bFormat ? '' : diffLinesUnified(aFormat.split('\n'), bFormat.split('\n'), options) @@ -158,13 +177,14 @@ function compareObjects( a: Record, b: Record, options?: DiffOptions, + memorize: Memorize = DEFAULT_MEMORIZE, ) { let difference let hasThrown = false try { const formatOptions = getFormatOptions(FORMAT_OPTIONS, options) - difference = getObjectsDifference(a, b, formatOptions, options) + difference = getObjectsDifference(a, b, formatOptions, options, memorize) } catch { hasThrown = true @@ -175,7 +195,7 @@ function compareObjects( // without calling `toJSON`. It's also possible that toJSON might throw. if (difference === undefined || difference === noDiffMessage) { const formatOptions = getFormatOptions(FALLBACK_FORMAT_OPTIONS, options) - difference = getObjectsDifference(a, b, formatOptions, options) + difference = getObjectsDifference(a, b, formatOptions, options, memorize) if (difference !== noDiffMessage && !hasThrown) { difference = `${getCommonMessage( @@ -188,6 +208,10 @@ function compareObjects( return difference } +export function getDefaultFormatOptions(options?: DiffOptions): PrettyFormatOptions { + return getFormatOptions(FORMAT_OPTIONS, options) +} + function getFormatOptions( formatOptions: PrettyFormatOptions, options?: DiffOptions, @@ -207,6 +231,7 @@ function getObjectsDifference( b: Record, formatOptions: PrettyFormatOptions, options?: DiffOptions, + memorize: Memorize = DEFAULT_MEMORIZE, ): string { const formatOptionsZeroIndent = { ...formatOptions, indent: 0 } const aCompare = prettyFormat(a, formatOptionsZeroIndent) @@ -216,8 +241,8 @@ function getObjectsDifference( return getCommonMessage(NO_DIFF_MESSAGE, options) } else { - const aDisplay = prettyFormat(a, formatOptions) - const bDisplay = prettyFormat(b, formatOptions) + const aDisplay = memorize('expected', prettyFormat(a, formatOptions)) + const bDisplay = memorize('actual', prettyFormat(b, formatOptions)) return diffLinesUnified2( aDisplay.split('\n'), @@ -248,6 +273,7 @@ export function printDiffOrStringify( received: unknown, expected: unknown, options?: DiffOptions, + memory?: StringifiedMemory, ): string | undefined { const { aAnnotation, bAnnotation } = normalizeDiffOptions(options) @@ -286,7 +312,8 @@ export function printDiffOrStringify( const clonedExpected = deepClone(expected, { forceWritable: true }) const clonedReceived = deepClone(received, { forceWritable: true }) const { replacedExpected, replacedActual } = replaceAsymmetricMatcher(clonedReceived, clonedExpected) - const difference = diff(replacedExpected, replacedActual, options) + const memorize = memory ? createMemorize(memory) : DEFAULT_MEMORIZE + const difference = diff(replacedExpected, replacedActual, options, memorize) return difference // } diff --git a/packages/utils/src/error.ts b/packages/utils/src/error.ts index bd0da5b619a6..023a2021f3e8 100644 --- a/packages/utils/src/error.ts +++ b/packages/utils/src/error.ts @@ -1,7 +1,7 @@ import type { DiffOptions } from './diff' import type { TestError } from './types' -import { printDiffOrStringify } from './diff' -import { stringify } from './display' +import { format as prettyFormat } from '@vitest/pretty-format' +import { getDefaultFormatOptions, printDiffOrStringify } from './diff' import { serializeValue } from './serialize' export { serializeValue as serializeError } @@ -22,17 +22,19 @@ export function processError( && err.expected !== undefined && err.actual !== undefined) ) { - err.diff = printDiffOrStringify(err.actual, err.expected, { + const options = { ...diffOptions, ...err.diffOptions as DiffOptions, - }) - } + } + err.diff = printDiffOrStringify( + err.actual, + err.expected, + options, + err, + ) - if ('expected' in err && typeof err.expected !== 'string') { - err.expected = stringify(err.expected, 10) - } - if ('actual' in err && typeof err.actual !== 'string') { - err.actual = stringify(err.actual, 10) + err.expected = prettifyValue(err.expected, options) + err.actual = prettifyValue(err.actual, options) } // some Error implementations may not allow rewriting cause @@ -56,3 +58,10 @@ export function processError( ) } } + +function prettifyValue(value: unknown, options: DiffOptions): string | undefined { + if (typeof value !== 'string') { + return prettyFormat(value, getDefaultFormatOptions(options)) + } + return value +} diff --git a/packages/utils/src/serialize.ts b/packages/utils/src/serialize.ts index b822bca5e687..69f7d107c05d 100644 --- a/packages/utils/src/serialize.ts +++ b/packages/utils/src/serialize.ts @@ -3,6 +3,11 @@ declare class Element { tagName: string } +// buffer exists only in node +declare class Buffer { + length: number +} + const IS_RECORD_SYMBOL = '@@__IMMUTABLE_RECORD__@@' const IS_COLLECTION_SYMBOL = '@@__IMMUTABLE_ITERABLE__@@' diff --git a/test/unit/test/__snapshots__/jest-expect.test.ts.snap b/test/unit/test/__snapshots__/jest-expect.test.ts.snap index 5aec9b6bc32a..8feeab4a4725 100644 --- a/test/unit/test/__snapshots__/jest-expect.test.ts.snap +++ b/test/unit/test/__snapshots__/jest-expect.test.ts.snap @@ -20,7 +20,7 @@ exports[`asymmetric matcher error 2`] = ` exports[`asymmetric matcher error 3`] = ` { - "actual": "Object { + "actual": "{ "foo": "hello", }", "diff": "- Expected @@ -30,7 +30,7 @@ exports[`asymmetric matcher error 3`] = ` - "foo": StringContaining "xx", + "foo": "hello", }", - "expected": "Object { + "expected": "{ "foo": StringContaining "xx", }", "message": "expected { foo: 'hello' } to deeply equal { foo: StringContaining "xx" }", @@ -39,7 +39,7 @@ exports[`asymmetric matcher error 3`] = ` exports[`asymmetric matcher error 4`] = ` { - "actual": "Object { + "actual": "{ "foo": "hello", }", "diff": "- Expected @@ -49,7 +49,7 @@ exports[`asymmetric matcher error 4`] = ` - "foo": StringNotContaining "ll", + "foo": "hello", }", - "expected": "Object { + "expected": "{ "foo": StringNotContaining "ll", }", "message": "expected { foo: 'hello' } to deeply equal { foo: StringNotContaining "ll" }", @@ -58,7 +58,7 @@ exports[`asymmetric matcher error 4`] = ` exports[`asymmetric matcher error 5`] = ` { - "actual": "hello", + "actual": ""hello"", "diff": "- Expected: stringContainingCustom<"xx"> @@ -71,7 +71,7 @@ stringContainingCustom<"xx"> exports[`asymmetric matcher error 6`] = ` { - "actual": "hello", + "actual": ""hello"", "diff": "- Expected: not.stringContainingCustom<"ll"> @@ -84,7 +84,7 @@ not.stringContainingCustom<"ll"> exports[`asymmetric matcher error 7`] = ` { - "actual": "Object { + "actual": "{ "foo": "hello", }", "diff": "- Expected @@ -94,7 +94,7 @@ exports[`asymmetric matcher error 7`] = ` - "foo": stringContainingCustom<"xx">, + "foo": "hello", }", - "expected": "Object { + "expected": "{ "foo": stringContainingCustom<"xx">, }", "message": "expected { foo: 'hello' } to deeply equal { foo: stringContainingCustom<"xx"> }", @@ -103,7 +103,7 @@ exports[`asymmetric matcher error 7`] = ` exports[`asymmetric matcher error 8`] = ` { - "actual": "Object { + "actual": "{ "foo": "hello", }", "diff": "- Expected @@ -113,7 +113,7 @@ exports[`asymmetric matcher error 8`] = ` - "foo": not.stringContainingCustom<"ll">, + "foo": "hello", }", - "expected": "Object { + "expected": "{ "foo": not.stringContainingCustom<"ll">, }", "message": "expected { foo: 'hello' } to deeply equal { foo: not.stringContainingCustom{…} }", @@ -122,25 +122,25 @@ exports[`asymmetric matcher error 8`] = ` exports[`asymmetric matcher error 9`] = ` { - "actual": "undefined", + "actual": undefined, "diff": undefined, - "expected": "undefined", + "expected": undefined, "message": "expected "hello" to contain "xx"", } `; exports[`asymmetric matcher error 10`] = ` { - "actual": "undefined", + "actual": undefined, "diff": undefined, - "expected": "undefined", + "expected": undefined, "message": "expected "hello" not to contain "ll"", } `; exports[`asymmetric matcher error 11`] = ` { - "actual": "hello", + "actual": ""hello"", "diff": "- Expected: testComplexMatcher @@ -341,7 +341,7 @@ exports[`asymmetric matcher error 22`] = ` exports[`asymmetric matcher error 23`] = ` { - "actual": "hello", + "actual": ""hello"", "diff": "- Expected: stringContainingCustom<"ll"> @@ -354,7 +354,9 @@ stringContainingCustom<"ll"> exports[`asymmetric matcher error 24`] = ` { - "actual": "[Error: hello]", + "actual": "Error { + "message": "hello", +}", "diff": "- Expected: StringContaining "ll" @@ -369,7 +371,9 @@ Error { exports[`asymmetric matcher error 25`] = ` { - "actual": "[Error: hello]", + "actual": "Error { + "message": "hello", +}", "diff": "- Expected: stringContainingCustom<"ll"> @@ -384,7 +388,9 @@ Error { exports[`asymmetric matcher error 26`] = ` { - "actual": "[Error: hello]", + "actual": "MyError2 { + "message": "hello", +}", "diff": "- Expected: [Function MyError1] @@ -412,7 +418,7 @@ undefined", exports[`diff 2`] = ` { - "actual": "Object { + "actual": "{ "hello": "world", }", "diff": "- Expected: @@ -429,7 +435,7 @@ false exports[`diff 3`] = ` { - "actual": "Object { + "actual": "{ "hello": "world", }", "diff": "- Expected: @@ -446,7 +452,7 @@ NaN exports[`diff 4`] = ` { - "actual": "Object { + "actual": "{ "hello": "world", }", "diff": "- Expected: @@ -463,7 +469,7 @@ undefined exports[`diff 5`] = ` { - "actual": "Object { + "actual": "{ "hello": "world", }", "diff": "- Expected: @@ -480,7 +486,10 @@ null exports[`error equality 1`] = ` { - "actual": "[Error: hi]", + "actual": "MyError { + "message": "hi", + "custom": "a", +}", "diff": "- Expected + Received @@ -489,14 +498,20 @@ exports[`error equality 1`] = ` - "custom": "b", + "custom": "a", }", - "expected": "[Error: hi]", + "expected": "MyError { + "message": "hi", + "custom": "b", +}", "message": "expected Error: hi { custom: 'a' } to deeply equal Error: hi { custom: 'b' }", } `; exports[`error equality 2`] = ` { - "actual": "[Error: hi]", + "actual": "MyError { + "message": "hi", + "custom": "a", +}", "diff": "- Expected + Received @@ -505,14 +520,20 @@ exports[`error equality 2`] = ` - "custom": "b", + "custom": "a", }", - "expected": "[Error: hi]", + "expected": "MyError { + "message": "hi", + "custom": "b", +}", "message": "expected a thrown error to be Error: hi { custom: 'b' }", } `; exports[`error equality 3`] = ` { - "actual": "[Error: hi]", + "actual": "MyError { + "message": "hi", + "custom": "a", +}", "diff": "- Expected + Received @@ -521,14 +542,20 @@ exports[`error equality 3`] = ` + "message": "hi", "custom": "a", }", - "expected": "[Error: hello]", + "expected": "MyError { + "message": "hello", + "custom": "a", +}", "message": "expected Error: hi { custom: 'a' } to deeply equal Error: hello { custom: 'a' }", } `; exports[`error equality 4`] = ` { - "actual": "[Error: hello]", + "actual": "MyError { + "message": "hello", + "custom": "a", +}", "diff": "- Expected + Received @@ -537,14 +564,20 @@ exports[`error equality 4`] = ` "message": "hello", "custom": "a", }", - "expected": "[Error: hello]", + "expected": "YourError { + "message": "hello", + "custom": "a", +}", "message": "expected Error: hello { custom: 'a' } to strictly equal Error: hello { custom: 'a' }", } `; exports[`error equality 5`] = ` { - "actual": "[Error: hello]", + "actual": "Error { + "message": "hello", + "cause": "x", +}", "diff": "- Expected + Received @@ -553,14 +586,19 @@ exports[`error equality 5`] = ` - "cause": "y", + "cause": "x", }", - "expected": "[Error: hello]", + "expected": "Error { + "message": "hello", + "cause": "y", +}", "message": "expected Error: hello to deeply equal Error: hello", } `; exports[`error equality 6`] = ` { - "actual": "[Error: hello]", + "actual": "Error { + "message": "hello", +}", "diff": "- Expected + Received @@ -568,14 +606,19 @@ exports[`error equality 6`] = ` "message": "hello", - "cause": "y", }", - "expected": "[Error: hello]", + "expected": "Error { + "message": "hello", + "cause": "y", +}", "message": "expected Error: hello to deeply equal Error: hello", } `; exports[`error equality 7`] = ` { - "actual": "[Error: hello]", + "actual": "Error { + "message": "hello", +}", "diff": "- Expected + Received @@ -583,14 +626,19 @@ exports[`error equality 7`] = ` - "message": "world", + "message": "hello", }", - "expected": "[Error: world]", + "expected": "Error { + "message": "world", +}", "message": "expected Error: hello to deeply equal Error: world", } `; exports[`error equality 8`] = ` { - "actual": "[Error: hello]", + "actual": "Error { + "message": "hello", + "cause": "x", +}", "diff": "- Expected + Received @@ -600,7 +648,7 @@ exports[`error equality 8`] = ` + "message": "hello", + "cause": "x", }", - "expected": "Object { + "expected": "{ "something": "else", }", "message": "expected Error: hello to deeply equal { something: 'else' }", @@ -609,7 +657,16 @@ exports[`error equality 8`] = ` exports[`error equality 9`] = ` { - "actual": "[AggregateError: outer]", + "actual": "AggregateError { + "message": "outer", + "cause": "x", + "errors": [ + Error { + "message": "inner", + "cause": "x", + }, + ], +}", "diff": "- Expected + Received @@ -624,14 +681,26 @@ exports[`error equality 9`] = ` }, ], }", - "expected": "[AggregateError: outer]", + "expected": "AggregateError { + "message": "outer", + "cause": "x", + "errors": [ + Error { + "message": "inner", + "cause": "y", + }, + ], +}", "message": "expected AggregateError: outer { …(1) } to deeply equal AggregateError: outer { …(1) }", } `; exports[`error equality 10`] = ` { - "actual": "[Error: hello]", + "actual": "Error { + "message": "hello", + "cause": [Circular], +}", "diff": "- Expected + Received @@ -640,14 +709,20 @@ exports[`error equality 10`] = ` + "message": "hello", "cause": [Circular], }", - "expected": "[Error: world]", + "expected": "Error { + "message": "world", + "cause": [Circular], +}", "message": "expected Error: hello to deeply equal Error: world", } `; exports[`error equality 11`] = ` { - "actual": "[Error: hello]", + "actual": "Error { + "message": "hello", + "cause": "x", +}", "diff": "- Expected + Received @@ -667,7 +742,10 @@ exports[`error equality 11`] = ` exports[`error equality 12`] = ` { - "actual": "[Error: hello]", + "actual": "Error { + "message": "hello", + "cause": "x", +}", "diff": "- Expected + Received @@ -687,7 +765,10 @@ exports[`error equality 12`] = ` exports[`error equality 13`] = ` { - "actual": "[Error: hello]", + "actual": "Error { + "message": "hello", + "cause": "x", +}", "diff": "- Expected + Received @@ -706,11 +787,185 @@ exports[`error equality 13`] = ` } `; +exports[`expected and actual reuse the stringification from the diff 1`] = ` +{ + "actual": "1", + "diff": "- Expected ++ Received + +- 2 ++ 1", + "expected": "2", + "message": "expected 1 to deeply equal 2", +} +`; + +exports[`expected and actual reuse the stringification from the diff 2`] = ` +{ + "actual": "false", + "diff": "- Expected ++ Received + +- true ++ false", + "expected": "true", + "message": "expected false to deeply equal true", +} +`; + +exports[`expected and actual reuse the stringification from the diff 3`] = ` +{ + "actual": "a +b +c", + "diff": "- Expected ++ Received + + a +- B ++ b + c", + "expected": "a +B +c", + "message": "expected 'a\\nb\\nc' to deeply equal 'a\\nB\\nc'", +} +`; + +exports[`expected and actual reuse the stringification from the diff 4`] = ` +{ + "actual": "foo", + "diff": "Expected: "bar" +Received: "foo"", + "expected": "bar", + "message": "expected 'foo' to deeply equal 'bar'", +} +`; + +exports[`expected and actual reuse the stringification from the diff 5`] = ` +{ + "actual": "Map { + "x" => 1, + "y" => 2, +}", + "diff": "- Expected ++ Received + + Map { + "x" => 1, +- "y" => 3, ++ "y" => 2, + }", + "expected": "Map { + "x" => 1, + "y" => 3, +}", + "message": "expected Map{ 'x' => 1, 'y' => 2 } to deeply equal Map{ 'x' => 1, 'y' => 3 }", +} +`; + +exports[`expected and actual reuse the stringification from the diff 6`] = ` +{ + "actual": "Set { + 1, + 2, +}", + "diff": "- Expected ++ Received + + Set { + 1, +- 3, ++ 2, + }", + "expected": "Set { + 1, + 3, +}", + "message": "expected Set{ 1, 2 } to deeply equal Set{ 1, 3 }", +} +`; + +exports[`expected and actual reuse the stringification from the diff 7`] = ` +{ + "actual": "1", + "diff": "- Expected: +"foo" + ++ Received: +1", + "expected": ""foo"", + "message": "expected 1 to deeply equal 'foo'", +} +`; + +exports[`expected and actual reuse the stringification from the diff 8`] = ` +{ + "actual": "[ + { + "x": 1, + "y": 2, + "z": 3, + }, + { + "x": 3, + "y": 1, + "z": 2, + }, + { + "x": 2, + "y": 3, + "z": 1, + }, + "wrong", +]", + "diff": "- Expected ++ Received + + [ + { + "x": 1, + "y": 2, + "z": 3, + }, + { + "x": 3, + "y": 1, + "z": 2, + }, + { + "x": 2, + "y": 3, + "z": 1, + }, ++ "wrong", + ]", + "expected": "[ + { + "x": 1, + "y": 2, + "z": 3, + }, + { + "x": 3, + "y": 1, + "z": 2, + }, + { + "x": 2, + "y": 3, + "z": 1, + }, +]", + "message": "expected [ { x: 1, y: 2, z: 3 }, …(3) ] to deeply equal [ ObjectContaining {"x": 1}, …(2) ]", +} +`; + exports[`toBeOneOf() > error message 1`] = ` { - "actual": "undefined", + "actual": undefined, "diff": undefined, - "expected": "undefined", + "expected": undefined, "message": "expect(received).toBeOneOf() Expected value to be one of: @@ -727,9 +982,9 @@ Received: exports[`toBeOneOf() > error message 2`] = ` { - "actual": "undefined", + "actual": undefined, "diff": undefined, - "expected": "undefined", + "expected": undefined, "message": "expect(received).toBeOneOf() Expected value to be one of: @@ -744,7 +999,7 @@ Received: exports[`toBeOneOf() > error message 3`] = ` { - "actual": "Object { + "actual": "{ "a": 0, }", "diff": "- Expected: @@ -773,7 +1028,7 @@ toBeOneOf error message 4`] = ` { - "actual": "Object { + "actual": "{ "name": "mango", }", "diff": "- Expected @@ -787,7 +1042,7 @@ exports[`toBeOneOf() > error message 4`] = ` - ]>, + "name": "mango", }", - "expected": "Object { + "expected": "{ "name": toBeOneOf error message 4`] = ` exports[`toHaveBeenNthCalledWith error 1`] = ` { - "actual": "Array [ + "actual": "[ "Hi", ]", "diff": "- Expected @@ -810,7 +1065,7 @@ exports[`toHaveBeenNthCalledWith error 1`] = ` - "hey", + "Hi", ]", - "expected": "Array [ + "expected": "[ "hey", ]", "message": "expected 2nd "vi.fn()" call to have been called with [ 'hey' ]", @@ -819,11 +1074,11 @@ exports[`toHaveBeenNthCalledWith error 1`] = ` exports[`toHaveBeenNthCalledWith error 2`] = ` { - "actual": "undefined", + "actual": undefined, "diff": undefined, - "expected": "Array [ - "hey", -]", + "expected": [ + "hey", + ], "message": "expected 3rd "vi.fn()" call to have been called with [ 'hey' ], but called only 2 times", } `; @@ -850,7 +1105,7 @@ Received: "hellohellohellohellohellohellohellohellohellohellohellohellohellohell exports[`toMatch/toContain diff 3`] = ` { - "actual": "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", + "actual": ""hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello"", "diff": "- Expected: /world/ @@ -863,9 +1118,9 @@ exports[`toMatch/toContain diff 3`] = ` exports[`toSatisfy() > error message 1`] = ` { - "actual": "undefined", + "actual": undefined, "diff": undefined, - "expected": "undefined", + "expected": undefined, "message": "expect(received).toSatisfy() Expected value to satisfy: @@ -878,9 +1133,9 @@ Received: exports[`toSatisfy() > error message 2`] = ` { - "actual": "undefined", + "actual": undefined, "diff": undefined, - "expected": "undefined", + "expected": undefined, "message": "expect(received).toSatisfy() Expected value to satisfy: @@ -893,7 +1148,7 @@ Received: exports[`toSatisfy() > error message 3`] = ` { - "actual": "Object { + "actual": "{ "value": 2, }", "diff": "- Expected @@ -903,7 +1158,7 @@ exports[`toSatisfy() > error message 3`] = ` - "value": toSatisfy<[Function isOdd]>, + "value": 2, }", - "expected": "Object { + "expected": "{ "value": toSatisfy<[Function isOdd]>, }", "message": "expected { value: 2 } to deeply equal { value: toSatisfy<[Function isOdd]> }", @@ -912,7 +1167,7 @@ exports[`toSatisfy() > error message 3`] = ` exports[`toSatisfy() > error message 4`] = ` { - "actual": "Object { + "actual": "{ "value": 2, }", "diff": "- Expected @@ -922,7 +1177,7 @@ exports[`toSatisfy() > error message 4`] = ` - "value": toSatisfy<[Function isOdd], "ODD">, + "value": 2, }", - "expected": "Object { + "expected": "{ "value": toSatisfy<[Function isOdd], "ODD">, }", "message": "expected { value: 2 } to deeply equal { value: toSatisfy{…} }", diff --git a/test/unit/test/jest-expect.test.ts b/test/unit/test/jest-expect.test.ts index 70610b1ad6b1..eb501d7df4e8 100644 --- a/test/unit/test/jest-expect.test.ts +++ b/test/unit/test/jest-expect.test.ts @@ -2106,3 +2106,32 @@ it('diff', () => { snapshotError(() => expect({ hello: 'world' }).toBeUndefined()) snapshotError(() => expect({ hello: 'world' }).toBeNull()) }) + +it('expected and actual reuse the stringification from the diff', () => { + // primitives + snapshotError(() => expect(1).toEqual(2)) + snapshotError(() => expect(false).toEqual(true)) + + // strings + snapshotError(() => expect('a\nb\nc').toEqual('a\nB\nc')) + snapshotError(() => expect('foo').toEqual('bar')) + + // map/set (stringification happens after sorting) + snapshotError(() => expect(new Map([['x', 1], ['y', 2]])).toEqual(new Map([['x', 1], ['y', 3]]))) + snapshotError(() => expect(new Set([1, 2])).toEqual(new Set([1, 3]))) + + // mismatched types + snapshotError(() => expect(1).toEqual('foo')) + + // asymmetric matchers in arrays + snapshotError(() => expect([ + { x: 1, y: 2, z: 3 }, + { x: 3, y: 1, z: 2 }, + { x: 2, y: 3, z: 1 }, + 'wrong', + ]).toEqual([ + expect.objectContaining({ x: 1 }), + expect.objectContaining({ z: 2 }), + expect.objectContaining({ y: 3 }), + ])) +})