Skip to content

Commit 5ba0cc9

Browse files
authored
fix: print underlying error when global hooks fail (#11003)
1 parent ca479ff commit 5ba0cc9

File tree

5 files changed

+70
-19
lines changed

5 files changed

+70
-19
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
- `[jest-circus]` Fixed the issue of beforeAll & afterAll hooks getting executed even if it is inside a skipped `describe` block [#10451](https://github.com/facebook/jest/issues/10451)
3232
- `[jest-circus]` Fix `testLocation` on Windows when using `test.each` ([#10871](https://github.com/facebook/jest/pull/10871))
3333
- `[jest-cli]` Use testFailureExitCode when bailing from a failed test ([#10958](https://github.com/facebook/jest/pull/10958))
34+
- `[jest-cli]` Print custom error if error thrown from global hooks is not an error already ([#11003](https://github.com/facebook/jest/pull/11003))
3435
- `[jest-config]` [**BREAKING**] Change default file extension order by moving json behind ts and tsx ([10572](https://github.com/facebook/jest/pull/10572))
3536
- `[jest-console]` `console.dir` now respects the second argument correctly ([#10638](https://github.com/facebook/jest/pull/10638))
3637
- `[jest-each]` [**BREAKING**] Ignore excess words in headings ([#8766](https://github.com/facebook/jest/pull/8766))

e2e/__tests__/globalSetup.test.ts

+36-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
import {tmpdir} from 'os';
99
import * as path from 'path';
1010
import * as fs from 'graceful-fs';
11-
import {cleanup, runYarnInstall} from '../Utils';
11+
import {
12+
cleanup,
13+
createEmptyPackage,
14+
runYarnInstall,
15+
writeFiles,
16+
} from '../Utils';
1217
import runJest, {json as runWithJson} from '../runJest';
1318

1419
const DIR = path.join(tmpdir(), 'jest-global-setup');
@@ -19,6 +24,7 @@ const customTransformDIR = path.join(
1924
'jest-global-setup-custom-transform',
2025
);
2126
const nodeModulesDIR = path.join(tmpdir(), 'jest-global-setup-node-modules');
27+
const rejectionDir = path.join(tmpdir(), 'jest-global-setup-rejection');
2228
const e2eDir = path.resolve(__dirname, '../global-setup');
2329

2430
beforeAll(() => {
@@ -31,13 +37,16 @@ beforeEach(() => {
3137
cleanup(project2DIR);
3238
cleanup(customTransformDIR);
3339
cleanup(nodeModulesDIR);
40+
cleanup(rejectionDir);
3441
});
42+
3543
afterAll(() => {
3644
cleanup(DIR);
3745
cleanup(project1DIR);
3846
cleanup(project2DIR);
3947
cleanup(customTransformDIR);
4048
cleanup(nodeModulesDIR);
49+
cleanup(rejectionDir);
4150
});
4251

4352
test('globalSetup is triggered once before all test suites', () => {
@@ -62,8 +71,9 @@ test('jest throws an error when globalSetup does not export a function', () => {
6271
]);
6372

6473
expect(exitCode).toBe(1);
65-
expect(stderr).toMatch(
66-
`TypeError: globalSetup file must export a function at ${setupPath}`,
74+
expect(stderr).toContain('Jest: Got error running globalSetup');
75+
expect(stderr).toContain(
76+
`globalSetup file must export a function at ${setupPath}`,
6777
);
6878
});
6979

@@ -145,8 +155,9 @@ test('globalSetup throws with named export', () => {
145155
]);
146156

147157
expect(exitCode).toBe(1);
148-
expect(stderr).toMatch(
149-
`TypeError: globalSetup file must export a function at ${setupPath}`,
158+
expect(stderr).toContain('Jest: Got error running globalSetup');
159+
expect(stderr).toContain(
160+
`globalSetup file must export a function at ${setupPath}`,
150161
);
151162
});
152163

@@ -161,3 +172,23 @@ test('should transform node_modules if configured by transformIgnorePatterns', (
161172

162173
expect(exitCode).toBe(0);
163174
});
175+
176+
test('properly handle rejections', () => {
177+
createEmptyPackage(rejectionDir, {jest: {globalSetup: '<rootDir>/setup.js'}});
178+
writeFiles(rejectionDir, {
179+
'setup.js': `
180+
module.exports = () => Promise.reject();
181+
`,
182+
'test.js': `
183+
test('dummy', () => {
184+
expect(true).toBe(true);
185+
});
186+
`,
187+
});
188+
189+
const {exitCode, stderr} = runJest(rejectionDir, [`--no-cache`]);
190+
191+
expect(exitCode).toBe(1);
192+
expect(stderr).toContain('Error: Jest: Got error running globalSetup');
193+
expect(stderr).toContain('reason: undefined');
194+
});

e2e/__tests__/globalTeardown.test.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@ test('jest throws an error when globalTeardown does not export a function', () =
5555
]);
5656

5757
expect(exitCode).toBe(1);
58-
expect(stderr).toMatch(
59-
`TypeError: globalTeardown file must export a function at ${teardownPath}`,
58+
expect(stderr).toContain('Jest: Got error running globalTeardown');
59+
expect(stderr).toContain(
60+
`globalTeardown file must export a function at ${teardownPath}`,
6061
);
6162
});
6263

@@ -125,7 +126,8 @@ test('globalTeardown throws with named export', () => {
125126
]);
126127

127128
expect(exitCode).toBe(1);
128-
expect(stderr).toMatch(
129-
`TypeError: globalTeardown file must export a function at ${teardownPath}`,
129+
expect(stderr).toContain('Jest: Got error running globalTeardown');
130+
expect(stderr).toContain(
131+
`globalTeardown file must export a function at ${teardownPath}`,
130132
);
131133
});

packages/jest-cli/src/cli/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export async function run(
3737
} catch (error) {
3838
clearLine(process.stderr);
3939
clearLine(process.stdout);
40-
if (error.stack) {
40+
if (error?.stack) {
4141
console.error(chalk.red(error.stack));
4242
} else {
4343
console.error(chalk.red(error));

packages/jest-core/src/runGlobalHook.ts

+26-9
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8+
import * as util from 'util';
89
import pEachSeries = require('p-each-series');
910
import {ScriptTransformer} from '@jest/transform';
1011
import type {Config} from '@jest/types';
1112
import type {Test} from 'jest-runner';
1213
import {interopRequireDefault} from 'jest-util';
14+
import prettyFormat from 'pretty-format';
1315

1416
export default async ({
1517
allTests,
@@ -29,7 +31,7 @@ export default async ({
2931
}
3032

3133
if (globalModulePaths.size > 0) {
32-
await pEachSeries(Array.from(globalModulePaths), async modulePath => {
34+
await pEachSeries(globalModulePaths, async modulePath => {
3335
if (!modulePath) {
3436
return;
3537
}
@@ -45,17 +47,32 @@ export default async ({
4547

4648
const transformer = new ScriptTransformer(projectConfig);
4749

48-
await transformer.requireAndTranspileModule(modulePath, async m => {
49-
const globalModule = interopRequireDefault(m).default;
50+
try {
51+
await transformer.requireAndTranspileModule(modulePath, async m => {
52+
const globalModule = interopRequireDefault(m).default;
5053

51-
if (typeof globalModule !== 'function') {
52-
throw new TypeError(
53-
`${moduleName} file must export a function at ${modulePath}`,
54-
);
54+
if (typeof globalModule !== 'function') {
55+
throw new TypeError(
56+
`${moduleName} file must export a function at ${modulePath}`,
57+
);
58+
}
59+
60+
await globalModule(globalConfig);
61+
});
62+
} catch (error) {
63+
if (util.types.isNativeError(error)) {
64+
error.message = `Jest: Got error running ${moduleName} - ${modulePath}, reason: ${error.message}`;
65+
66+
throw error;
5567
}
5668

57-
await globalModule(globalConfig);
58-
});
69+
throw new Error(
70+
`Jest: Got error running ${moduleName} - ${modulePath}, reason: ${prettyFormat(
71+
error,
72+
{maxDepth: 3},
73+
)}`,
74+
);
75+
}
5976
});
6077
}
6178

0 commit comments

Comments
 (0)