Skip to content

Commit

Permalink
feat(jest-message-util): render codeframe on failure (#5087)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB authored and cpojer committed Dec 15, 2017
1 parent 6f7c85a commit 78477eb
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@

### Features

* `[jest-message-util]` Add codeframe to test assertion failures
([#5087](https://github.com/facebook/jest/pull/5087))
* `[jest-config]` Add Global Setup/Teardown options
([#4716](https://github.com/facebook/jest/pull/4716))
* `[jest-config]` Add `testEnvironmentOptions` to apply to jsdom options or node
Expand Down
8 changes: 6 additions & 2 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,14 +262,18 @@ here, and some code mutates that value in the midst of running a test, that
mutation will _not_ be persisted across test runs for other test files.

### `globalSetup` [string]

Default: `undefined`

This option allows the use of a custom global setup module which exports an async function that is triggered once before all test suites.
This option allows the use of a custom global setup module which exports an
async function that is triggered once before all test suites.

### `globalTeardown` [string]

Default: `undefined`

This option allows the use of a custom global teardown module which exports an async function that is triggered once after all test suites.
This option allows the use of a custom global teardown module which exports an
async function that is triggered once after all test suites.

### `mapCoverage` [boolean]

Expand Down
10 changes: 5 additions & 5 deletions docs/TimerMocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ test('waits 1 second before ending the game', () => {
const timerGame = require('../timerGame');
timerGame();

expect(setTimeout).toHaveBeenCalledTimes(1)
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
});
```
Expand Down Expand Up @@ -63,7 +63,7 @@ test('calls the callback after 1 second', () => {

// Now our callback should have been called!
expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1)
expect(callback).toHaveBeenCalledTimes(1);
});
```

Expand Down Expand Up @@ -110,7 +110,7 @@ describe('infiniteTimerGame', () => {

// At this point in time, there should have been a single call to
// setTimeout to schedule the end of the game in 1 second.
expect(setTimeout).toHaveBeenCalledTimes(1)
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);

// Fast forward and exhaust only currently pending timers
Expand All @@ -122,7 +122,7 @@ describe('infiniteTimerGame', () => {

// And it should have created a new timer to start the game over in
// 10 seconds
expect(setTimeout).toHaveBeenCalledTimes(2)
expect(setTimeout).toHaveBeenCalledTimes(2);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000);
});
});
Expand Down Expand Up @@ -170,7 +170,7 @@ it('calls the callback after 1 second via advanceTimersByTime', () => {

// Now our callback should have been called!
expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1)
expect(callback).toHaveBeenCalledTimes(1);
});
```

Expand Down
116 changes: 116 additions & 0 deletions integration_tests/__tests__/__snapshots__/failures.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ exports[`not throwing Error objects 4`] = `
expect(received).toBeTruthy()
Expected value to be truthy, instead received
false
11 | const throws = () => {
12 | expect.assertions(2);
> 13 | expect(false).toBeTruthy();
14 | };
15 | const redeclare = () => {
16 | expect.assertions(1);
at __tests__/assertion_count.test.js:13:17
● .assertions() › throws
expect.assertions(2)
Expand All @@ -48,6 +54,12 @@ exports[`not throwing Error objects 4`] = `
expect(received).toBeTruthy()
Expected value to be truthy, instead received
false
15 | const redeclare = () => {
16 | expect.assertions(1);
> 17 | expect(false).toBeTruthy();
18 | expect.assertions(2);
19 | };
20 |
at __tests__/assertion_count.test.js:17:17
● .assertions() › throws on assertion
expect.assertions(0)
Expand Down Expand Up @@ -85,6 +97,13 @@ exports[`works with node assert 1`] = `
true
Received:
false
13 |
14 | test('assert', () => {
> 15 | assert(false);
16 | });
17 |
18 | test('assert with a message', () => {
at __tests__/node_assertion_error.test.js:15:3
Expand All @@ -99,6 +118,13 @@ exports[`works with node assert 1`] = `
Message:
this is a message
17 |
18 | test('assert with a message', () => {
> 19 | assert(false, 'this is a message');
20 | });
21 |
22 | test('assert.ok', () => {
at __tests__/node_assertion_error.test.js:19:3
Expand All @@ -110,6 +136,13 @@ exports[`works with node assert 1`] = `
true
Received:
false
21 |
22 | test('assert.ok', () => {
> 23 | assert.ok(false);
24 | });
25 |
26 | test('assert.ok with a message', () => {
at __tests__/node_assertion_error.test.js:23:10
Expand All @@ -124,6 +157,13 @@ exports[`works with node assert 1`] = `
Message:
this is a message
25 |
26 | test('assert.ok with a message', () => {
> 27 | assert.ok(false, 'this is a message');
28 | });
29 |
30 | test('assert.equal', () => {
at __tests__/node_assertion_error.test.js:27:10
Expand All @@ -135,6 +175,13 @@ exports[`works with node assert 1`] = `
2
Received:
1
29 |
30 | test('assert.equal', () => {
> 31 | assert.equal(1, 2);
32 | });
33 |
34 | test('assert.notEqual', () => {
at __tests__/node_assertion_error.test.js:31:10
Expand All @@ -150,6 +197,13 @@ exports[`works with node assert 1`] = `
Difference:
Compared values have no visual difference.
33 |
34 | test('assert.notEqual', () => {
> 35 | assert.notEqual(1, 1);
36 | });
37 |
38 | test('assert.deepEqual', () => {
at __tests__/node_assertion_error.test.js:35:10
Expand All @@ -175,6 +229,13 @@ exports[`works with node assert 1`] = `
},
},
}
37 |
38 | test('assert.deepEqual', () => {
> 39 | assert.deepEqual({a: {b: {c: 5}}}, {a: {b: {c: 6}}});
40 | });
41 |
42 | test('assert.deepEqual with a message', () => {
at __tests__/node_assertion_error.test.js:39:10
Expand Down Expand Up @@ -203,6 +264,13 @@ exports[`works with node assert 1`] = `
},
},
}
41 |
42 | test('assert.deepEqual with a message', () => {
> 43 | assert.deepEqual({a: {b: {c: 5}}}, {a: {b: {c: 7}}}, 'this is a message');
44 | });
45 |
46 | test('assert.notDeepEqual', () => {
at __tests__/node_assertion_error.test.js:43:10
Expand All @@ -218,6 +286,13 @@ exports[`works with node assert 1`] = `
Difference:
Compared values have no visual difference.
45 |
46 | test('assert.notDeepEqual', () => {
> 47 | assert.notDeepEqual({a: 1}, {a: 1});
48 | });
49 |
50 | test('assert.strictEqual', () => {
at __tests__/node_assertion_error.test.js:47:10
Expand All @@ -229,6 +304,13 @@ exports[`works with node assert 1`] = `
NaN
Received:
1
49 |
50 | test('assert.strictEqual', () => {
> 51 | assert.strictEqual(1, NaN);
52 | });
53 |
54 | test('assert.notStrictEqual', () => {
at __tests__/node_assertion_error.test.js:51:10
Expand All @@ -247,6 +329,13 @@ exports[`works with node assert 1`] = `
Difference:
Compared values have no visual difference.
53 |
54 | test('assert.notStrictEqual', () => {
> 55 | assert.notStrictEqual(1, 1, 'My custom error message');
56 | });
57 |
58 | test('assert.deepStrictEqual', () => {
at __tests__/node_assertion_error.test.js:55:10
Expand All @@ -268,6 +357,13 @@ exports[`works with node assert 1`] = `
- \\"a\\": 2,
+ \\"a\\": 1,
}
57 |
58 | test('assert.deepStrictEqual', () => {
> 59 | assert.deepStrictEqual({a: 1}, {a: 2});
60 | });
61 |
62 | test('assert.notDeepStrictEqual', () => {
at __tests__/node_assertion_error.test.js:59:10
Expand All @@ -283,6 +379,13 @@ exports[`works with node assert 1`] = `
Difference:
Compared values have no visual difference.
61 |
62 | test('assert.notDeepStrictEqual', () => {
> 63 | assert.notDeepStrictEqual({a: 1}, {a: 1});
64 | });
65 |
66 | test('assert.ifError', () => {
at __tests__/node_assertion_error.test.js:63:10
Expand All @@ -301,6 +404,13 @@ exports[`works with node assert 1`] = `
Message:
Got unwanted exception.
69 |
70 | test('assert.doesNotThrow', () => {
> 71 | assert.doesNotThrow(() => {
72 | throw Error('err!');
73 | });
74 | });
at __tests__/node_assertion_error.test.js:71:10
Expand All @@ -313,6 +423,12 @@ exports[`works with node assert 1`] = `
Message:
Missing expected exception.
75 |
76 | test('assert.throws', () => {
> 77 | assert.throws(() => {});
78 | });
79 |
at __tests__/node_assertion_error.test.js:77:10
Expand Down
7 changes: 7 additions & 0 deletions integration_tests/__tests__/failures.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ test('works with node assert', () => {
Got unwanted exception.
err!
err!
69 |
70 | test('assert.doesNotThrow', () => {
> 71 | assert.doesNotThrow(() => {
72 | throw Error('err!');
73 | });
74 | });
at __tests__/node_assertion_error.test.js:71:10
`);
Expand Down
1 change: 1 addition & 0 deletions packages/jest-message-util/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"license": "MIT",
"main": "build/index.js",
"dependencies": {
"@babel/code-frame": "^7.0.0-beta.35",
"chalk": "^2.0.1",
"micromatch": "^2.3.11",
"slash": "^1.0.0",
Expand Down
34 changes: 33 additions & 1 deletion packages/jest-message-util/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
import type {Glob, Path} from 'types/Config';
import type {AssertionResult, TestResult} from 'types/TestResult';

import fs from 'fs';
import path from 'path';
import chalk from 'chalk';
import micromatch from 'micromatch';
import slash from 'slash';
import {codeFrameColumns} from '@babel/code-frame';
import StackUtils from 'stack-utils';

let nodeInternals = [];
Expand Down Expand Up @@ -194,15 +196,45 @@ export const formatStackTrace = (
testPath: ?Path,
) => {
let lines = stack.split(/\n/);
let renderedCallsite = '';
const relativeTestPath = testPath
? slash(path.relative(config.rootDir, testPath))
: null;
lines = removeInternalStackEntries(lines, options);
return lines

if (testPath) {
const topFrame = lines
.join('\n')
.trim()
.split('\n')[0];

const parsedFrame = StackUtils.parseLine(topFrame);

if (parsedFrame) {
renderedCallsite = codeFrameColumns(
fs.readFileSync(testPath, 'utf8'),
{
start: {line: parsedFrame.line},
},
{highlightCode: true},
);

renderedCallsite = renderedCallsite
.split('\n')
.map(line => MESSAGE_INDENT + line)
.join('\n');

renderedCallsite = `\n${renderedCallsite}\n`;
}
}

const stacktrace = lines
.map(trimPaths)
.map(formatPaths.bind(null, config, options, relativeTestPath))
.map(line => STACK_INDENT + line)
.join('\n');

return renderedCallsite + stacktrace;
};

export const formatResultsErrors = (
Expand Down
Loading

0 comments on commit 78477eb

Please sign in to comment.