Skip to content

Commit 2f0dec5

Browse files
niieanicpojer
authored andcommitted
Prevent a ENOENT crash by checking for file existence in jest-message-util (#5405)
* Prevent a ENOENT crash by checking for existence Jest tries to load the stack-trace based on the top frame. The problem is, the filename may be provided by the source-map, which is unreliable since it can point to a non-existent file. In such a case, the code would cause a hard crash of "Test suite failed to run" with a stack-trace pointing at "exports.formatStackTrace", which I have corrected here. * add an integration test for bad-source-map case * lint CHANGELOG * use a try-catch rather than fs.existsSync
1 parent 0e55c7c commit 2f0dec5

6 files changed

Lines changed: 65 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
## master
22

3-
### features
3+
### Features
44

55
* `[jest-mock]` Add util methods to create async functions.
66
([#5318](https://github.com/facebook/jest/pull/5318))
77

88
### Fixes
99

10+
* `[jest-message-util]` Prevent an `ENOENT` crash when the test file contained a
11+
malformed source-map. ([#5405](https://github.com/facebook/jest/pull/5405)).
1012
* `[jest]` Add `import-local` to `jest` package.
1113
([#5353](https://github.com/facebook/jest/pull/5353))
1214
* `[expect]` Support class instances in `.toHaveProperty()` matcher.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc. 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+
* @flow
8+
*/
9+
'use strict';
10+
11+
const runJest = require('../runJest');
12+
13+
test('suite with test cases that contain malformed sourcemaps', () => {
14+
const result = runJest('bad-source-map');
15+
expect(result.stderr).not.toMatch('ENOENT');
16+
});

integration-tests/bad-source-map/__tests__/bad-source-map.js

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

integration-tests/bad-source-map/__tests__/bad-source-map.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"jest": {
3+
"testEnvironment": "node"
4+
}
5+
}

packages/jest-message-util/src/index.js

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,22 @@ const trim = string => (string || '').replace(/^\s+/, '').replace(/\s+$/, '');
7070
const trimPaths = string =>
7171
string.match(STACK_PATH_REGEXP) ? trim(string) : string;
7272

73+
const getRenderedCallsite = (fileContent: string, line: number) => {
74+
let renderedCallsite = codeFrameColumns(
75+
fileContent,
76+
{start: {line}},
77+
{highlightCode: true},
78+
);
79+
80+
renderedCallsite = renderedCallsite
81+
.split('\n')
82+
.map(line => MESSAGE_INDENT + line)
83+
.join('\n');
84+
85+
renderedCallsite = `\n${renderedCallsite}\n`;
86+
return renderedCallsite;
87+
};
88+
7389
// ExecError is an error thrown outside of the test suite (not inside an `it` or
7490
// `before/after each` hooks). If it's thrown, none of the tests in the file
7591
// are executed.
@@ -228,20 +244,15 @@ export const formatStackTrace = (
228244
const filename = topFrame.file;
229245

230246
if (path.isAbsolute(filename)) {
231-
renderedCallsite = codeFrameColumns(
232-
fs.readFileSync(filename, 'utf8'),
233-
{
234-
start: {line: topFrame.line},
235-
},
236-
{highlightCode: true},
237-
);
238-
239-
renderedCallsite = renderedCallsite
240-
.split('\n')
241-
.map(line => MESSAGE_INDENT + line)
242-
.join('\n');
243-
244-
renderedCallsite = `\n${renderedCallsite}\n`;
247+
let fileContent;
248+
try {
249+
// TODO: check & read HasteFS instead of reading the filesystem:
250+
// see: https://github.com/facebook/jest/pull/5405#discussion_r164281696
251+
fileContent = fs.readFileSync(filename, 'utf8');
252+
renderedCallsite = getRenderedCallsite(fileContent, topFrame.line);
253+
} catch (e) {
254+
// the file does not exist or is inaccessible, we ignore
255+
}
245256
}
246257
}
247258

0 commit comments

Comments
 (0)