diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a386a795440..3fa2b87e2fd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - `[jest-config]` [**BREAKING**] Set default display name color based on runner ([#8689](https://github.com/facebook/jest/pull/8689)) - `[jest-config]` Merge preset globals with project globals ([#9027](https://github.com/facebook/jest/pull/9027)) - `[jest-config]` Support `.cjs` config files ([#9291](https://github.com/facebook/jest/pull/9291)) +- `[jest-config, jest-reporter, jest-runner]` Add `slowTestThreshold` configuration option ([#9366](https://github.com/facebook/jest/pull/9366)) - `[jest-core]` Support reporters as default exports ([#9161](https://github.com/facebook/jest/pull/9161)) - `[jest-diff]` Add options for colors and symbols ([#8841](https://github.com/facebook/jest/pull/8841)) - `[jest-diff]` [**BREAKING**] Export as ECMAScript module ([#8873](https://github.com/facebook/jest/pull/8873)) diff --git a/TestUtils.ts b/TestUtils.ts index 1398da6b1ba3..10375966013c 100644 --- a/TestUtils.ts +++ b/TestUtils.ts @@ -106,6 +106,7 @@ const DEFAULT_PROJECT_CONFIG: Config.ProjectConfig = { setupFilesAfterEnv: [], skipFilter: false, skipNodeResolution: false, + slowTestThreshold: 5, snapshotResolver: null, snapshotSerializers: [], testEnvironment: 'node', diff --git a/docs/Configuration.md b/docs/Configuration.md index 50e03f28b87a..5a10fb21337a 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -779,6 +779,12 @@ Example `jest.setup.js` file jest.setTimeout(10000); // in milliseconds ``` +### `slowTestThreshold` [number] + +Default: `5` + +A number of seconds after which a test is considered as slow and reported as such in the results. + ### `snapshotResolver` [string] Default: `undefined` diff --git a/e2e/__tests__/__snapshots__/showConfig.test.ts.snap b/e2e/__tests__/__snapshots__/showConfig.test.ts.snap index 7f3d73efee7c..1f9f052ad80b 100644 --- a/e2e/__tests__/__snapshots__/showConfig.test.ts.snap +++ b/e2e/__tests__/__snapshots__/showConfig.test.ts.snap @@ -50,6 +50,7 @@ exports[`--showConfig outputs config info and exits 1`] = ` "setupFiles": [], "setupFilesAfterEnv": [], "skipFilter": false, + "slowTestThreshold": 5, "snapshotSerializers": [], "testEnvironment": "<>/jest-environment-jsdom/build/index.js", "testEnvironmentOptions": {}, diff --git a/packages/jest-config/src/Defaults.ts b/packages/jest-config/src/Defaults.ts index af912c569d2c..e8a7a42da269 100644 --- a/packages/jest-config/src/Defaults.ts +++ b/packages/jest-config/src/Defaults.ts @@ -52,6 +52,7 @@ const defaultOptions: Config.DefaultOptions = { setupFiles: [], setupFilesAfterEnv: [], skipFilter: false, + slowTestThreshold: 5, snapshotSerializers: [], testEnvironment: 'jest-environment-jsdom', testEnvironmentOptions: {}, diff --git a/packages/jest-config/src/Descriptions.ts b/packages/jest-config/src/Descriptions.ts index 3ff6d9b38b11..5404b008476f 100644 --- a/packages/jest-config/src/Descriptions.ts +++ b/packages/jest-config/src/Descriptions.ts @@ -66,6 +66,8 @@ const descriptions: {[key in keyof Config.InitialOptions]: string} = { 'The paths to modules that run some code to configure or set up the testing environment before each test', setupFilesAfterEnv: 'A list of paths to modules that run some code to configure or set up the testing framework before each test', + slowTestThreshold: + 'A number of seconds after which a test is considered as slow and reported as such in the results.', snapshotSerializers: 'A list of paths to snapshot serializer modules Jest should use for snapshot testing', testEnvironment: 'The test environment that will be used for testing', diff --git a/packages/jest-config/src/ValidConfig.ts b/packages/jest-config/src/ValidConfig.ts index b07cdb441f2c..9dcd3533b6dd 100644 --- a/packages/jest-config/src/ValidConfig.ts +++ b/packages/jest-config/src/ValidConfig.ts @@ -99,6 +99,7 @@ const initialOptions: Config.InitialOptions = { silent: true, skipFilter: false, skipNodeResolution: false, + slowTestThreshold: 5, snapshotResolver: '/snapshotResolver.js', snapshotSerializers: ['my-serializer-module'], testEnvironment: 'jest-environment-jsdom', diff --git a/packages/jest-config/src/index.ts b/packages/jest-config/src/index.ts index faf1a3deb1a0..a569a0f44514 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -199,6 +199,7 @@ const groupOptions = ( setupFilesAfterEnv: options.setupFilesAfterEnv, skipFilter: options.skipFilter, skipNodeResolution: options.skipNodeResolution, + slowTestThreshold: options.slowTestThreshold, snapshotResolver: options.snapshotResolver, snapshotSerializers: options.snapshotSerializers, testEnvironment: options.testEnvironment, diff --git a/packages/jest-config/src/normalize.ts b/packages/jest-config/src/normalize.ts index f0cf0213eee0..2dea6a3f3e99 100644 --- a/packages/jest-config/src/normalize.ts +++ b/packages/jest-config/src/normalize.ts @@ -870,6 +870,7 @@ export default function normalize( case 'silent': case 'skipFilter': case 'skipNodeResolution': + case 'slowTestThreshold': case 'testEnvironment': case 'testEnvironmentOptions': case 'testFailureExitCode': 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 3be6c4ebb716..abcedcd37a56 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 @@ -45,6 +45,7 @@ exports[`prints the config object 1`] = ` "setupFilesAfterEnv": [], "skipFilter": false, "skipNodeResolution": false, + "slowTestThreshold": 5, "snapshotResolver": null, "snapshotSerializers": [], "testEnvironment": "node", diff --git a/packages/jest-reporters/src/__tests__/get_result_header.test.js b/packages/jest-reporters/src/__tests__/get_result_header.test.js index 3c0c3351ae3e..da182c17b1e6 100644 --- a/packages/jest-reporters/src/__tests__/get_result_header.test.js +++ b/packages/jest-reporters/src/__tests__/get_result_header.test.js @@ -11,9 +11,28 @@ const terminalLink = require('terminal-link'); jest.mock('terminal-link', () => jest.fn(() => 'wannabehyperlink')); +const endTime = 1577717671160; +const testTime = 5500; + const testResult = { testFilePath: '/foo', }; +const testResultSlow = { + perfStats: { + end: endTime, + slow: true, + start: endTime - testTime, + }, + testFilePath: '/foo', +}; +const testResultFast = { + perfStats: { + end: endTime, + slow: false, + start: endTime - testTime, + }, + testFilePath: '/foo', +}; const globalConfig = makeGlobalConfig(); @@ -36,3 +55,15 @@ test('should render the terminal link', () => { expect(result).toContain('wannabehyperlink'); }); + +test('should display test time for slow test', () => { + const result = getResultHeader(testResultSlow, globalConfig); + + expect(result).toContain(`${testTime / 1000}s`); +}); + +test('should not display test time for fast test ', () => { + const result = getResultHeader(testResultFast, globalConfig); + + expect(result).not.toContain(`${testTime / 1000}s`); +}); diff --git a/packages/jest-reporters/src/get_result_header.ts b/packages/jest-reporters/src/get_result_header.ts index 827b7f84833d..4b7d8fe51793 100644 --- a/packages/jest-reporters/src/get_result_header.ts +++ b/packages/jest-reporters/src/get_result_header.ts @@ -41,12 +41,11 @@ export default ( const status = result.numFailingTests > 0 || result.testExecError ? FAIL : PASS; - const runTime = result.perfStats - ? (result.perfStats.end - result.perfStats.start) / 1000 - : null; - const testDetail = []; - if (runTime !== null && runTime > 5) { + + if (result.perfStats && result.perfStats.slow) { + const runTime = (result.perfStats.end - result.perfStats.start) / 1000; + testDetail.push(LONG_TEST_COLOR(runTime + 's')); } diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 5d0232a28c08..59ea3e19b3ff 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -259,7 +259,12 @@ async function runTestInternal( result.numPendingTests + result.numTodoTests; - result.perfStats = {end: Date.now(), start}; + const end = Date.now(); + result.perfStats = { + end, + slow: (end - start) / 1000 > config.slowTestThreshold, + start, + }; result.testFilePath = path; result.console = testConsole.getBuffer(); result.skipped = testCount === result.numPendingTests; diff --git a/packages/jest-test-result/src/__tests__/formatTestResults.test.ts b/packages/jest-test-result/src/__tests__/formatTestResults.test.ts index 523d79b34d7c..f7464021ca51 100644 --- a/packages/jest-test-result/src/__tests__/formatTestResults.test.ts +++ b/packages/jest-test-result/src/__tests__/formatTestResults.test.ts @@ -19,7 +19,7 @@ describe('formatTestResults', () => { testResults: [ { numFailingTests: 0, - perfStats: {end: 2, start: 1}, + perfStats: {end: 2, slow: false, start: 1}, // @ts-ignore testResults: [assertion], }, diff --git a/packages/jest-test-result/src/helpers.ts b/packages/jest-test-result/src/helpers.ts index 07bc0e6e2226..6e86703dd55f 100644 --- a/packages/jest-test-result/src/helpers.ts +++ b/packages/jest-test-result/src/helpers.ts @@ -58,6 +58,7 @@ export const buildFailureTestResult = ( openHandles: [], perfStats: { end: 0, + slow: false, start: 0, }, skipped: false, @@ -155,6 +156,7 @@ export const createEmptyTestResult = (): TestResult => ({ openHandles: [], perfStats: { end: 0, + slow: false, start: 0, }, skipped: false, diff --git a/packages/jest-test-result/src/types.ts b/packages/jest-test-result/src/types.ts index 7a19b782d6d0..3a3b561fdf3b 100644 --- a/packages/jest-test-result/src/types.ts +++ b/packages/jest-test-result/src/types.ts @@ -121,6 +121,7 @@ export type TestResult = { openHandles: Array; perfStats: { end: Milliseconds; + slow: boolean; start: Milliseconds; }; skipped: boolean; diff --git a/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap b/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap index 38b4e2ae1b32..bb604e15bf7b 100644 --- a/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap +++ b/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap @@ -44,6 +44,7 @@ Object { "setupFilesAfterEnv": Array [], "skipFilter": false, "skipNodeResolution": false, + "slowTestThreshold": 5, "snapshotResolver": null, "snapshotSerializers": Array [], "testEnvironment": "node", @@ -222,7 +223,7 @@ exports[`ScriptTransformer uses multiple preprocessors 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"automock":false,"browser":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"filter":null,"forceCoverageMatch":[],"globalSetup":null,"globalTeardown":null,"globals":{},"haste":{"providesModuleNodeModules":[]},"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"resolver":null,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"snapshotResolver":null,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-jasmine2","testURL":"http://localhost","timers":"real","transform":[["^.+\\\\.js$","test_preprocessor"],["^.+\\\\.css$","css-preprocessor"]],"transformIgnorePatterns":["/node_modules/"],"unmockedModulePathPatterns":null,"watchPathIgnorePatterns":[]}', + config: '{"automock":false,"browser":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"filter":null,"forceCoverageMatch":[],"globalSetup":null,"globalTeardown":null,"globals":{},"haste":{"providesModuleNodeModules":[]},"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"resolver":null,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotResolver":null,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-jasmine2","testURL":"http://localhost","timers":"real","transform":[["^.+\\\\.js$","test_preprocessor"],["^.+\\\\.css$","css-preprocessor"]],"transformIgnorePatterns":["/node_modules/"],"unmockedModulePathPatterns":null,"watchPathIgnorePatterns":[]}', }; `; @@ -239,7 +240,7 @@ exports[`ScriptTransformer uses the supplied preprocessor 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"automock":false,"browser":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"filter":null,"forceCoverageMatch":[],"globalSetup":null,"globalTeardown":null,"globals":{},"haste":{"providesModuleNodeModules":[]},"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"resolver":null,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"snapshotResolver":null,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-jasmine2","testURL":"http://localhost","timers":"real","transform":[["^.+\\\\.js$","test_preprocessor"]],"transformIgnorePatterns":["/node_modules/"],"unmockedModulePathPatterns":null,"watchPathIgnorePatterns":[]}', + config: '{"automock":false,"browser":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"filter":null,"forceCoverageMatch":[],"globalSetup":null,"globalTeardown":null,"globals":{},"haste":{"providesModuleNodeModules":[]},"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"resolver":null,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotResolver":null,"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-jasmine2","testURL":"http://localhost","timers":"real","transform":[["^.+\\\\.js$","test_preprocessor"]],"transformIgnorePatterns":["/node_modules/"],"unmockedModulePathPatterns":null,"watchPathIgnorePatterns":[]}', }; `; diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index a69546d8b271..d162e46f2731 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -65,6 +65,7 @@ export type DefaultOptions = { setupFiles: Array; setupFilesAfterEnv: Array; skipFilter: boolean; + slowTestThreshold: number; snapshotSerializers: Array; testEnvironment: string; testEnvironmentOptions: Record; @@ -174,6 +175,7 @@ export type InitialOptions = Partial<{ silent: boolean; skipFilter: boolean; skipNodeResolution: boolean; + slowTestThreshold: number; snapshotResolver: Path; snapshotSerializers: Array; errorOnDeprecated: boolean; @@ -336,6 +338,7 @@ export type ProjectConfig = { setupFilesAfterEnv: Array; skipFilter: boolean; skipNodeResolution?: boolean; + slowTestThreshold: number; snapshotResolver?: Path; snapshotSerializers: Array; testEnvironment: string;