diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dc5d3e317b7..ee46f754762b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Features +- `[jest-config, jest-reporter, jest-runner, jest-test-sequencer]` Add `slowTestThreshold` configuration option ([#9366](https://github.com/facebook/jest/pull/9366)) - `[jest-worker]` Added support for workers to send custom messages to parent in jest-worker ([#10293](https://github.com/facebook/jest/pull/10293)) - `[pretty-format]` Added support for serializing custom elements (web components) ([#10217](https://github.com/facebook/jest/pull/10237)) diff --git a/TestUtils.ts b/TestUtils.ts index 0de81a811a81..a00d1434b198 100644 --- a/TestUtils.ts +++ b/TestUtils.ts @@ -102,6 +102,7 @@ const DEFAULT_PROJECT_CONFIG: Config.ProjectConfig = { setupFilesAfterEnv: [], skipFilter: false, skipNodeResolution: false, + slowTestThreshold: 5, snapshotResolver: undefined, snapshotSerializers: [], testEnvironment: 'node', diff --git a/docs/Configuration.md b/docs/Configuration.md index bd9f49fe35bf..a500132032c2 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -800,6 +800,12 @@ Example `jest.setup.js` file jest.setTimeout(10000); // in milliseconds ``` +### `slowTestThreshold` [number] + +Default: `5` + +The 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 9c644c2b47e6..3f4e045e6cc6 100644 --- a/e2e/__tests__/__snapshots__/showConfig.test.ts.snap +++ b/e2e/__tests__/__snapshots__/showConfig.test.ts.snap @@ -48,6 +48,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-cli/src/init/__tests__/__snapshots__/init.test.js.snap b/packages/jest-cli/src/init/__tests__/__snapshots__/init.test.js.snap index 365a1406c7f6..f00ac27eea2f 100644 --- a/packages/jest-cli/src/init/__tests__/__snapshots__/init.test.js.snap +++ b/packages/jest-cli/src/init/__tests__/__snapshots__/init.test.js.snap @@ -176,6 +176,9 @@ module.exports = { // A list of paths to modules that run some code to configure or set up the testing framework before each test // setupFilesAfterEnv: [], + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + // A list of paths to snapshot serializer modules Jest should use for snapshot testing // snapshotSerializers: [], diff --git a/packages/jest-config/src/Defaults.ts b/packages/jest-config/src/Defaults.ts index f10ec309c835..e86c3174785c 100644 --- a/packages/jest-config/src/Defaults.ts +++ b/packages/jest-config/src/Defaults.ts @@ -50,6 +50,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 f625c5e7e1a3..07d170eb9c44 100644 --- a/packages/jest-config/src/Descriptions.ts +++ b/packages/jest-config/src/Descriptions.ts @@ -67,6 +67,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: + 'The 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 4fa4022acc8c..096416b383e8 100644 --- a/packages/jest-config/src/ValidConfig.ts +++ b/packages/jest-config/src/ValidConfig.ts @@ -97,6 +97,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 de88d4423cf8..8588b49118a8 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -205,6 +205,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 9f6f35062759..ecd851488142 100644 --- a/packages/jest-config/src/normalize.ts +++ b/packages/jest-config/src/normalize.ts @@ -907,6 +907,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 0d7a420000ea..7dcda5d79001 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 @@ -38,6 +38,7 @@ exports[`prints the config object 1`] = ` "setupFilesAfterEnv": [], "skipFilter": false, "skipNodeResolution": false, + "slowTestThreshold": 5, "snapshotSerializers": [], "testEnvironment": "node", "testEnvironmentOptions": {}, 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..a15a4e3df424 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,30 @@ 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, + runtime: testTime, + slow: true, + start: endTime - testTime, + }, + testFilePath: '/foo', +}; +const testResultFast = { + perfStats: { + end: endTime, + runtime: testTime, + slow: false, + start: endTime - testTime, + }, + testFilePath: '/foo', +}; const globalConfig = makeGlobalConfig(); @@ -36,3 +57,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 2b9dd7b25403..5d71b9068c4f 100644 --- a/packages/jest-reporters/src/get_result_header.ts +++ b/packages/jest-reporters/src/get_result_header.ts @@ -42,12 +42,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?.slow) { + const runTime = result.perfStats.runtime / 1000; + testDetail.push(LONG_TEST_COLOR(formatTime(runTime, 0))); } diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 13e574b40436..4fe7bcb0c995 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -266,7 +266,14 @@ async function runTestInternal( result.numPendingTests + result.numTodoTests; - result.perfStats = {end: Date.now(), start}; + const end = Date.now(); + const testRuntime = end - start; + result.perfStats = { + end, + runtime: testRuntime, + slow: testRuntime / 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 3b448597d90c..d186a02469ad 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, runtime: 1, slow: false, start: 1}, // @ts-expect-error testResults: [assertion], }, diff --git a/packages/jest-test-result/src/helpers.ts b/packages/jest-test-result/src/helpers.ts index 1e56aecebb23..33233f6cc32f 100644 --- a/packages/jest-test-result/src/helpers.ts +++ b/packages/jest-test-result/src/helpers.ts @@ -58,6 +58,8 @@ export const buildFailureTestResult = ( openHandles: [], perfStats: { end: 0, + runtime: 0, + slow: false, start: 0, }, skipped: false, @@ -155,6 +157,8 @@ export const createEmptyTestResult = (): TestResult => ({ openHandles: [], perfStats: { end: 0, + runtime: 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 ffd9c23735b6..a38e3f287db8 100644 --- a/packages/jest-test-result/src/types.ts +++ b/packages/jest-test-result/src/types.ts @@ -92,6 +92,8 @@ export type TestResult = { openHandles: Array; perfStats: { end: Milliseconds; + runtime: Milliseconds; + slow: boolean; start: Milliseconds; }; skipped: boolean; diff --git a/packages/jest-test-sequencer/src/__tests__/test_sequencer.test.js b/packages/jest-test-sequencer/src/__tests__/test_sequencer.test.js index bebb86400145..2ee1b47d5f61 100644 --- a/packages/jest-test-sequencer/src/__tests__/test_sequencer.test.js +++ b/packages/jest-test-sequencer/src/__tests__/test_sequencer.test.js @@ -134,23 +134,23 @@ test('writes the cache based on results without existing cache', () => { testResults: [ { numFailingTests: 0, - perfStats: {end: 2, start: 1}, + perfStats: {end: 2, runtime: 1, start: 1}, testFilePath: '/test-a.js', }, { numFailingTests: 0, - perfStats: {end: 0, start: 0}, + perfStats: {end: 0, runtime: 0, start: 0}, skipped: true, testFilePath: '/test-b.js', }, { numFailingTests: 1, - perfStats: {end: 4, start: 1}, + perfStats: {end: 4, runtime: 3, start: 1}, testFilePath: '/test-c.js', }, { numFailingTests: 1, - perfStats: {end: 2, start: 1}, + perfStats: {end: 2, runtime: 1, start: 1}, testFilePath: '/test-x.js', }, ], @@ -177,23 +177,23 @@ test('writes the cache based on the results', () => { testResults: [ { numFailingTests: 0, - perfStats: {end: 2, start: 1}, + perfStats: {end: 2, runtime: 1, start: 1}, testFilePath: '/test-a.js', }, { numFailingTests: 0, - perfStats: {end: 0, start: 0}, + perfStats: {end: 0, runtime: 0, start: 0}, skipped: true, testFilePath: '/test-b.js', }, { numFailingTests: 1, - perfStats: {end: 4, start: 1}, + perfStats: {end: 4, runtime: 3, start: 1}, testFilePath: '/test-c.js', }, { numFailingTests: 1, - perfStats: {end: 2, start: 1}, + perfStats: {end: 2, runtime: 1, start: 1}, testFilePath: '/test-x.js', }, ], @@ -228,23 +228,23 @@ test('works with multiple contexts', () => { testResults: [ { numFailingTests: 0, - perfStats: {end: 2, start: 1}, + perfStats: {end: 2, runtime: 1, start: 1}, testFilePath: '/test-a.js', }, { numFailingTests: 0, - perfStats: {end: 0, start: 0}, + perfStats: {end: 0, runtime: 1, start: 0}, skipped: true, testFilePath: '/test-b.js', }, { numFailingTests: 0, - perfStats: {end: 4, start: 1}, + perfStats: {end: 4, runtime: 3, start: 1}, testFilePath: '/test-c.js', }, { numFailingTests: 1, - perfStats: {end: 2, start: 1}, + perfStats: {end: 2, runtime: 1, start: 1}, testFilePath: '/test-x.js', }, ], diff --git a/packages/jest-test-sequencer/src/index.ts b/packages/jest-test-sequencer/src/index.ts index 26b45976311e..95864c0ba62c 100644 --- a/packages/jest-test-sequencer/src/index.ts +++ b/packages/jest-test-sequencer/src/index.ts @@ -118,7 +118,7 @@ export default class TestSequencer { const perf = testResult.perfStats; cache[testResult.testFilePath] = [ testResult.numFailingTests ? FAIL : SUCCESS, - perf.end - perf.start || 0, + perf.runtime || 0, ]; } }); 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 6114987506eb..d2db10b07090 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 @@ -41,6 +41,7 @@ Object { "setupFilesAfterEnv": Array [], "skipFilter": false, "skipNodeResolution": false, + "slowTestThreshold": 5, "snapshotResolver": undefined, "snapshotSerializers": Array [], "testEnvironment": "node", @@ -229,7 +230,7 @@ exports[`ScriptTransformer uses multiple preprocessors 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"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/"],"watchPathIgnorePatterns":[]}', + config: '{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"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/"],"watchPathIgnorePatterns":[]}', }; `; @@ -246,7 +247,7 @@ exports[`ScriptTransformer uses the supplied preprocessor 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"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/"],"watchPathIgnorePatterns":[]}', + config: '{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{},"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleLoader":"/test_module_loader_path","moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"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/"],"watchPathIgnorePatterns":[]}', }; `; diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index 079061013c83..25a629e7bd6c 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; @@ -171,6 +172,7 @@ export type InitialOptions = Partial<{ silent: boolean; skipFilter: boolean; skipNodeResolution: boolean; + slowTestThreshold: number; snapshotResolver: Path; snapshotSerializers: Array; errorOnDeprecated: boolean; @@ -332,6 +334,7 @@ export type ProjectConfig = { setupFilesAfterEnv: Array; skipFilter: boolean; skipNodeResolution?: boolean; + slowTestThreshold: number; snapshotResolver?: Path; snapshotSerializers: Array; testEnvironment: string;