Skip to content

Commit

Permalink
Improve chai support (with detailed output, to match jest exceptions) (
Browse files Browse the repository at this point in the history
…#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
  • Loading branch information
rpgeeganage authored and jeysal committed May 17, 2019
1 parent ccea646 commit 6d31fb1
Show file tree
Hide file tree
Showing 10 changed files with 263 additions and 40 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,8 @@

### Features

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

### Fixes

- `[babel-plugin-jest-hoist]` Expand list of whitelisted globals in global mocks ([#8429](https://github.com/facebook/jest/pull/8429)
Expand Down
85 changes: 85 additions & 0 deletions e2e/__tests__/__snapshots__/chaiAssertionLibrary.ts.snap
@@ -0,0 +1,85 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`chai assertion errors should display properly 1`] = `
FAIL __tests__/chai_assertion.js
● chai.js assertion library test › expect
Expected value "hello sunshine"
Received:
"hello world"
Message:
expected 'hello world' to equal 'hello sunshine'
Difference:
- Expected
+ Received
- hello sunshine
+ hello world
11 | describe('chai.js assertion library test', () => {
12 | it('expect', () => {
> 13 | chai.expect('hello world').to.equal('hello sunshine');
| ^
14 | });
15 |
16 | it('should', () => {
at Object.equal (__tests__/chai_assertion.js:13:35)
chai.js assertion library testshould
Expected value "hello world"
Received:
"hello sunshine"
Message:
expected 'hello sunshine' to equal 'hello world'
Difference:
- Expected
+ Received
- hello world
+ hello sunshine
18 | const expectedString = 'hello world';
19 | const actualString = 'hello sunshine';
> 20 | actualString.should.equal(expectedString);
| ^
21 | });
22 |
23 | it('assert', () => {
at Object.equal (__tests__/chai_assertion.js:20:25)
chai.js assertion library testassert
Expected value "hello sunshine"
Received:
"hello world"
Message:
expected 'hello world' to equal 'hello sunshine'
Difference:
- Expected
+ Received
- hello sunshine
+ hello world
22 |
23 | it('assert', () => {
> 24 | chai.assert.strictEqual('hello world', 'hello sunshine');
| ^
25 | });
26 | });
27 |
at Object.strictEqual (__tests__/chai_assertion.js:24:17)
`;
20 changes: 20 additions & 0 deletions e2e/__tests__/chaiAssertionLibrary.ts
@@ -0,0 +1,20 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import path from 'path';
import {wrap} from 'jest-snapshot-serializer-raw';
import runJest from '../runJest';
import {extractSummary, run} from '../Utils';

test('chai assertion errors should display properly', () => {
const dir = path.resolve(__dirname, '../chai-assertion-library-errors');
run('yarn', dir);

const {stderr} = runJest('chai-assertion-library-errors');
const {rest} = extractSummary(stderr);
expect(wrap(rest)).toMatchSnapshot();
});
26 changes: 26 additions & 0 deletions e2e/chai-assertion-library-errors/__tests__/chai_assertion.js
@@ -0,0 +1,26 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';
const chai = require('chai');

describe('chai.js assertion library test', () => {
it('expect', () => {
chai.expect('hello world').to.equal('hello sunshine');
});

it('should', () => {
chai.should();
const expectedString = 'hello world';
const actualString = 'hello sunshine';
actualString.should.equal(expectedString);
});

it('assert', () => {
chai.assert.strictEqual('hello world', 'hello sunshine');
});
});
9 changes: 9 additions & 0 deletions e2e/chai-assertion-library-errors/package.json
@@ -0,0 +1,9 @@
{
"jest": {
"testEnvironment": "node",
"verbose": false
},
"dependencies": {
"chai": "^4.2.0"
}
}
47 changes: 47 additions & 0 deletions e2e/chai-assertion-library-errors/yarn.lock
@@ -0,0 +1,47 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


assertion-error@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==

chai@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5"
integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==
dependencies:
assertion-error "^1.1.0"
check-error "^1.0.2"
deep-eql "^3.0.1"
get-func-name "^2.0.0"
pathval "^1.1.0"
type-detect "^4.0.5"

check-error@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=

deep-eql@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"
integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==
dependencies:
type-detect "^4.0.0"

get-func-name@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=

pathval@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0"
integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA=

type-detect@^4.0.0, type-detect@^4.0.5:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
54 changes: 36 additions & 18 deletions packages/jest-circus/src/formatNodeAssertErrors.ts
Expand Up @@ -60,7 +60,7 @@ const formatNodeAssertErrors = (event: Circus.Event, state: Circus.State) => {
} else {
error = errors;
}
return error.code === 'ERR_ASSERTION'
return isAssertionError(error)
? {message: assertionErrorMessage(error, {expand: state.expand})}
: errors;
});
Expand Down Expand Up @@ -91,22 +91,28 @@ const operatorMessage = (operator: string | undefined) => {
};

const assertThrowingMatcherHint = (operatorName: string) =>
chalk.dim('assert') +
chalk.dim('.' + operatorName + '(') +
chalk.red('function') +
chalk.dim(')');
operatorName
? chalk.dim('assert') +
chalk.dim('.' + operatorName + '(') +
chalk.red('function') +
chalk.dim(')')
: '';

const assertMatcherHint = (
operator: string | undefined | null,
operatorName: string,
) => {
let message =
chalk.dim('assert') +
chalk.dim('.' + operatorName + '(') +
chalk.red('received') +
chalk.dim(', ') +
chalk.green('expected') +
chalk.dim(')');
let message = '';

if (operatorName) {
message =
chalk.dim('assert') +
chalk.dim('.' + operatorName + '(') +
chalk.red('received') +
chalk.dim(', ') +
chalk.green('expected') +
chalk.dim(')');
}

if (operator === '==') {
message +=
Expand Down Expand Up @@ -134,8 +140,7 @@ function assertionErrorMessage(

if (operatorName === 'doesNotThrow') {
return (
assertThrowingMatcherHint(operatorName) +
'\n\n' +
buildHintString(assertThrowingMatcherHint(operatorName)) +
chalk.reset(`Expected the function not to throw an error.\n`) +
chalk.reset(`Instead, it threw:\n`) +
` ${printReceived(actual)}` +
Expand All @@ -146,8 +151,7 @@ function assertionErrorMessage(

if (operatorName === 'throws') {
return (
assertThrowingMatcherHint(operatorName) +
'\n\n' +
buildHintString(assertThrowingMatcherHint(operatorName)) +
chalk.reset(`Expected the function to throw an error.\n`) +
chalk.reset(`But it didn't throw anything.`) +
chalk.reset(hasCustomMessage ? '\n\nMessage:\n ' + message : '') +
Expand All @@ -156,8 +160,7 @@ function assertionErrorMessage(
}

return (
assertMatcherHint(operator, operatorName) +
'\n\n' +
buildHintString(assertMatcherHint(operator, operatorName)) +
chalk.reset(`Expected value ${operatorMessage(operator)}`) +
` ${printExpected(expected)}\n` +
chalk.reset(`Received:\n`) +
Expand All @@ -168,4 +171,19 @@ function assertionErrorMessage(
);
}

function isAssertionError(
error: Circus.TestError,
): error is AssertionErrorWithStack {
return (
error &&
(error instanceof AssertionError ||
error.name === AssertionError.name ||
error.code === 'ERR_ASSERTION')
);
}

function buildHintString(hint: string) {
return hint ? hint + '\n\n' : '';
}

export default formatNodeAssertErrors;
41 changes: 24 additions & 17 deletions packages/jest-jasmine2/src/assertionErrorMessage.ts
Expand Up @@ -55,19 +55,25 @@ const operatorMessage = (operator: string | null) => {
};

const assertThrowingMatcherHint = (operatorName: string) =>
chalk.dim('assert') +
chalk.dim('.' + operatorName + '(') +
chalk.red('function') +
chalk.dim(')');
operatorName
? chalk.dim('assert') +
chalk.dim('.' + operatorName + '(') +
chalk.red('function') +
chalk.dim(')')
: '';

const assertMatcherHint = (operator: string | null, operatorName: string) => {
let message =
chalk.dim('assert') +
chalk.dim('.' + operatorName + '(') +
chalk.red('received') +
chalk.dim(', ') +
chalk.green('expected') +
chalk.dim(')');
let message = '';

if (operatorName) {
message =
chalk.dim('assert') +
chalk.dim('.' + operatorName + '(') +
chalk.red('received') +
chalk.dim(', ') +
chalk.green('expected') +
chalk.dim(')');
}

if (operator === '==') {
message +=
Expand Down Expand Up @@ -95,8 +101,7 @@ function assertionErrorMessage(

if (operatorName === 'doesNotThrow') {
return (
assertThrowingMatcherHint(operatorName) +
'\n\n' +
buildHintString(assertThrowingMatcherHint(operatorName)) +
chalk.reset(`Expected the function not to throw an error.\n`) +
chalk.reset(`Instead, it threw:\n`) +
` ${printReceived(actual)}` +
Expand All @@ -107,8 +112,7 @@ function assertionErrorMessage(

if (operatorName === 'throws') {
return (
assertThrowingMatcherHint(operatorName) +
'\n\n' +
buildHintString(assertThrowingMatcherHint(operatorName)) +
chalk.reset(`Expected the function to throw an error.\n`) +
chalk.reset(`But it didn't throw anything.`) +
chalk.reset(hasCustomMessage ? '\n\nMessage:\n ' + message : '') +
Expand All @@ -117,8 +121,7 @@ function assertionErrorMessage(
}

return (
assertMatcherHint(operator, operatorName) +
'\n\n' +
buildHintString(assertMatcherHint(operator, operatorName)) +
chalk.reset(`Expected value ${operatorMessage(operator)}`) +
` ${printExpected(expected)}\n` +
chalk.reset(`Received:\n`) +
Expand All @@ -129,4 +132,8 @@ function assertionErrorMessage(
);
}

function buildHintString(hint: string) {
return hint ? hint + '\n\n' : '';
}

export default assertionErrorMessage;
5 changes: 4 additions & 1 deletion packages/jest-jasmine2/src/jasmine/Env.ts
Expand Up @@ -663,7 +663,10 @@ export default function(j$: Jasmine) {
let checkIsError;
let message;

if (error instanceof AssertionError) {
if (
error instanceof AssertionError ||
(error && error.name === AssertionError.name)
) {
checkIsError = false;
// @ts-ignore TODO Possible error: j$.Spec does not have expand property
message = assertionErrorMessage(error, {expand: j$.Spec.expand});
Expand Down

0 comments on commit 6d31fb1

Please sign in to comment.