diff --git a/CHANGELOG.md b/CHANGELOG.md index 13bd95c916d4..80a3ad1707b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - `[expect]` Highlight substring differences when matcher fails, part 1 ([#8448](https://github.com/facebook/jest/pull/8448)) - `[jest-cli]` Improve chai support (with detailed output, to match jest exceptions) ([#8454](https://github.com/facebook/jest/pull/8454)) +- `[*]` Manage the global timeout with `--testTimeout` command line argument. ([#8456](https://github.com/facebook/jest/pull/8456)) ### Fixes diff --git a/TestUtils.ts b/TestUtils.ts index ba80887bda29..7150270a7b2e 100644 --- a/TestUtils.ts +++ b/TestUtils.ts @@ -55,6 +55,7 @@ const DEFAULT_GLOBAL_CONFIG: Config.GlobalConfig = { testNamePattern: '', testPathPattern: '', testResultsProcessor: null, + testTimeout: 5000, updateSnapshot: 'none', useStderr: false, verbose: false, diff --git a/docs/CLI.md b/docs/CLI.md index 2530a940879b..079524e58d13 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -301,6 +301,10 @@ Lets you specify a custom test runner. Lets you specify a custom test sequencer. Please refer to the documentation of the corresponding configuration property for details. +### `--testTimeout=` + +Default timeout of a test in milliseconds. Default value: 5000. + ### `--updateSnapshot` Alias: `-u`. Use this flag to re-record every snapshot that fails during this test run. Can be used together with a test suite pattern or with `--testNamePattern` to re-record snapshots. diff --git a/e2e/__tests__/__snapshots__/timeouts.test.ts.snap b/e2e/__tests__/__snapshots__/timeouts.test.ts.snap index 19ed2b0400be..4a2e6ad2faba 100644 --- a/e2e/__tests__/__snapshots__/timeouts.test.ts.snap +++ b/e2e/__tests__/__snapshots__/timeouts.test.ts.snap @@ -1,5 +1,18 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`does not exceed the command line testTimeout 1`] = ` +PASS __tests__/a-banana.js + ✓ banana +`; + +exports[`does not exceed the command line testTimeout 2`] = ` +Test Suites: 1 passed, 1 total +Tests: 1 passed, 1 total +Snapshots: 0 total +Time: <> +Ran all test suites. +`; + exports[`does not exceed the timeout 1`] = ` PASS __tests__/a-banana.js ✓ banana @@ -13,6 +26,14 @@ Time: <> Ran all test suites. `; +exports[`exceeds the command line testTimeout 1`] = ` +Test Suites: 1 failed, 1 total +Tests: 1 failed, 1 total +Snapshots: 0 total +Time: <> +Ran all test suites. +`; + exports[`exceeds the timeout 1`] = ` Test Suites: 1 failed, 1 total Tests: 1 failed, 1 total diff --git a/e2e/__tests__/timeouts.test.ts b/e2e/__tests__/timeouts.test.ts index 09d38e6e9d1b..7e9ab43e5da1 100644 --- a/e2e/__tests__/timeouts.test.ts +++ b/e2e/__tests__/timeouts.test.ts @@ -60,3 +60,53 @@ test('does not exceed the timeout', () => { expect(wrap(summary)).toMatchSnapshot(); expect(status).toBe(0); }); + +test('exceeds the command line testTimeout', () => { + writeFiles(DIR, { + '__tests__/a-banana.js': ` + + test('banana', () => { + return new Promise(resolve => { + setTimeout(resolve, 1000); + }); + }); + `, + 'package.json': '{}', + }); + + const {stderr, status} = runJest(DIR, [ + '-w=1', + '--ci=false', + '--testTimeout=200', + ]); + const {rest, summary} = extractSummary(stderr); + expect(rest).toMatch( + /(jest\.setTimeout|jasmine\.DEFAULT_TIMEOUT_INTERVAL|Exceeded timeout)/, + ); + expect(wrap(summary)).toMatchSnapshot(); + expect(status).toBe(1); +}); + +test('does not exceed the command line testTimeout', () => { + writeFiles(DIR, { + '__tests__/a-banana.js': ` + + test('banana', () => { + return new Promise(resolve => { + setTimeout(resolve, 200); + }); + }); + `, + 'package.json': '{}', + }); + + const {stderr, status} = runJest(DIR, [ + '-w=1', + '--ci=false', + '--testTimeout=1000', + ]); + const {rest, summary} = extractSummary(stderr); + expect(wrap(rest)).toMatchSnapshot(); + expect(wrap(summary)).toMatchSnapshot(); + expect(status).toBe(0); +}); diff --git a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts index 434dd63675f1..8f75aba0df63 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts @@ -16,7 +16,12 @@ import { buildSnapshotResolver, } from 'jest-snapshot'; import throat from 'throat'; -import {addEventHandler, dispatch, ROOT_DESCRIBE_BLOCK_NAME} from '../state'; +import { + addEventHandler, + dispatch, + getState as getRunnerState, + ROOT_DESCRIBE_BLOCK_NAME, +} from '../state'; import {getTestID} from '../utils'; import run from '../run'; import globals from '..'; @@ -42,6 +47,10 @@ export const initialize = ({ testPath: Config.Path; parentProcess: Process; }) => { + if (globalConfig.testTimeout) { + getRunnerState().testTimeout = globalConfig.testTimeout; + } + const mutex = throat(globalConfig.maxConcurrency); Object.assign(global, globals); diff --git a/packages/jest-cli/src/cli/args.ts b/packages/jest-cli/src/cli/args.ts index 2f7c38aa0279..7bba9a038621 100644 --- a/packages/jest-cli/src/cli/args.ts +++ b/packages/jest-cli/src/cli/args.ts @@ -622,6 +622,10 @@ export const options = { 'provided: `/path/to/testSequencer.js`', type: 'string' as 'string', }, + testTimeout: { + description: 'This option sets the default timeouts of test cases.', + type: 'number' as 'number', + }, testURL: { description: 'This option sets the URL for the jsdom environment.', type: 'string' as 'string', diff --git a/packages/jest-config/src/ValidConfig.ts b/packages/jest-config/src/ValidConfig.ts index 97e22dc030da..ee69c72b178f 100644 --- a/packages/jest-config/src/ValidConfig.ts +++ b/packages/jest-config/src/ValidConfig.ts @@ -113,6 +113,7 @@ const initialOptions: Config.InitialOptions = { testResultsProcessor: 'processor-node-module', testRunner: 'jasmine2', testSequencer: '@jest/test-sequencer', + testTimeout: 5000, testURL: 'http://localhost', timers: 'real', transform: { diff --git a/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap b/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap index b566ee92df51..96d7622c963e 100644 --- a/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap +++ b/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap @@ -168,6 +168,16 @@ exports[`testPathPattern ignores invalid regular expressions exports[`testPathPattern --testPathPattern ignores invalid regular expressions and logs a warning 1`] = `" Invalid testPattern a( supplied. Running all tests instead."`; +exports[`testTimeout should throw an error if timeout is a negative number 1`] = ` +"Validation Error: + + Option \\"testTimeout\\" must be a natural number. + + Configuration Documentation: + https://jestjs.io/docs/configuration.html +" +`; + exports[`watchPlugins throw error when a watch plugin is not found 1`] = ` "Validation Error: diff --git a/packages/jest-config/src/__tests__/normalize.test.js b/packages/jest-config/src/__tests__/normalize.test.js index 88e557f1939e..3930cd473e09 100644 --- a/packages/jest-config/src/__tests__/normalize.test.js +++ b/packages/jest-config/src/__tests__/normalize.test.js @@ -1562,3 +1562,19 @@ describe('displayName', () => { }, ); }); + +describe('testTimeout', () => { + it('should return timeout value if defined', () => { + console.warn.mockImplementation(() => {}); + const {options} = normalize({rootDir: '/root/', testTimeout: 1000}, {}); + + expect(options.testTimeout).toBe(1000); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('should throw an error if timeout is a negative number', () => { + expect(() => + normalize({rootDir: '/root/', testTimeout: -1}, {}), + ).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/packages/jest-config/src/index.ts b/packages/jest-config/src/index.ts index 337e7ab47c81..9290126323df 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -149,6 +149,7 @@ const groupOptions = ( testPathPattern: options.testPathPattern, testResultsProcessor: options.testResultsProcessor, testSequencer: options.testSequencer, + testTimeout: options.testTimeout, updateSnapshot: options.updateSnapshot, useStderr: options.useStderr, verbose: options.verbose, diff --git a/packages/jest-config/src/normalize.ts b/packages/jest-config/src/normalize.ts index 813706ab60f2..d31047ac538d 100644 --- a/packages/jest-config/src/normalize.ts +++ b/packages/jest-config/src/normalize.ts @@ -789,6 +789,16 @@ export default function normalize( value = oldOptions[key]; break; } + case 'testTimeout': { + if (oldOptions[key] < 0) { + throw createConfigError( + ` Option "${chalk.bold('testTimeout')}" must be a natural number.`, + ); + } + + value = oldOptions[key]; + break; + } case 'automock': case 'browser': case 'cache': diff --git a/packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.ts.snap b/packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.ts.snap index fc4ae3ee76e0..357612825f0a 100644 --- a/packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.ts.snap +++ b/packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.ts.snap @@ -112,6 +112,7 @@ exports[`prints the config object 1`] = ` "testNamePattern": "", "testPathPattern": "", "testResultsProcessor": null, + "testTimeout": 5000, "updateSnapshot": "none", "useStderr": false, "verbose": false, diff --git a/packages/jest-jasmine2/src/index.ts b/packages/jest-jasmine2/src/index.ts index 1860ad51bf96..7c5e887ed88d 100644 --- a/packages/jest-jasmine2/src/index.ts +++ b/packages/jest-jasmine2/src/index.ts @@ -34,6 +34,7 @@ async function jasmine2( const jasmine = jasmineFactory.create({ process, testPath, + testTimeout: globalConfig.testTimeout, }); const env = jasmine.getEnv(); diff --git a/packages/jest-jasmine2/src/jasmine/jasmineLight.ts b/packages/jest-jasmine2/src/jasmine/jasmineLight.ts index bde9103904fb..837f04906805 100644 --- a/packages/jest-jasmine2/src/jasmine/jasmineLight.ts +++ b/packages/jest-jasmine2/src/jasmine/jasmineLight.ts @@ -43,7 +43,7 @@ import Timer from './Timer'; const create = function(createOptions: Record): Jasmine { const j$ = {...createOptions} as Jasmine; - j$._DEFAULT_TIMEOUT_INTERVAL = 5000; + j$._DEFAULT_TIMEOUT_INTERVAL = createOptions.testTimeout || 5000; j$.getEnv = function(options?: object) { const env = (j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options)); diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index 0793c60d5b38..00a78f0bd1ca 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -207,6 +207,7 @@ export type InitialOptions = { testRunner?: string; testSequencer?: string; testURL?: string; + testTimeout?: number; timers?: 'real' | 'fake'; transform?: { [key: string]: string; @@ -344,6 +345,7 @@ export type GlobalConfig = { testPathPattern: string; testResultsProcessor: string | null | undefined; testSequencer: string; + testTimeout: number; updateSnapshot: SnapshotUpdateState; useStderr: boolean; verbose: boolean | null | undefined; @@ -489,6 +491,7 @@ export type Argv = Arguments< testRunner: string; testSequencer: string; testURL: string; + testTimeout: number | null | undefined; timers: string; transform: string; transformIgnorePatterns: Array;