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 @@ -2,6 +2,7 @@

### Features

- `[jest-config]` Add `openHandlesTimeout` option to configure possible open handles warning. ([#13875](https://github.com/facebook/jest/pull/13875))
- `[jest-message-util]` Add support for [error causes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause)

### Fixes
Expand Down
4 changes: 4 additions & 0 deletions docs/CLI.md
Expand Up @@ -306,6 +306,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
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
11 changes: 11 additions & 0 deletions e2e/__tests__/detectOpenHandles.ts
Expand Up @@ -29,6 +29,17 @@ 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
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 @@ -114,6 +114,7 @@ export const initialOptions: Config.InitialOptions = {
noStackTrace: false,
notify: false,
notifyMode: 'failure-change',
openHandlesTimeout: 1000,
onlyChanged: false,
onlyFailures: false,
passWithNoTests: false,
Expand Down Expand Up @@ -261,6 +262,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 @@ -168,6 +169,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
4 changes: 4 additions & 0 deletions packages/jest-types/src/Config.ts
Expand Up @@ -178,6 +178,7 @@ export type DefaultOptions = {
noStackTrace: boolean;
notify: boolean;
notifyMode: NotifyMode;
openHandlesTimeout: number;
passWithNoTests: boolean;
prettierPath: string;
resetMocks: boolean;
Expand Down Expand Up @@ -273,6 +274,7 @@ export type InitialOptions = Partial<{
notifyMode: string;
onlyChanged: boolean;
onlyFailures: boolean;
openHandlesTimeout: number;
robhogan marked this conversation as resolved.
Show resolved Hide resolved
outputFile: string;
passWithNoTests: boolean;
preset: string | null | undefined;
Expand Down Expand Up @@ -389,6 +391,7 @@ export type GlobalConfig = {
outputFile?: string;
onlyChanged: boolean;
onlyFailures: boolean;
openHandlesTimeout: number;
passWithNoTests: boolean;
projects: Array<string>;
replname?: string;
Expand Down Expand Up @@ -448,6 +451,7 @@ export type ProjectConfig = {
moduleNameMapper: Array<[string, string]>;
modulePathIgnorePatterns: Array<string>;
modulePaths?: Array<string>;
openHandlesTimeout: number;
preset?: string;
prettierPath: string;
resetMocks: boolean;
Expand Down
2 changes: 2 additions & 0 deletions packages/test-utils/src/config.ts
Expand Up @@ -40,6 +40,7 @@ const DEFAULT_GLOBAL_CONFIG: Config.GlobalConfig = {
notifyMode: 'failure-change',
onlyChanged: false,
onlyFailures: false,
openHandlesTimeout: 1000,
outputFile: undefined,
passWithNoTests: false,
projects: [],
Expand Down Expand Up @@ -92,6 +93,7 @@ const DEFAULT_PROJECT_CONFIG: Config.ProjectConfig = {
moduleNameMapper: [],
modulePathIgnorePatterns: [],
modulePaths: [],
openHandlesTimeout: 1000,
prettierPath: 'prettier',
resetMocks: false,
resetModules: false,
Expand Down