Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

expect: Highlight substring differences when matcher fails, part 2 #8528

Merged
merged 7 commits into from
Jun 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ bin/
flow-typed/**
packages/*/build/**
packages/*/build-es5/**
packages/jest-matcher-utils/src/cleanupSemantic.ts
packages/jest-diff/src/cleanupSemantic.ts
website/blog
website/build
website/node_modules
Expand Down
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
fixtures/failing-jsons/
packages/jest-matcher-utils/src/cleanupSemantic.ts
packages/jest-diff/src/cleanupSemantic.ts
packages/jest-config/src/__tests__/jest-preset.json
packages/pretty-format/perf/world.geo.json
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### Features

- `[expect]` Highlight substring differences when matcher fails, part 1 ([#8448](https://github.com/facebook/jest/pull/8448))
- `[expect]` Highlight substring differences when matcher fails, part 2 ([#8528](https://github.com/facebook/jest/pull/8528))
- `[jest-cli]` Improve chai support (with detailed output, to match jest exceptions) ([#8454](https://github.com/facebook/jest/pull/8454))

### Fixes
Expand Down
72 changes: 66 additions & 6 deletions packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,9 @@ string" 1`] = `
<green>- Expected</>
<red>+ Received</>

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

Expected: <green>\\"without trailing space\\"</>
Received: <red>\\"with<inverse> </></>
<red>trailing space\\"</>"
<green>- Expected</>
<red>+ Received</>

<green>- with<inverse>out</> trailing space</>
<red>+ with·</>
<red>+ trailing space</>"
`;

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

exports[`.toEqual() {pass: false} expect("type TypeName<T> = T extends Function ? \\"function\\" : \\"object\\";").toEqual("type TypeName<T> = T extends Function
? \\"function\\"
: \\"object\\";") 1`] = `
"<dim>expect(</><red>received</><dim>).</>toEqual<dim>(</><green>expected</><dim>) // deep equality</>

<green>- Expected</>
<red>+ Received</>

<green>- type TypeName<T> = T extends Function</>
<green>- ? \\"function\\"</>
<green>- : \\"object\\";</>
<red>+ type TypeName<T> = T extends Function<inverse> </>? \\"function\\"<inverse> </>: \\"object\\";</>"
`;

exports[`.toEqual() {pass: false} expect([1, 2]).toEqual([2, 1]) 1`] = `
"<dim>expect(</><red>received</><dim>).</>toEqual<dim>(</><green>expected</><dim>) // deep equality</>

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

exports[`.toHaveProperty() {pass: false} expect({"children": ["Roses are red.
Violets are blue.
Testing with Jest is good for you."], "props": null, "type": "pre"}).toHaveProperty('children,0', "Roses are red, violets are blue.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😀

Testing with Jest
Is good for you.") 1`] = `
"<dim>expect(</><red>received</><dim>).</>toHaveProperty<dim>(</><green>path</><dim>, </><green>value</><dim>)</>

Expected path: <green>[\\"children\\", 0]</>

<green>- Expected value</>
<red>+ Received value</>

<green>- Roses are red<inverse>, v</>iolets are blue.</>
<red>+ Roses are red<inverse>.</></>
<red>+ <inverse>V</>iolets are blue.</>
<green>- Testing with Jest</>
<green>- <inverse>I</>s good for you.</>
<red>+ Testing with Jest<inverse> i</>s good for you.</>"
`;

exports[`.toHaveProperty() {pass: false} expect({"key": 1}).toHaveProperty('not') 1`] = `
"<dim>expect(</><red>received</><dim>).</>toHaveProperty<dim>(</><green>path</><dim>)</>

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

exports[`.toStrictEqual() displays substring diff for multiple lines 1`] = `
"<dim>expect(</><red>received</><dim>).</>toStrictEqual<dim>(</><green>expected</><dim>) // deep equality</>

<green>- Expected</>
<red>+ Received</>

<green>- 6<inverse>9</> |·</>
<red>+ 6<inverse>8</> |·</>
<green>- <inverse>70</> | test('assert.doesNotThrow', () => {</>
<red>+ <inverse>69</> | test('assert.doesNotThrow', () => {</>
<green>- > 7<inverse>1</> | assert.doesNotThrow(() => {</>
<red>+ > 7<inverse>0</> | assert.doesNotThrow(() => {</>
<dim> | ^</>
<green>- 7<inverse>2</> | throw Error('err!');</>
<red>+ 7<inverse>1</> | throw Error('err!');</>
<green>- 7<inverse>3</> | });</>
<red>+ 7<inverse>2</> | });</>
<green>- 7<inverse>4</> | });</>
<red>+ 7<inverse>3</> | });</>
<green>- at Object.doesNotThrow (__tests__/assertionError.test.js:7<inverse>1</>:10)</>
<red>+ at Object.doesNotThrow (__tests__/assertionError.test.js:7<inverse>0</>:10)</>"
`;

exports[`.toStrictEqual() matches the expected snapshot when it fails 1`] = `
"<dim>expect(</><red>received</><dim>).</>toStrictEqual<dim>(</><green>expected</><dim>) // deep equality</>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,18 @@ Expected message: not <green>\\"Invalid array length\\"</>
"
`;

exports[`toThrow error-message fail multiline diff highlight incorrect expected space 1`] = `
"<dim>expect(</><red>received</><dim>).</>toThrow<dim>(</><green>expected</><dim>)</>

<green>- Expected message</>
<red>+ Received message</>

<green>- There is no route defined for key Settings.<inverse> </></>
<red>+ There is no route defined for key Settings.</>
<dim> Must be one of: 'Home'</>
"
`;

exports[`toThrow expected is undefined threw, but should not have (non-error falsey) 1`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toThrow<dim>()</>

Expand Down Expand Up @@ -457,6 +469,18 @@ Expected message: not <green>\\"Invalid array length\\"</>
"
`;

exports[`toThrowError error-message fail multiline diff highlight incorrect expected space 1`] = `
"<dim>expect(</><red>received</><dim>).</>toThrowError<dim>(</><green>expected</><dim>)</>

<green>- Expected message</>
<red>+ Received message</>

<green>- There is no route defined for key Settings.<inverse> </></>
<red>+ There is no route defined for key Settings.</>
<dim> Must be one of: 'Home'</>
"
`;

exports[`toThrowError expected is undefined threw, but should not have (non-error falsey) 1`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toThrowError<dim>()</>

Expand Down
51 changes: 46 additions & 5 deletions packages/expect/src/__tests__/matchers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

const {stringify} = require('jest-matcher-utils');
Expand Down Expand Up @@ -330,6 +329,32 @@ describe('.toStrictEqual()', () => {
).toThrowErrorMatchingSnapshot();
});

it('displays substring diff for multiple lines', () => {
const expected = [
' 69 | ',
" 70 | test('assert.doesNotThrow', () => {",
' > 71 | assert.doesNotThrow(() => {',
' | ^',
" 72 | throw Error('err!');",
' 73 | });',
' 74 | });',
' at Object.doesNotThrow (__tests__/assertionError.test.js:71:10)',
].join('\n');
const received = [
' 68 | ',
" 69 | test('assert.doesNotThrow', () => {",
' > 70 | assert.doesNotThrow(() => {',
' | ^',
" 71 | throw Error('err!');",
' 72 | });',
' 73 | });',
' at Object.doesNotThrow (__tests__/assertionError.test.js:70:10)',
].join('\n');
expect(() =>
jestExpect(received).toStrictEqual(expected),
).toThrowErrorMatchingSnapshot();
});

it('does not pass for different types', () => {
expect({
test: new TestClassA(1, 2),
Expand Down Expand Up @@ -371,6 +396,10 @@ describe('.toEqual()', () => {
[{a: 5}, {b: 6}],
['banana', 'apple'],
['1\u{00A0}234,57\u{00A0}$', '1 234,57 $'], // issues/6881
[
'type TypeName<T> = T extends Function ? "function" : "object";',
'type TypeName<T> = T extends Function\n? "function"\n: "object";',
],
[null, undefined],
[[1], [2]],
[[1, 2], [2, 1]],
Expand Down Expand Up @@ -1361,13 +1390,24 @@ describe('.toHaveProperty()', () => {
const memoized = function() {};
memoized.memo = [];

const receivedDiff = {
const pathDiff = ['children', 0];

const receivedDiffSingle = {
children: ['"That cartoon"'],
props: null,
type: 'p',
};
const pathDiff = ['children', 0];
const valueDiff = '"That cat cartoon"';
const valueDiffSingle = '"That cat cartoon"';

const receivedDiffMultiple = {
children: [
'Roses are red.\nViolets are blue.\nTesting with Jest is good for you.',
],
props: null,
type: 'pre',
};
const valueDiffMultiple =
'Roses are red, violets are blue.\nTesting with Jest\nIs good for you.';

[
[{a: {b: {c: {d: 1}}}}, 'a.b.c.d', 1],
Expand Down Expand Up @@ -1405,7 +1445,8 @@ describe('.toHaveProperty()', () => {
[{a: {b: {c: {d: 1}}}}, 'a.b.c.d', 2],
[{'a.b.c.d': 1}, 'a.b.c.d', 2],
[{'a.b.c.d': 1}, ['a.b.c.d'], 2],
[receivedDiff, pathDiff, valueDiff],
[receivedDiffSingle, pathDiff, valueDiffSingle],
[receivedDiffMultiple, pathDiff, valueDiffMultiple],
[{a: {b: {c: {d: 1}}}}, ['a', 'b', 'c', 'd'], 2],
[{a: {b: {c: {}}}}, 'a.b.c.d', 1],
[{a: 1}, 'a.b.c.d', 5],
Expand Down
14 changes: 13 additions & 1 deletion packages/expect/src/__tests__/toThrowMatchers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

'use strict';
Expand Down Expand Up @@ -266,6 +265,19 @@ class customError extends Error {
}).not[toThrow]({message}),
).toThrowErrorMatchingSnapshot();
});

test('multiline diff highlight incorrect expected space', () => {
// jest/issues/2673
const a =
"There is no route defined for key Settings. \nMust be one of: 'Home'";
const b =
"There is no route defined for key Settings.\nMust be one of: 'Home'";
expect(() =>
jestExpect(() => {
throw new ErrorMessage(b);
})[toThrow]({message: a}),
).toThrowErrorMatchingSnapshot();
});
});
});

Expand Down
18 changes: 14 additions & 4 deletions packages/expect/src/toThrowMatchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
RECEIVED_COLOR,
matcherErrorMessage,
matcherHint,
printDiffOrStringify,
printExpected,
printReceived,
printWithType,
Expand Down Expand Up @@ -231,13 +232,22 @@ const toThrowExpectedObject = (
: () =>
matcherHint(matcherName, undefined, undefined, options) +
'\n\n' +
formatExpected('Expected message: ', expected.message) +
(thrown === null
? '\n' + DID_NOT_THROW
? formatExpected('Expected message: ', expected.message) +
'\n' +
DID_NOT_THROW
: thrown.hasMessage
? formatReceived('Received message: ', thrown, 'message') +
? printDiffOrStringify(
expected.message,
thrown.message,
'Expected message',
'Received message',
true,
) +
'\n' +
formatStack(thrown)
: formatReceived('Received value: ', thrown, 'value'));
: formatExpected('Expected message: ', expected.message) +
formatReceived('Received value: ', thrown, 'value'));

return {message, pass};
};
Expand Down
Loading