Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jest-config): openHandlesTimeout config option #13875

Merged
merged 13 commits into from Feb 23, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

- `[jest-changed-files]` Support Sapling ([#13941](https://github.com/facebook/jest/pull/13941))
- `[jest-cli, jest-config, @jest/core, jest-haste-map, @jest/reporters, jest-runner, jest-runtime, @jest/types]` Add `workerThreads` configuration option to allow using [worker threads](https://nodejs.org/dist/latest/docs/api/worker_threads.html) for parallelization ([#13939](https://github.com/facebook/jest/pull/13939))
- `[jest-config]` Add `openHandlesTimeout` option to configure possible open handles warning. ([#13875](https://github.com/facebook/jest/pull/13875))
- `[@jest/create-cache-key-function]` Allow passing `length` argument to `createCacheKey()` function and set its default value to `16` on Windows ([#13827](https://github.com/facebook/jest/pull/13827))
- `[jest-message-util]` Add support for [AggregateError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError) ([#13946](https://github.com/facebook/jest/pull/13946))
- `[jest-worker]` Add `start` method to worker farms ([#13937](https://github.com/facebook/jest/pull/13937))
Expand Down
4 changes: 4 additions & 0 deletions docs/CLI.md
Expand Up @@ -310,6 +310,10 @@ Activates notifications for test results. Good for when you don't want your cons

Alias: `-o`. Attempts to identify which tests to run based on which files have changed in the current repository. Only works if you're running tests in a git/hg repository at the moment and requires a static dependency graph (ie. no dynamic requires).

### `--openHandlesTimeout=<milliseconds>`

When `--detectOpenHandles` and `--forceExit` are _disabled_, Jest will print a warning if the process has not exited cleanly after this number of milliseconds. A value of `0` disables the warning. Defaults to `1000`.

### `--outputFile=<filename>`

Write test results to a file when the `--json` option is also specified. The returned JSON structure is documented in [testResultsProcessor](Configuration.md#testresultsprocessor-string).
Expand Down
6 changes: 6 additions & 0 deletions docs/Configuration.md
Expand Up @@ -1078,6 +1078,12 @@ Specifies notification mode. Requires `notify: true`.
- `success-change`: send a notification when tests pass or once when it fails.
- `failure-change`: send a notification when tests fail or once when it passes.

### `openHandlesTimeout` \[number]

Default: `1000`

Print a warning indicating that there are probable open handles if Jest does not exit cleanly this number of milliseconds after it completes. Use `0` to disable the warning.

### `preset` \[string]

Default: `undefined`
Expand Down
8 changes: 7 additions & 1 deletion e2e/__tests__/__snapshots__/detectOpenHandles.ts.snap
Expand Up @@ -7,7 +7,13 @@ exports[`prints message about flag on forceExit 1`] = `"Force exiting Jest: Have
exports[`prints message about flag on slow tests 1`] = `
"Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with \`--detectOpenHandles\` to troubleshoot this issue."
'This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with \`--detectOpenHandles\` to troubleshoot this issue."
`;

exports[`prints message about flag on slow tests with a custom timeout 1`] = `
"Jest did not exit 0.5 seconds after the test run has completed.
'This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with \`--detectOpenHandles\` to troubleshoot this issue."
`;

exports[`prints out info about open handlers 1`] = `
Expand Down
2 changes: 2 additions & 0 deletions e2e/__tests__/__snapshots__/showConfig.test.ts.snap
Expand Up @@ -44,6 +44,7 @@ exports[`--showConfig outputs config info and exits 1`] = `
],
"moduleNameMapper": [],
"modulePathIgnorePatterns": [],
"openHandlesTimeout": 1000,
"prettierPath": "prettier",
"resetMocks": false,
"resetModules": false,
Expand Down Expand Up @@ -121,6 +122,7 @@ exports[`--showConfig outputs config info and exits 1`] = `
"notifyMode": "failure-change",
"onlyChanged": false,
"onlyFailures": false,
"openHandlesTimeout": 1000,
"passWithNoTests": false,
"projects": [],
"rootDir": "<<REPLACED_ROOT_DIR>>",
Expand Down
14 changes: 14 additions & 0 deletions e2e/__tests__/detectOpenHandles.ts
Expand Up @@ -29,6 +29,20 @@ it('prints message about flag on slow tests', async () => {
expect(textAfterTest).toMatchSnapshot();
});

it('prints message about flag on slow tests with a custom timeout', async () => {
const run = runContinuous('detect-open-handles', [
'outside',
'--openHandlesTimeout=500',
]);
await run.waitUntil(({stderr}) =>
stderr.includes('Jest did not exit 0.5 seconds'),
);
const {stderr} = await run.end();
const textAfterTest = getTextAfterTest(stderr);

expect(textAfterTest).toMatchSnapshot();
});

it('prints message about flag on forceExit', async () => {
const run = runContinuous('detect-open-handles', ['outside', '--forceExit']);
await run.waitUntil(({stderr}) => stderr.includes('Force exiting Jest'));
Expand Down
6 changes: 6 additions & 0 deletions packages/jest-cli/src/args.ts
Expand Up @@ -423,6 +423,12 @@ export const options: {[key: string]: Options} = {
description: 'Run tests that failed in the previous execution.',
type: 'boolean',
},
openHandlesTimeout: {
description:
'Print a warning about probable open handles if Jest does not exit ' +
'cleanly after this number of milliseconds. `0` to disable.',
type: 'number',
},
outputFile: {
description:
'Write test results to a file when the --json option is ' +
Expand Down
12 changes: 9 additions & 3 deletions packages/jest-cli/src/run.ts
Expand Up @@ -132,18 +132,24 @@ const readResultsAndExit = (
}

exit(code);
} else if (!globalConfig.detectOpenHandles) {
} else if (
!globalConfig.detectOpenHandles &&
globalConfig.openHandlesTimeout !== 0
) {
const timeout = globalConfig.openHandlesTimeout;
setTimeout(() => {
console.warn(
chalk.yellow.bold(
'Jest did not exit one second after the test run has completed.\n\n',
`Jest did not exit ${
timeout === 1000 ? 'one second' : `${timeout / 1000} seconds`
} after the test run has completed.\n\n'`,
) +
chalk.yellow(
'This usually means that there are asynchronous operations that ' +
"weren't stopped in your tests. Consider running Jest with " +
'`--detectOpenHandles` to troubleshoot this issue.',
),
);
}, 1000).unref();
}, timeout).unref();
}
};
1 change: 1 addition & 0 deletions packages/jest-config/src/Defaults.ts
Expand Up @@ -60,6 +60,7 @@ const defaultOptions: Config.DefaultOptions = {
noStackTrace: false,
notify: false,
notifyMode: 'failure-change',
openHandlesTimeout: 1000,
passWithNoTests: false,
prettierPath: 'prettier',
resetMocks: false,
Expand Down
2 changes: 2 additions & 0 deletions packages/jest-config/src/ValidConfig.ts
Expand Up @@ -116,6 +116,7 @@ export const initialOptions: Config.InitialOptions = {
notifyMode: 'failure-change',
onlyChanged: false,
onlyFailures: false,
openHandlesTimeout: 1000,
passWithNoTests: false,
preset: 'react-native',
prettierPath: '<rootDir>/node_modules/prettier',
Expand Down Expand Up @@ -262,6 +263,7 @@ export const initialProjectOptions: Config.InitialProjectOptions = {
},
modulePathIgnorePatterns: ['<rootDir>/build/'],
modulePaths: ['/shared/vendor/modules'],
openHandlesTimeout: 1000,
preset: 'react-native',
prettierPath: '<rootDir>/node_modules/prettier',
resetMocks: false,
Expand Down
2 changes: 2 additions & 0 deletions packages/jest-config/src/index.ts
Expand Up @@ -113,6 +113,7 @@ const groupOptions = (
notifyMode: options.notifyMode,
onlyChanged: options.onlyChanged,
onlyFailures: options.onlyFailures,
openHandlesTimeout: options.openHandlesTimeout,
outputFile: options.outputFile,
passWithNoTests: options.passWithNoTests,
projects: options.projects,
Expand Down Expand Up @@ -169,6 +170,7 @@ const groupOptions = (
moduleNameMapper: options.moduleNameMapper,
modulePathIgnorePatterns: options.modulePathIgnorePatterns,
modulePaths: options.modulePaths,
openHandlesTimeout: options.openHandlesTimeout,
prettierPath: options.prettierPath,
resetMocks: options.resetMocks,
resetModules: options.resetModules,
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/normalize.ts
Expand Up @@ -912,6 +912,7 @@ export default async function normalize(
case 'notifyMode':
case 'onlyChanged':
case 'onlyFailures':
case 'openHandlesTimeout':
case 'outputFile':
case 'passWithNoTests':
case 'replname':
Expand Down
Expand Up @@ -28,6 +28,7 @@ exports[`prints the config object 1`] = `
"moduleNameMapper": [],
"modulePathIgnorePatterns": [],
"modulePaths": [],
"openHandlesTimeout": 1000,
"prettierPath": "prettier",
"resetMocks": false,
"resetModules": false,
Expand Down Expand Up @@ -90,6 +91,7 @@ exports[`prints the config object 1`] = `
"notifyMode": "failure-change",
"onlyChanged": false,
"onlyFailures": false,
"openHandlesTimeout": 1000,
"passWithNoTests": false,
"projects": [],
"reporters": [],
Expand Down