diff --git a/CHANGELOG.md b/CHANGELOG.md index 9491211f29d0..ba08092c9029 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - `[jest-config]` [**BREAKING**] Add `mts` and `cts` to default `moduleFileExtensions` config ([#14369](https://github.com/facebook/jest/pull/14369)) - `[jest-config]` [**BREAKING**] Update `testMatch` and `testRegex` default option for supporting `mjs`, `cjs`, `mts`, and `cts` ([#14584](https://github.com/jestjs/jest/pull/14584)) - `[@jest/core]` [**BREAKING**] Group together open handles with the same stack trace ([#13417](https://github.com/jestjs/jest/pull/13417), & [#14543](https://github.com/jestjs/jest/pull/14543)) +- `[@jest/core]` Add `perfStats` to surface test setup overhead ([#14622](https://github.com/jestjs/jest/pull/14622)) - `[@jest/core, @jest/test-sequencer]` [**BREAKING**] Exposes `globalConfig` & `contexts` to `TestSequencer` ([#14535](https://github.com/jestjs/jest/pull/14535), & [#14543](https://github.com/jestjs/jest/pull/14543)) - `[jest-environment-jsdom]` [**BREAKING**] Upgrade JSDOM to v22 ([#13825](https://github.com/jestjs/jest/pull/13825)) - `[@jest/fake-timers]` [**BREAKING**] Upgrade `@sinonjs/fake-timers` to v11 ([#14544](https://github.com/jestjs/jest/pull/14544)) diff --git a/docs/Configuration.md b/docs/Configuration.md index d557dcee7904..901088da3d7b 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -2080,13 +2080,22 @@ This option allows the use of a custom results processor. This processor must be "column": number, "line": number }, - "duration": number | null + "duration": number | null, + "startAt": epoch | null }, ... ], "perfStats": { - "start": epoch, - "end": epoch + "end": epoch, + "loadTestEnvironmentEnd": epoch, + "loadTestEnvironmentStart": epoch, + "runtime": number, + "setupAfterEnvEnd": epoch, + "setupAfterEnvStart": epoch, + "setupFilesEnd": epoch, + "setupFilesStart": epoch, + "slow": boolean, + "start": epoch }, "testFilePath": absolute path to test file, "coverage": {} diff --git a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts index 682c7f3e4982..2a16a8635a66 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts @@ -74,6 +74,7 @@ const jestAdapter = async ( } }); + const setupAfterEnvStart = Date.now(); for (const path of config.setupFilesAfterEnv) { const esm = runtime.unstable_shouldLoadAsEsm(path); @@ -83,6 +84,7 @@ const jestAdapter = async ( runtime.requireModule(path); } } + const setupAfterEnvEnd = Date.now(); const esm = runtime.unstable_shouldLoadAsEsm(testPath); if (esm) { @@ -91,9 +93,15 @@ const jestAdapter = async ( runtime.requireModule(testPath); } + const setupAfterEnvPerfStats = { + setupAfterEnvEnd, + setupAfterEnvStart, + }; + const results = await runAndTransformResultsToJestFormat({ config, globalConfig, + setupAfterEnvPerfStats, testPath, }); 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 e337c1477eea..31190ad699d1 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts @@ -141,11 +141,13 @@ export const initialize = async ({ export const runAndTransformResultsToJestFormat = async ({ config, globalConfig, + setupAfterEnvPerfStats, testPath, }: { config: Config.ProjectConfig; globalConfig: Config.GlobalConfig; testPath: string; + setupAfterEnvPerfStats: Config.SetupAfterEnvPerfStats; }): Promise => { const runResult: Circus.RunResult = await run(); @@ -189,6 +191,7 @@ export const runAndTransformResultsToJestFormat = async ({ location: testResult.location, numPassingAsserts: testResult.numPassingAsserts, retryReasons: testResult.retryReasons, + startAt: testResult.startedAt, status, title: testResult.testPath[testResult.testPath.length - 1], }; @@ -215,8 +218,10 @@ export const runAndTransformResultsToJestFormat = async ({ await dispatch({name: 'teardown'}); + const emptyTestResult = createEmptyTestResult(); + return { - ...createEmptyTestResult(), + ...emptyTestResult, console: undefined, displayName: config.displayName, failureMessage, @@ -224,6 +229,10 @@ export const runAndTransformResultsToJestFormat = async ({ numPassingTests, numPendingTests, numTodoTests, + perfStats: { + ...emptyTestResult.perfStats, + ...setupAfterEnvPerfStats, + }, testExecError, testFilePath: testPath, testResults: assertionResults, diff --git a/packages/jest-circus/src/utils.ts b/packages/jest-circus/src/utils.ts index 8510ed97e7f4..786b010b7fb7 100644 --- a/packages/jest-circus/src/utils.ts +++ b/packages/jest-circus/src/utils.ts @@ -382,6 +382,7 @@ export const makeSingleTestResult = ( location, numPassingAsserts: test.numPassingAsserts, retryReasons: test.retryReasons.map(_getError).map(getErrorStack), + startedAt: test.startedAt, status, testPath: Array.from(testPath), }; diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index d352dd6adde3..8d458d34f3a7 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -87,6 +87,7 @@ async function runTestInternal( const docblockPragmas = docblock.parse(docblock.extract(testSource)); const customEnvironment = docblockPragmas['jest-environment']; + const loadTestEnvironmentStart = Date.now(); let testEnvironment = projectConfig.testEnvironment; if (customEnvironment) { @@ -169,6 +170,7 @@ async function runTestInternal( testPath: path, }, ); + const loadTestEnvironmentEnd = Date.now(); if (typeof environment.getVmContext !== 'function') { console.error( @@ -213,6 +215,7 @@ async function runTestInternal( const start = Date.now(); + const setupFilesStart = Date.now(); for (const path of projectConfig.setupFiles) { const esm = runtime.unstable_shouldLoadAsEsm(path); @@ -225,6 +228,7 @@ async function runTestInternal( } } } + const setupFilesEnd = Date.now(); const sourcemapOptions: sourcemapSupport.Options = { environment: 'node', @@ -329,8 +333,13 @@ async function runTestInternal( const end = Date.now(); const testRuntime = end - start; result.perfStats = { + ...result.perfStats, end, + loadTestEnvironmentEnd, + loadTestEnvironmentStart, runtime: testRuntime, + setupFilesEnd, + setupFilesStart, slow: testRuntime / 1000 > projectConfig.slowTestThreshold, start, }; diff --git a/packages/jest-test-result/src/helpers.ts b/packages/jest-test-result/src/helpers.ts index 0ad284e7c767..f1f6475428ba 100644 --- a/packages/jest-test-result/src/helpers.ts +++ b/packages/jest-test-result/src/helpers.ts @@ -57,7 +57,13 @@ export const buildFailureTestResult = ( openHandles: [], perfStats: { end: 0, + loadTestEnvironmentEnd: 0, + loadTestEnvironmentStart: 0, runtime: 0, + setupAfterEnvEnd: 0, + setupAfterEnvStart: 0, + setupFilesEnd: 0, + setupFilesStart: 0, slow: false, start: 0, }, @@ -155,7 +161,13 @@ export const createEmptyTestResult = (): TestResult => ({ openHandles: [], perfStats: { end: 0, + loadTestEnvironmentEnd: 0, + loadTestEnvironmentStart: 0, runtime: 0, + setupAfterEnvEnd: 0, + setupAfterEnvStart: 0, + setupFilesEnd: 0, + setupFilesStart: 0, slow: false, start: 0, }, diff --git a/packages/jest-test-result/src/types.ts b/packages/jest-test-result/src/types.ts index 6cd590a5a2db..7200d5a73da8 100644 --- a/packages/jest-test-result/src/types.ts +++ b/packages/jest-test-result/src/types.ts @@ -106,7 +106,13 @@ export type TestResult = { openHandles: Array; perfStats: { end: number; + loadTestEnvironmentEnd: number; + loadTestEnvironmentStart: number; runtime: number; + setupAfterEnvEnd: number; + setupAfterEnvStart: number; + setupFilesEnd: number; + setupFilesStart: number; slow: boolean; start: number; }; diff --git a/packages/jest-types/src/Circus.ts b/packages/jest-types/src/Circus.ts index 700ca87c2613..dbd87e6f8780 100644 --- a/packages/jest-types/src/Circus.ts +++ b/packages/jest-types/src/Circus.ts @@ -209,6 +209,7 @@ export type TestResult = { */ failing?: boolean; invocations: number; + startedAt?: number | null; status: TestStatus; location?: {column: number; line: number} | null; numPassingAsserts: number; diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index 34afe821412e..137b20333904 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -494,6 +494,11 @@ export type ProjectConfig = { workerIdleMemoryLimit?: number; }; +export type SetupAfterEnvPerfStats = { + setupAfterEnvStart: number; + setupAfterEnvEnd: number; +}; + export type Argv = Arguments< Partial<{ all: boolean; diff --git a/packages/jest-types/src/TestResult.ts b/packages/jest-types/src/TestResult.ts index 186e0ec6d6c8..3fe14647f8a1 100644 --- a/packages/jest-types/src/TestResult.ts +++ b/packages/jest-types/src/TestResult.ts @@ -23,6 +23,7 @@ type Callsite = { export type AssertionResult = { ancestorTitles: Array; duration?: number | null; + startAt?: number | null; /** * Whether [`test.failing()`](https://jestjs.io/docs/api#testfailingname-fn-timeout) * was used.