Skip to content

Commit

Permalink
expect: Improve report when matcher fails, part 19 (#8367)
Browse files Browse the repository at this point in the history
* expect: Improve report when matcher fails, part 19

* Delete obsolete code from printConstructorName

* Update CHANGELOG.md

* Call printReceivedConstructorName with constructor arg

* Append extends to be clearer when negative assertion fails
  • Loading branch information
pedrottimark authored and jeysal committed Apr 25, 2019
1 parent 4d3c1a1 commit bf6c71a
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 63 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -10,6 +10,7 @@
- `[docs]` Add DynamoDB guide ([#8319](https://github.com/facebook/jest/pull/8319))
- `[expect]` Improve report when matcher fails, part 17 ([#8349](https://github.com/facebook/jest/pull/8349))
- `[expect]` Improve report when matcher fails, part 18 ([#8356](https://github.com/facebook/jest/pull/8356))
- `[expect]` Improve report when matcher fails, part 19 ([#8367](https://github.com/facebook/jest/pull/8367))

### Fixes

Expand Down
31 changes: 23 additions & 8 deletions packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap
Expand Up @@ -1265,6 +1265,7 @@ exports[`.toBeInstanceOf() failing "a" and [Function String] 1`] = `
"<dim>expect(</><red>received</><dim>).</>toBeInstanceOf<dim>(</><green>expected</><dim>)</>

Expected constructor: <green>String</>

Received value has no prototype
Received value: <red>\\"a\\"</>"
`;
Expand All @@ -1274,13 +1275,14 @@ exports[`.toBeInstanceOf() failing /\\w+/ and [Function anonymous] 1`] = `

Expected constructor name is an empty string
Received constructor: <red>RegExp</>
Received value: <red>/\\\\w+/</>"
"
`;

exports[`.toBeInstanceOf() failing {} and [Function A] 1`] = `
"<dim>expect(</><red>received</><dim>).</>toBeInstanceOf<dim>(</><green>expected</><dim>)</>

Expected constructor: <green>A</>

Received value has no prototype
Received value: <red>{}</>"
`;
Expand All @@ -1290,21 +1292,22 @@ exports[`.toBeInstanceOf() failing {} and [Function B] 1`] = `

Expected constructor: <green>B</>
Received constructor: <red>A</>
Received value: <red>{}</>"
"
`;

exports[`.toBeInstanceOf() failing {} and [Function RegExp] 1`] = `
"<dim>expect(</><red>received</><dim>).</>toBeInstanceOf<dim>(</><green>expected</><dim>)</>

Expected constructor: <green>RegExp</>
Received constructor name is an empty string
Received value: <red>{}</>"
"
`;

exports[`.toBeInstanceOf() failing 1 and [Function Number] 1`] = `
"<dim>expect(</><red>received</><dim>).</>toBeInstanceOf<dim>(</><green>expected</><dim>)</>

Expected constructor: <green>Number</>

Received value has no prototype
Received value: <red>1</>"
`;
Expand All @@ -1313,6 +1316,7 @@ exports[`.toBeInstanceOf() failing null and [Function String] 1`] = `
"<dim>expect(</><red>received</><dim>).</>toBeInstanceOf<dim>(</><green>expected</><dim>)</>

Expected constructor: <green>String</>

Received value has no prototype
Received value: <red>null</>"
`;
Expand All @@ -1321,6 +1325,7 @@ exports[`.toBeInstanceOf() failing true and [Function Boolean] 1`] = `
"<dim>expect(</><red>received</><dim>).</>toBeInstanceOf<dim>(</><green>expected</><dim>)</>

Expected constructor: <green>Boolean</>

Received value has no prototype
Received value: <red>true</>"
`;
Expand All @@ -1329,6 +1334,7 @@ exports[`.toBeInstanceOf() failing undefined and [Function String] 1`] = `
"<dim>expect(</><red>received</><dim>).</>toBeInstanceOf<dim>(</><green>expected</><dim>)</>

Expected constructor: <green>String</>

Received value has no prototype
Received value: <red>undefined</>"
`;
Expand All @@ -1337,35 +1343,44 @@ exports[`.toBeInstanceOf() passing [] and [Function Array] 1`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toBeInstanceOf<dim>(</><green>expected</><dim>)</>

Expected constructor: not <green>Array</>
Received value: <red>[]</>"
"
`;

exports[`.toBeInstanceOf() passing {} and [Function A] 1`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toBeInstanceOf<dim>(</><green>expected</><dim>)</>

Expected constructor: not <green>A</>
Received value: <red>{}</>"
"
`;

exports[`.toBeInstanceOf() passing {} and [Function B] 1`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toBeInstanceOf<dim>(</><green>expected</><dim>)</>

Expected constructor: not <green>B</>
Received value: <red>{}</>"
Received constructor: <red>C</> extends <green>B</>
"
`;

exports[`.toBeInstanceOf() passing {} and [Function B] 2`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toBeInstanceOf<dim>(</><green>expected</><dim>)</>

Expected constructor: not <green>B</>
Received constructor: <red>E</> extends … extends <green>B</>
"
`;

exports[`.toBeInstanceOf() passing {} and [Function name() {}] 1`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toBeInstanceOf<dim>(</><green>expected</><dim>)</>

Expected constructor name is not a string
Received value: <red>{}</>"
"
`;

exports[`.toBeInstanceOf() passing Map {} and [Function Map] 1`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toBeInstanceOf<dim>(</><green>expected</><dim>)</>

Expected constructor: not <green>Map</>
Received value: <red>Map {}</>"
"
`;

exports[`.toBeInstanceOf() throws if constructor is not a function 1`] = `
Expand Down
Expand Up @@ -89,16 +89,16 @@ Received message: <red>\\"apple\\"</>
exports[`toThrow error class did not throw at all 1`] = `
"<dim>expect(</><red>received</><dim>).</>toThrow<dim>(</><green>expected</><dim>)</>

Expected name: <green>\\"Err\\"</>
Expected constructor: <green>Err</>

Received function did not throw"
`;

exports[`toThrow error class threw, but class did not match (error) 1`] = `
"<dim>expect(</><red>received</><dim>).</>toThrow<dim>(</><green>expected</><dim>)</>

Expected name: <green>\\"Err2\\"</>
Received name: <red>\\"Error\\"</>
Expected constructor: <green>Err2</>
Received constructor: <red>Err</>

Received message: <red>\\"apple\\"</>

Expand All @@ -108,17 +108,38 @@ Received message: <red>\\"apple\\"</>
exports[`toThrow error class threw, but class did not match (non-error falsey) 1`] = `
"<dim>expect(</><red>received</><dim>).</>toThrow<dim>(</><green>expected</><dim>)</>

Expected name: <green>\\"Err2\\"</>
Expected constructor: <green>Err2</>

Received value: <red>undefined</>
"
`;

exports[`toThrow error class threw, but class should not match (error subclass) 1`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toThrow<dim>(</><green>expected</><dim>)</>

Expected constructor: not <green>Err</>
Received constructor: <red>SubErr</> extends <green>Err</>

Received message: <red>\\"apple\\"</>

<dim>at jestExpect (</>packages/expect/src/__tests__/toThrowMatchers-test.js<dim>:24:74)</>"
`;

exports[`toThrow error class threw, but class should not match (error subsubclass) 1`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toThrow<dim>(</><green>expected</><dim>)</>

Expected constructor: not <green>Err</>
Received constructor: <red>SubSubErr</> extends … extends <green>Err</>

Received message: <red>\\"apple\\"</>

<dim>at jestExpect (</>packages/expect/src/__tests__/toThrowMatchers-test.js<dim>:24:74)</>"
`;

exports[`toThrow error class threw, but class should not match (error) 1`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toThrow<dim>(</><green>expected</><dim>)</>

Expected name: <green>\\"Err\\"</>
Received name: <red>\\"Error\\"</>
Expected constructor: not <green>Err</>

Received message: <red>\\"apple\\"</>

Expand Down Expand Up @@ -174,8 +195,8 @@ Received function did not throw"
exports[`toThrow promise/async throws if Error-like object is returned threw, but class did not match 1`] = `
"<dim>expect(</><red>received</><dim>).</>rejects<dim>.</>toThrow<dim>(</><green>expected</><dim>)</>

Expected name: <green>\\"Err2\\"</>
Received name: <red>\\"Error\\"</>
Expected constructor: <green>Err2</>
Received constructor: <red>Err</>

Received message: <red>\\"async apple\\"</>

Expand Down Expand Up @@ -364,16 +385,16 @@ Received message: <red>\\"apple\\"</>
exports[`toThrowError error class did not throw at all 1`] = `
"<dim>expect(</><red>received</><dim>).</>toThrowError<dim>(</><green>expected</><dim>)</>

Expected name: <green>\\"Err\\"</>
Expected constructor: <green>Err</>

Received function did not throw"
`;

exports[`toThrowError error class threw, but class did not match (error) 1`] = `
"<dim>expect(</><red>received</><dim>).</>toThrowError<dim>(</><green>expected</><dim>)</>

Expected name: <green>\\"Err2\\"</>
Received name: <red>\\"Error\\"</>
Expected constructor: <green>Err2</>
Received constructor: <red>Err</>

Received message: <red>\\"apple\\"</>

Expand All @@ -383,17 +404,38 @@ Received message: <red>\\"apple\\"</>
exports[`toThrowError error class threw, but class did not match (non-error falsey) 1`] = `
"<dim>expect(</><red>received</><dim>).</>toThrowError<dim>(</><green>expected</><dim>)</>

Expected name: <green>\\"Err2\\"</>
Expected constructor: <green>Err2</>

Received value: <red>undefined</>
"
`;

exports[`toThrowError error class threw, but class should not match (error subclass) 1`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toThrowError<dim>(</><green>expected</><dim>)</>

Expected constructor: not <green>Err</>
Received constructor: <red>SubErr</> extends <green>Err</>

Received message: <red>\\"apple\\"</>

<dim>at jestExpect (</>packages/expect/src/__tests__/toThrowMatchers-test.js<dim>:24:74)</>"
`;

exports[`toThrowError error class threw, but class should not match (error subsubclass) 1`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toThrowError<dim>(</><green>expected</><dim>)</>

Expected constructor: not <green>Err</>
Received constructor: <red>SubSubErr</> extends … extends <green>Err</>

Received message: <red>\\"apple\\"</>

<dim>at jestExpect (</>packages/expect/src/__tests__/toThrowMatchers-test.js<dim>:24:74)</>"
`;

exports[`toThrowError error class threw, but class should not match (error) 1`] = `
"<dim>expect(</><red>received</><dim>).</>not<dim>.</>toThrowError<dim>(</><green>expected</><dim>)</>

Expected name: <green>\\"Err\\"</>
Received name: <red>\\"Error\\"</>
Expected constructor: not <green>Err</>

Received message: <red>\\"apple\\"</>

Expand Down Expand Up @@ -449,8 +491,8 @@ Received function did not throw"
exports[`toThrowError promise/async throws if Error-like object is returned threw, but class did not match 1`] = `
"<dim>expect(</><red>received</><dim>).</>rejects<dim>.</>toThrowError<dim>(</><green>expected</><dim>)</>

Expected name: <green>\\"Err2\\"</>
Received name: <red>\\"Error\\"</>
Expected constructor: <green>Err2</>
Received constructor: <red>Err</>

Received message: <red>\\"async apple\\"</>

Expand Down
5 changes: 4 additions & 1 deletion packages/expect/src/__tests__/matchers.test.js
Expand Up @@ -687,6 +687,8 @@ describe('.toBeInstanceOf()', () => {
class A {}
class B {}
class C extends B {}
class D extends C {}
class E extends D {}

class HasStaticNameMethod {
constructor() {}
Expand All @@ -705,7 +707,8 @@ describe('.toBeInstanceOf()', () => {
[new Map(), Map],
[[], Array],
[new A(), A],
[new C(), B], // subclass
[new C(), B], // C extends B
[new E(), B], // E extends … extends B
[new HasStaticNameMethod(), HasStaticNameMethod],
].forEach(([a, b]) => {
test(`passing ${stringify(a)} and ${stringify(b)}`, () => {
Expand Down
34 changes: 34 additions & 0 deletions packages/expect/src/__tests__/toThrowMatchers.test.js
Expand Up @@ -146,6 +146,24 @@ class customError extends Error {
});

describe('error class', () => {
class SubErr extends Err {
constructor(...args) {
super(...args);
// In a carefully written error subclass,
// name property is equal to constructor name.
this.name = this.constructor.name;
}
}

class SubSubErr extends SubErr {
constructor(...args) {
super(...args);
// In a carefully written error subclass,
// name property is equal to constructor name.
this.name = this.constructor.name;
}
}

it('passes', () => {
jestExpect(() => {
throw new Err();
Expand Down Expand Up @@ -189,6 +207,22 @@ class customError extends Error {
}).not[toThrow](Err);
}).toThrowErrorMatchingSnapshot();
});

test('threw, but class should not match (error subclass)', () => {
expect(() => {
jestExpect(() => {
throw new SubErr('apple');
}).not[toThrow](Err);
}).toThrowErrorMatchingSnapshot();
});

test('threw, but class should not match (error subsubclass)', () => {
expect(() => {
jestExpect(() => {
throw new SubSubErr('apple');
}).not[toThrow](Err);
}).toThrowErrorMatchingSnapshot();
});
});

describe('error-message', () => {
Expand Down

0 comments on commit bf6c71a

Please sign in to comment.