Skip to content

Commit 6d31fb1

Browse files
rpgeeganagejeysal
authored andcommitted
Improve chai support (with detailed output, to match jest exceptions) (#8454)
* added fix to improve chai output * fix linting issue * update the change log * fix changelog lint * added extra check to see error is not undefined * fixed the comments * fix tsc issue * fix the incorrect test case * remove assert.(a,b) message if operator not set * remove assert.(a,b) message if operator not set * fix assert message * fix linting * removed unwanted empty string in final message
1 parent ccea646 commit 6d31fb1

10 files changed

Lines changed: 263 additions & 40 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
### Features
44

5+
- `[jest-cli]` Improve chai support (with detailed output, to match jest exceptions) ([#8454](https://github.com/facebook/jest/pull/8454))
6+
57
### Fixes
68

79
- `[babel-plugin-jest-hoist]` Expand list of whitelisted globals in global mocks ([#8429](https://github.com/facebook/jest/pull/8429)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`chai assertion errors should display properly 1`] = `
4+
FAIL __tests__/chai_assertion.js
5+
● chai.js assertion library test › expect
6+
7+
Expected value "hello sunshine"
8+
Received:
9+
"hello world"
10+
11+
Message:
12+
expected 'hello world' to equal 'hello sunshine'
13+
14+
Difference:
15+
16+
- Expected
17+
+ Received
18+
19+
- hello sunshine
20+
+ hello world
21+
22+
11 | describe('chai.js assertion library test', () => {
23+
12 | it('expect', () => {
24+
> 13 | chai.expect('hello world').to.equal('hello sunshine');
25+
| ^
26+
14 | });
27+
15 |
28+
16 | it('should', () => {
29+
30+
at Object.equal (__tests__/chai_assertion.js:13:35)
31+
32+
chai.js assertion library testshould
33+
34+
Expected value "hello world"
35+
Received:
36+
"hello sunshine"
37+
38+
Message:
39+
expected 'hello sunshine' to equal 'hello world'
40+
41+
Difference:
42+
43+
- Expected
44+
+ Received
45+
46+
- hello world
47+
+ hello sunshine
48+
49+
18 | const expectedString = 'hello world';
50+
19 | const actualString = 'hello sunshine';
51+
> 20 | actualString.should.equal(expectedString);
52+
| ^
53+
21 | });
54+
22 |
55+
23 | it('assert', () => {
56+
57+
at Object.equal (__tests__/chai_assertion.js:20:25)
58+
59+
chai.js assertion library testassert
60+
61+
Expected value "hello sunshine"
62+
Received:
63+
"hello world"
64+
65+
Message:
66+
expected 'hello world' to equal 'hello sunshine'
67+
68+
Difference:
69+
70+
- Expected
71+
+ Received
72+
73+
- hello sunshine
74+
+ hello world
75+
76+
22 |
77+
23 | it('assert', () => {
78+
> 24 | chai.assert.strictEqual('hello world', 'hello sunshine');
79+
| ^
80+
25 | });
81+
26 | });
82+
27 |
83+
84+
at Object.strictEqual (__tests__/chai_assertion.js:24:17)
85+
`;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import path from 'path';
9+
import {wrap} from 'jest-snapshot-serializer-raw';
10+
import runJest from '../runJest';
11+
import {extractSummary, run} from '../Utils';
12+
13+
test('chai assertion errors should display properly', () => {
14+
const dir = path.resolve(__dirname, '../chai-assertion-library-errors');
15+
run('yarn', dir);
16+
17+
const {stderr} = runJest('chai-assertion-library-errors');
18+
const {rest} = extractSummary(stderr);
19+
expect(wrap(rest)).toMatchSnapshot();
20+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
'use strict';
9+
const chai = require('chai');
10+
11+
describe('chai.js assertion library test', () => {
12+
it('expect', () => {
13+
chai.expect('hello world').to.equal('hello sunshine');
14+
});
15+
16+
it('should', () => {
17+
chai.should();
18+
const expectedString = 'hello world';
19+
const actualString = 'hello sunshine';
20+
actualString.should.equal(expectedString);
21+
});
22+
23+
it('assert', () => {
24+
chai.assert.strictEqual('hello world', 'hello sunshine');
25+
});
26+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"jest": {
3+
"testEnvironment": "node",
4+
"verbose": false
5+
},
6+
"dependencies": {
7+
"chai": "^4.2.0"
8+
}
9+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2+
# yarn lockfile v1
3+
4+
5+
assertion-error@^1.1.0:
6+
version "1.1.0"
7+
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
8+
integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==
9+
10+
chai@^4.2.0:
11+
version "4.2.0"
12+
resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5"
13+
integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==
14+
dependencies:
15+
assertion-error "^1.1.0"
16+
check-error "^1.0.2"
17+
deep-eql "^3.0.1"
18+
get-func-name "^2.0.0"
19+
pathval "^1.1.0"
20+
type-detect "^4.0.5"
21+
22+
check-error@^1.0.2:
23+
version "1.0.2"
24+
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
25+
integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=
26+
27+
deep-eql@^3.0.1:
28+
version "3.0.1"
29+
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"
30+
integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==
31+
dependencies:
32+
type-detect "^4.0.0"
33+
34+
get-func-name@^2.0.0:
35+
version "2.0.0"
36+
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
37+
integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=
38+
39+
pathval@^1.1.0:
40+
version "1.1.0"
41+
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0"
42+
integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA=
43+
44+
type-detect@^4.0.0, type-detect@^4.0.5:
45+
version "4.0.8"
46+
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
47+
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==

packages/jest-circus/src/formatNodeAssertErrors.ts

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ const formatNodeAssertErrors = (event: Circus.Event, state: Circus.State) => {
6060
} else {
6161
error = errors;
6262
}
63-
return error.code === 'ERR_ASSERTION'
63+
return isAssertionError(error)
6464
? {message: assertionErrorMessage(error, {expand: state.expand})}
6565
: errors;
6666
});
@@ -91,22 +91,28 @@ const operatorMessage = (operator: string | undefined) => {
9191
};
9292

9393
const assertThrowingMatcherHint = (operatorName: string) =>
94-
chalk.dim('assert') +
95-
chalk.dim('.' + operatorName + '(') +
96-
chalk.red('function') +
97-
chalk.dim(')');
94+
operatorName
95+
? chalk.dim('assert') +
96+
chalk.dim('.' + operatorName + '(') +
97+
chalk.red('function') +
98+
chalk.dim(')')
99+
: '';
98100

99101
const assertMatcherHint = (
100102
operator: string | undefined | null,
101103
operatorName: string,
102104
) => {
103-
let message =
104-
chalk.dim('assert') +
105-
chalk.dim('.' + operatorName + '(') +
106-
chalk.red('received') +
107-
chalk.dim(', ') +
108-
chalk.green('expected') +
109-
chalk.dim(')');
105+
let message = '';
106+
107+
if (operatorName) {
108+
message =
109+
chalk.dim('assert') +
110+
chalk.dim('.' + operatorName + '(') +
111+
chalk.red('received') +
112+
chalk.dim(', ') +
113+
chalk.green('expected') +
114+
chalk.dim(')');
115+
}
110116

111117
if (operator === '==') {
112118
message +=
@@ -134,8 +140,7 @@ function assertionErrorMessage(
134140

135141
if (operatorName === 'doesNotThrow') {
136142
return (
137-
assertThrowingMatcherHint(operatorName) +
138-
'\n\n' +
143+
buildHintString(assertThrowingMatcherHint(operatorName)) +
139144
chalk.reset(`Expected the function not to throw an error.\n`) +
140145
chalk.reset(`Instead, it threw:\n`) +
141146
` ${printReceived(actual)}` +
@@ -146,8 +151,7 @@ function assertionErrorMessage(
146151

147152
if (operatorName === 'throws') {
148153
return (
149-
assertThrowingMatcherHint(operatorName) +
150-
'\n\n' +
154+
buildHintString(assertThrowingMatcherHint(operatorName)) +
151155
chalk.reset(`Expected the function to throw an error.\n`) +
152156
chalk.reset(`But it didn't throw anything.`) +
153157
chalk.reset(hasCustomMessage ? '\n\nMessage:\n ' + message : '') +
@@ -156,8 +160,7 @@ function assertionErrorMessage(
156160
}
157161

158162
return (
159-
assertMatcherHint(operator, operatorName) +
160-
'\n\n' +
163+
buildHintString(assertMatcherHint(operator, operatorName)) +
161164
chalk.reset(`Expected value ${operatorMessage(operator)}`) +
162165
` ${printExpected(expected)}\n` +
163166
chalk.reset(`Received:\n`) +
@@ -168,4 +171,19 @@ function assertionErrorMessage(
168171
);
169172
}
170173

174+
function isAssertionError(
175+
error: Circus.TestError,
176+
): error is AssertionErrorWithStack {
177+
return (
178+
error &&
179+
(error instanceof AssertionError ||
180+
error.name === AssertionError.name ||
181+
error.code === 'ERR_ASSERTION')
182+
);
183+
}
184+
185+
function buildHintString(hint: string) {
186+
return hint ? hint + '\n\n' : '';
187+
}
188+
171189
export default formatNodeAssertErrors;

packages/jest-jasmine2/src/assertionErrorMessage.ts

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -55,19 +55,25 @@ const operatorMessage = (operator: string | null) => {
5555
};
5656

5757
const assertThrowingMatcherHint = (operatorName: string) =>
58-
chalk.dim('assert') +
59-
chalk.dim('.' + operatorName + '(') +
60-
chalk.red('function') +
61-
chalk.dim(')');
58+
operatorName
59+
? chalk.dim('assert') +
60+
chalk.dim('.' + operatorName + '(') +
61+
chalk.red('function') +
62+
chalk.dim(')')
63+
: '';
6264

6365
const assertMatcherHint = (operator: string | null, operatorName: string) => {
64-
let message =
65-
chalk.dim('assert') +
66-
chalk.dim('.' + operatorName + '(') +
67-
chalk.red('received') +
68-
chalk.dim(', ') +
69-
chalk.green('expected') +
70-
chalk.dim(')');
66+
let message = '';
67+
68+
if (operatorName) {
69+
message =
70+
chalk.dim('assert') +
71+
chalk.dim('.' + operatorName + '(') +
72+
chalk.red('received') +
73+
chalk.dim(', ') +
74+
chalk.green('expected') +
75+
chalk.dim(')');
76+
}
7177

7278
if (operator === '==') {
7379
message +=
@@ -95,8 +101,7 @@ function assertionErrorMessage(
95101

96102
if (operatorName === 'doesNotThrow') {
97103
return (
98-
assertThrowingMatcherHint(operatorName) +
99-
'\n\n' +
104+
buildHintString(assertThrowingMatcherHint(operatorName)) +
100105
chalk.reset(`Expected the function not to throw an error.\n`) +
101106
chalk.reset(`Instead, it threw:\n`) +
102107
` ${printReceived(actual)}` +
@@ -107,8 +112,7 @@ function assertionErrorMessage(
107112

108113
if (operatorName === 'throws') {
109114
return (
110-
assertThrowingMatcherHint(operatorName) +
111-
'\n\n' +
115+
buildHintString(assertThrowingMatcherHint(operatorName)) +
112116
chalk.reset(`Expected the function to throw an error.\n`) +
113117
chalk.reset(`But it didn't throw anything.`) +
114118
chalk.reset(hasCustomMessage ? '\n\nMessage:\n ' + message : '') +
@@ -117,8 +121,7 @@ function assertionErrorMessage(
117121
}
118122

119123
return (
120-
assertMatcherHint(operator, operatorName) +
121-
'\n\n' +
124+
buildHintString(assertMatcherHint(operator, operatorName)) +
122125
chalk.reset(`Expected value ${operatorMessage(operator)}`) +
123126
` ${printExpected(expected)}\n` +
124127
chalk.reset(`Received:\n`) +
@@ -129,4 +132,8 @@ function assertionErrorMessage(
129132
);
130133
}
131134

135+
function buildHintString(hint: string) {
136+
return hint ? hint + '\n\n' : '';
137+
}
138+
132139
export default assertionErrorMessage;

packages/jest-jasmine2/src/jasmine/Env.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,10 @@ export default function(j$: Jasmine) {
663663
let checkIsError;
664664
let message;
665665

666-
if (error instanceof AssertionError) {
666+
if (
667+
error instanceof AssertionError ||
668+
(error && error.name === AssertionError.name)
669+
) {
667670
checkIsError = false;
668671
// @ts-ignore TODO Possible error: j$.Spec does not have expand property
669672
message = assertionErrorMessage(error, {expand: j$.Spec.expand});

0 commit comments

Comments
 (0)