Skip to content

Commit 98bafaa

Browse files
pedrottimarkSimenB
authored andcommitted
expect: Highlight substring differences when matcher fails, part 2 (#8528)
1 parent 62ca17c commit 98bafaa

27 files changed

+1806
-482
lines changed

.eslintignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ bin/
44
flow-typed/**
55
packages/*/build/**
66
packages/*/build-es5/**
7-
packages/jest-matcher-utils/src/cleanupSemantic.ts
7+
packages/jest-diff/src/cleanupSemantic.ts
88
website/blog
99
website/build
1010
website/node_modules

.prettierignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
fixtures/failing-jsons/
2-
packages/jest-matcher-utils/src/cleanupSemantic.ts
2+
packages/jest-diff/src/cleanupSemantic.ts
33
packages/jest-config/src/__tests__/jest-preset.json
44
packages/pretty-format/perf/world.geo.json

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### Features
44

55
- `[expect]` Highlight substring differences when matcher fails, part 1 ([#8448](https://github.com/facebook/jest/pull/8448))
6+
- `[expect]` Highlight substring differences when matcher fails, part 2 ([#8528](https://github.com/facebook/jest/pull/8528))
67
- `[jest-cli]` Improve chai support (with detailed output, to match jest exceptions) ([#8454](https://github.com/facebook/jest/pull/8454))
78
- `[*]` Manage the global timeout with `--testTimeout` command line argument. ([#8456](https://github.com/facebook/jest/pull/8456))
89
- `[pretty-format]` Render custom displayName of memoized components

packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap

+66-6
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,9 @@ string" 1`] = `
303303
<green>- Expected</>
304304
<red>+ Received</>
305305

306-
<green>- 3</>
307-
<red>+ four</>
308-
<red>+ 4</>
306+
<green>- <inverse>3</></>
307+
<red>+ <inverse>four</></>
308+
<red>+ <inverse>4</></>
309309
<dim> line</>
310310
<dim> string</>"
311311
`;
@@ -321,9 +321,12 @@ exports[`.toBe() fails for: "with
321321
trailing space" and "without trailing space" 1`] = `
322322
"<dim>expect(</><red>received</><dim>).</>toBe<dim>(</><green>expected</><dim>) // Object.is equality</>
323323

324-
Expected: <green>\\"without trailing space\\"</>
325-
Received: <red>\\"with<inverse> </></>
326-
<red>trailing space\\"</>"
324+
<green>- Expected</>
325+
<red>+ Received</>
326+
327+
<green>- with<inverse>out</> trailing space</>
328+
<red>+ with·</>
329+
<red>+ trailing space</>"
327330
`;
328331

329332
exports[`.toBe() fails for: /received/ and /expected/ 1`] = `
@@ -1971,6 +1974,20 @@ Expected: <green>\\"apple\\"</>
19711974
Received: <red>\\"banana\\"</>"
19721975
`;
19731976

1977+
exports[`.toEqual() {pass: false} expect("type TypeName<T> = T extends Function ? \\"function\\" : \\"object\\";").toEqual("type TypeName<T> = T extends Function
1978+
? \\"function\\"
1979+
: \\"object\\";") 1`] = `
1980+
"<dim>expect(</><red>received</><dim>).</>toEqual<dim>(</><green>expected</><dim>) // deep equality</>
1981+
1982+
<green>- Expected</>
1983+
<red>+ Received</>
1984+
1985+
<green>- type TypeName<T> = T extends Function</>
1986+
<green>- ? \\"function\\"</>
1987+
<green>- : \\"object\\";</>
1988+
<red>+ type TypeName<T> = T extends Function<inverse> </>? \\"function\\"<inverse> </>: \\"object\\";</>"
1989+
`;
1990+
19741991
exports[`.toEqual() {pass: false} expect([1, 2]).toEqual([2, 1]) 1`] = `
19751992
"<dim>expect(</><red>received</><dim>).</>toEqual<dim>(</><green>expected</><dim>) // deep equality</>
19761993

@@ -3080,6 +3097,26 @@ Expected value: <green>\\"\\\\\\"That <inverse>cat </>cartoon\\\\\\"\\"</>
30803097
Received value: <red>\\"\\\\\\"That cartoon\\\\\\"\\"</>"
30813098
`;
30823099

3100+
exports[`.toHaveProperty() {pass: false} expect({"children": ["Roses are red.
3101+
Violets are blue.
3102+
Testing with Jest is good for you."], "props": null, "type": "pre"}).toHaveProperty('children,0', "Roses are red, violets are blue.
3103+
Testing with Jest
3104+
Is good for you.") 1`] = `
3105+
"<dim>expect(</><red>received</><dim>).</>toHaveProperty<dim>(</><green>path</><dim>, </><green>value</><dim>)</>
3106+
3107+
Expected path: <green>[\\"children\\", 0]</>
3108+
3109+
<green>- Expected value</>
3110+
<red>+ Received value</>
3111+
3112+
<green>- Roses are red<inverse>, v</>iolets are blue.</>
3113+
<red>+ Roses are red<inverse>.</></>
3114+
<red>+ <inverse>V</>iolets are blue.</>
3115+
<green>- Testing with Jest</>
3116+
<green>- <inverse>I</>s good for you.</>
3117+
<red>+ Testing with Jest<inverse> i</>s good for you.</>"
3118+
`;
3119+
30833120
exports[`.toHaveProperty() {pass: false} expect({"key": 1}).toHaveProperty('not') 1`] = `
30843121
"<dim>expect(</><red>received</><dim>).</>toHaveProperty<dim>(</><green>path</><dim>)</>
30853122

@@ -3527,6 +3564,29 @@ Expected: <green>\\"<inverse>Another caveat is that</> Jest will not typecheck y
35273564
Received: <red>\\"<inverse>Because TypeScript support in Babel is just transpilation,</> Jest will not type<inverse>-</>check your tests<inverse> as they run</>.\\"</>"
35283565
`;
35293566

3567+
exports[`.toStrictEqual() displays substring diff for multiple lines 1`] = `
3568+
"<dim>expect(</><red>received</><dim>).</>toStrictEqual<dim>(</><green>expected</><dim>) // deep equality</>
3569+
3570+
<green>- Expected</>
3571+
<red>+ Received</>
3572+
3573+
<green>- 6<inverse>9</> |·</>
3574+
<red>+ 6<inverse>8</> |·</>
3575+
<green>- <inverse>70</> | test('assert.doesNotThrow', () => {</>
3576+
<red>+ <inverse>69</> | test('assert.doesNotThrow', () => {</>
3577+
<green>- > 7<inverse>1</> | assert.doesNotThrow(() => {</>
3578+
<red>+ > 7<inverse>0</> | assert.doesNotThrow(() => {</>
3579+
<dim> | ^</>
3580+
<green>- 7<inverse>2</> | throw Error('err!');</>
3581+
<red>+ 7<inverse>1</> | throw Error('err!');</>
3582+
<green>- 7<inverse>3</> | });</>
3583+
<red>+ 7<inverse>2</> | });</>
3584+
<green>- 7<inverse>4</> | });</>
3585+
<red>+ 7<inverse>3</> | });</>
3586+
<green>- at Object.doesNotThrow (__tests__/assertionError.test.js:7<inverse>1</>:10)</>
3587+
<red>+ at Object.doesNotThrow (__tests__/assertionError.test.js:7<inverse>0</>:10)</>"
3588+
`;
3589+
35303590
exports[`.toStrictEqual() matches the expected snapshot when it fails 1`] = `
35313591
"<dim>expect(</><red>received</><dim>).</>toStrictEqual<dim>(</><green>expected</><dim>) // deep equality</>
35323592

packages/expect/src/__tests__/__snapshots__/toThrowMatchers.test.js.snap

+24
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,18 @@ Expected message: not <green>\\"Invalid array length\\"</>
161161
"
162162
`;
163163

164+
exports[`toThrow error-message fail multiline diff highlight incorrect expected space 1`] = `
165+
"<dim>expect(</><red>received</><dim>).</>toThrow<dim>(</><green>expected</><dim>)</>
166+
167+
<green>- Expected message</>
168+
<red>+ Received message</>
169+
170+
<green>- There is no route defined for key Settings.<inverse> </></>
171+
<red>+ There is no route defined for key Settings.</>
172+
<dim> Must be one of: 'Home'</>
173+
"
174+
`;
175+
164176
exports[`toThrow expected is undefined threw, but should not have (non-error falsey) 1`] = `
165177
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toThrow<dim>()</>
166178

@@ -457,6 +469,18 @@ Expected message: not <green>\\"Invalid array length\\"</>
457469
"
458470
`;
459471

472+
exports[`toThrowError error-message fail multiline diff highlight incorrect expected space 1`] = `
473+
"<dim>expect(</><red>received</><dim>).</>toThrowError<dim>(</><green>expected</><dim>)</>
474+
475+
<green>- Expected message</>
476+
<red>+ Received message</>
477+
478+
<green>- There is no route defined for key Settings.<inverse> </></>
479+
<red>+ There is no route defined for key Settings.</>
480+
<dim> Must be one of: 'Home'</>
481+
"
482+
`;
483+
460484
exports[`toThrowError expected is undefined threw, but should not have (non-error falsey) 1`] = `
461485
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toThrowError<dim>()</>
462486

packages/expect/src/__tests__/matchers.test.js

+46-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
*
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
6-
*
76
*/
87

98
const {stringify} = require('jest-matcher-utils');
@@ -330,6 +329,32 @@ describe('.toStrictEqual()', () => {
330329
).toThrowErrorMatchingSnapshot();
331330
});
332331

332+
it('displays substring diff for multiple lines', () => {
333+
const expected = [
334+
' 69 | ',
335+
" 70 | test('assert.doesNotThrow', () => {",
336+
' > 71 | assert.doesNotThrow(() => {',
337+
' | ^',
338+
" 72 | throw Error('err!');",
339+
' 73 | });',
340+
' 74 | });',
341+
' at Object.doesNotThrow (__tests__/assertionError.test.js:71:10)',
342+
].join('\n');
343+
const received = [
344+
' 68 | ',
345+
" 69 | test('assert.doesNotThrow', () => {",
346+
' > 70 | assert.doesNotThrow(() => {',
347+
' | ^',
348+
" 71 | throw Error('err!');",
349+
' 72 | });',
350+
' 73 | });',
351+
' at Object.doesNotThrow (__tests__/assertionError.test.js:70:10)',
352+
].join('\n');
353+
expect(() =>
354+
jestExpect(received).toStrictEqual(expected),
355+
).toThrowErrorMatchingSnapshot();
356+
});
357+
333358
it('does not pass for different types', () => {
334359
expect({
335360
test: new TestClassA(1, 2),
@@ -371,6 +396,10 @@ describe('.toEqual()', () => {
371396
[{a: 5}, {b: 6}],
372397
['banana', 'apple'],
373398
['1\u{00A0}234,57\u{00A0}$', '1 234,57 $'], // issues/6881
399+
[
400+
'type TypeName<T> = T extends Function ? "function" : "object";',
401+
'type TypeName<T> = T extends Function\n? "function"\n: "object";',
402+
],
374403
[null, undefined],
375404
[[1], [2]],
376405
[[1, 2], [2, 1]],
@@ -1361,13 +1390,24 @@ describe('.toHaveProperty()', () => {
13611390
const memoized = function() {};
13621391
memoized.memo = [];
13631392

1364-
const receivedDiff = {
1393+
const pathDiff = ['children', 0];
1394+
1395+
const receivedDiffSingle = {
13651396
children: ['"That cartoon"'],
13661397
props: null,
13671398
type: 'p',
13681399
};
1369-
const pathDiff = ['children', 0];
1370-
const valueDiff = '"That cat cartoon"';
1400+
const valueDiffSingle = '"That cat cartoon"';
1401+
1402+
const receivedDiffMultiple = {
1403+
children: [
1404+
'Roses are red.\nViolets are blue.\nTesting with Jest is good for you.',
1405+
],
1406+
props: null,
1407+
type: 'pre',
1408+
};
1409+
const valueDiffMultiple =
1410+
'Roses are red, violets are blue.\nTesting with Jest\nIs good for you.';
13711411

13721412
[
13731413
[{a: {b: {c: {d: 1}}}}, 'a.b.c.d', 1],
@@ -1405,7 +1445,8 @@ describe('.toHaveProperty()', () => {
14051445
[{a: {b: {c: {d: 1}}}}, 'a.b.c.d', 2],
14061446
[{'a.b.c.d': 1}, 'a.b.c.d', 2],
14071447
[{'a.b.c.d': 1}, ['a.b.c.d'], 2],
1408-
[receivedDiff, pathDiff, valueDiff],
1448+
[receivedDiffSingle, pathDiff, valueDiffSingle],
1449+
[receivedDiffMultiple, pathDiff, valueDiffMultiple],
14091450
[{a: {b: {c: {d: 1}}}}, ['a', 'b', 'c', 'd'], 2],
14101451
[{a: {b: {c: {}}}}, 'a.b.c.d', 1],
14111452
[{a: 1}, 'a.b.c.d', 5],

packages/expect/src/__tests__/toThrowMatchers.test.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
*
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
6-
*
76
*/
87

98
'use strict';
@@ -266,6 +265,19 @@ class customError extends Error {
266265
}).not[toThrow]({message}),
267266
).toThrowErrorMatchingSnapshot();
268267
});
268+
269+
test('multiline diff highlight incorrect expected space', () => {
270+
// jest/issues/2673
271+
const a =
272+
"There is no route defined for key Settings. \nMust be one of: 'Home'";
273+
const b =
274+
"There is no route defined for key Settings.\nMust be one of: 'Home'";
275+
expect(() =>
276+
jestExpect(() => {
277+
throw new ErrorMessage(b);
278+
})[toThrow]({message: a}),
279+
).toThrowErrorMatchingSnapshot();
280+
});
269281
});
270282
});
271283

packages/expect/src/toThrowMatchers.ts

+14-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
RECEIVED_COLOR,
1313
matcherErrorMessage,
1414
matcherHint,
15+
printDiffOrStringify,
1516
printExpected,
1617
printReceived,
1718
printWithType,
@@ -231,13 +232,22 @@ const toThrowExpectedObject = (
231232
: () =>
232233
matcherHint(matcherName, undefined, undefined, options) +
233234
'\n\n' +
234-
formatExpected('Expected message: ', expected.message) +
235235
(thrown === null
236-
? '\n' + DID_NOT_THROW
236+
? formatExpected('Expected message: ', expected.message) +
237+
'\n' +
238+
DID_NOT_THROW
237239
: thrown.hasMessage
238-
? formatReceived('Received message: ', thrown, 'message') +
240+
? printDiffOrStringify(
241+
expected.message,
242+
thrown.message,
243+
'Expected message',
244+
'Received message',
245+
true,
246+
) +
247+
'\n' +
239248
formatStack(thrown)
240-
: formatReceived('Received value: ', thrown, 'value'));
249+
: formatExpected('Expected message: ', expected.message) +
250+
formatReceived('Received value: ', thrown, 'value'));
241251

242252
return {message, pass};
243253
};

0 commit comments

Comments
 (0)