From 142c735e16e3da04627e021d8428134ddd266082 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Thu, 5 Jun 2025 10:45:57 +0200 Subject: [PATCH] fix(browser): calculate prepare time from `createTesters` call on the main thread (#8101) --- docs/guide/profiling-test-performance.md | 2 +- packages/browser/src/client/channel.ts | 1 + packages/browser/src/client/orchestrator.ts | 7 ++++--- packages/browser/src/client/tester/tester.ts | 14 +++++++++----- packages/browser/src/node/pool.ts | 3 +++ packages/vitest/src/types/browser.ts | 1 + 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/docs/guide/profiling-test-performance.md b/docs/guide/profiling-test-performance.md index 2095cbf11..2042b6219 100644 --- a/docs/guide/profiling-test-performance.md +++ b/docs/guide/profiling-test-performance.md @@ -20,7 +20,7 @@ When you run Vitest it reports multiple time metrics of your tests: - Collect: Time spent for collecting all tests in the test files. This includes the time it took to import all file dependencies. - Tests: Time spent for actually running the test cases. - Environment: Time spent for setting up the test [`environment`](/config/#environment), for example JSDOM. -- Prepare: Time Vitest uses to prepare the test runner. +- Prepare: Time Vitest uses to prepare the test runner. When running tests in Node, this is the time to import and execute all internal utilities inside the worker. When running tests in the browser, this also includes the time to initiate the iframe. ## Test runner diff --git a/packages/browser/src/client/channel.ts b/packages/browser/src/client/channel.ts index 242f4aba0..31309d018 100644 --- a/packages/browser/src/client/channel.ts +++ b/packages/browser/src/client/channel.ts @@ -40,6 +40,7 @@ export interface IframeCleanupEvent { export interface IframePrepareEvent { event: 'prepare' iframeId: string + startTime: number } export type GlobalChannelIncomingEvent = GlobalChannelTestRunCanceledEvent diff --git a/packages/browser/src/client/orchestrator.ts b/packages/browser/src/client/orchestrator.ts index 6888d7dcb..f1b071957 100644 --- a/packages/browser/src/client/orchestrator.ts +++ b/packages/browser/src/client/orchestrator.ts @@ -107,7 +107,7 @@ export class IframeOrchestrator { if (!this.iframes.has(ID_ALL)) { debug('preparing non-isolated iframe') - await this.prepareIframe(container, ID_ALL) + await this.prepareIframe(container, ID_ALL, options.startTime) } const config = getConfig() @@ -140,7 +140,7 @@ export class IframeOrchestrator { this.iframes.delete(file) } - const iframe = await this.prepareIframe(container, file) + const iframe = await this.prepareIframe(container, file, options.startTime) await setIframeViewport(iframe, width, height) // running tests after the "prepare" event await sendEventToIframe({ @@ -157,7 +157,7 @@ export class IframeOrchestrator { }) } - private async prepareIframe(container: HTMLDivElement, iframeId: string) { + private async prepareIframe(container: HTMLDivElement, iframeId: string, startTime: number) { const iframe = this.createTestIframe(iframeId) container.appendChild(iframe) @@ -167,6 +167,7 @@ export class IframeOrchestrator { sendEventToIframe({ event: 'prepare', iframeId, + startTime, }).then(resolve, reject) } iframe.onerror = (e) => { diff --git a/packages/browser/src/client/tester/tester.ts b/packages/browser/src/client/tester/tester.ts index 1751c98d8..105857fdb 100644 --- a/packages/browser/src/client/tester/tester.ts +++ b/packages/browser/src/client/tester/tester.ts @@ -63,7 +63,7 @@ channel.addEventListener('message', async (e) => { break } case 'prepare': { - await prepare().catch(err => unhandledError(err, 'Prepare Error')) + await prepare(data).catch(err => unhandledError(err, 'Prepare Error')) break } case 'viewport:done': @@ -93,7 +93,7 @@ getBrowserState().iframeId = iframeId let contextSwitched = false -async function prepareTestEnvironment() { +async function prepareTestEnvironment(options: PrepareOptions) { debug?.('trying to resolve runner', `${reloadStart}`) const config = getConfig() @@ -142,7 +142,7 @@ async function prepareTestEnvironment() { }) } - state.durations.prepare = performance.now() - state.durations.prepare + state.durations.prepare = performance.now() - options.startTime return { runner, @@ -189,8 +189,12 @@ async function executeTests(method: 'run' | 'collect', files: string[]) { } } -async function prepare() { - preparedData = await prepareTestEnvironment() +interface PrepareOptions { + startTime: number +} + +async function prepare(options: PrepareOptions) { + preparedData = await prepareTestEnvironment(options) // page is reloading debug?.('runner resolved successfully') diff --git a/packages/browser/src/node/pool.ts b/packages/browser/src/node/pool.ts index a7a08ad10..479c3b30e 100644 --- a/packages/browser/src/node/pool.ts +++ b/packages/browser/src/node/pool.ts @@ -8,6 +8,7 @@ import type { } from 'vitest/node' import crypto from 'node:crypto' import * as nodeos from 'node:os' +import { performance } from 'node:perf_hooks' import { createDefer } from '@vitest/utils' import { stringify } from 'flatted' import { createDebugger } from 'vitest/node' @@ -307,6 +308,7 @@ class BrowserPool { if (!this._promise) { throw new Error(`Unexpected empty queue`) } + const startTime = performance.now() const orchestrator = this.getOrchestrator(sessionId) debug?.('[%s] run test %s', sessionId, file) @@ -320,6 +322,7 @@ class BrowserPool { // this will be parsed by the test iframe, not the orchestrator // so we need to stringify it first to avoid double serialization providedContext: this._providedContext || '[{}]', + startTime, }, ) .then(() => { diff --git a/packages/vitest/src/types/browser.ts b/packages/vitest/src/types/browser.ts index 11d7c2122..4e124e023 100644 --- a/packages/vitest/src/types/browser.ts +++ b/packages/vitest/src/types/browser.ts @@ -4,4 +4,5 @@ export interface BrowserTesterOptions { method: TestExecutionMethod files: string[] providedContext: string + startTime: number } -- 2.51.2