[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

perf(browser): cut per-file round trips (#10730)

authored by

Vladimir and committed by
GitHub
(Jul 10, 2026, 9:18 AM +0200) 3feefd9e b60605ca

+47 -7
+16
packages/browser/src/client/orchestrator.ts
··· 576 576 ) 577 577 } 578 578 579 + let currentViewport: { width: number; height: number } | undefined 580 + 579 581 async function setIframeViewport( 580 582 width: number, 581 583 height: number, ··· 589 591 document.body.style.setProperty('--viewport-width', `${width}px`) 590 592 document.body.style.setProperty('--viewport-height', `${height}px`) 591 593 594 + // playwright emulates the viewport per page, so it keeps its size 595 + // between test files and the command round trip is only needed when 596 + // the size actually changes; other providers resize the window, which 597 + // outside code can move under us, so they always re-pin 598 + const cacheable = getBrowserState().provider === 'playwright' 599 + if ( 600 + cacheable 601 + && currentViewport?.width === width 602 + && currentViewport?.height === height 603 + ) { 604 + return 605 + } 606 + 592 607 await client.rpc.triggerCommand( 593 608 getBrowserState().sessionId, 594 609 '__vitest_viewport', 595 610 undefined, 596 611 [{ width, height }], 597 612 ) 613 + currentViewport = cacheable ? { width, height } : undefined 598 614 } 599 615 } 600 616
+4
packages/browser/src/node/project.ts
··· 22 22 export class ProjectBrowser implements IProjectBrowser { 23 23 public testerHtml: Promise<string> | string 24 24 public testerFilepath: string 25 + // the tester template is read once per server, so the transformed HTML 26 + // is stable too — computing it for every iframe request pays the whole 27 + // transformIndexHtml plugin pipeline each time 28 + public testerHtmlTransformed: Promise<string> | undefined 25 29 26 30 public provider!: BrowserProvider 27 31 public vitest: Vitest
+10 -6
packages/browser/src/node/serverTester.ts
··· 59 59 __VITEST_API_TOKEN__: JSON.stringify(globalServer.vitest.config.api.token), 60 60 }) 61 61 62 - const testerHtml = typeof browserProject.testerHtml === 'string' 63 - ? browserProject.testerHtml 64 - : await browserProject.testerHtml 65 - 66 62 try { 67 - const url = join('/@fs/', browserProject.testerFilepath) 68 - const indexhtml = await browserProject.vite.transformIndexHtml(url, testerHtml) 63 + browserProject.testerHtmlTransformed ??= (async () => { 64 + const testerHtml = typeof browserProject.testerHtml === 'string' 65 + ? browserProject.testerHtml 66 + : await browserProject.testerHtml 67 + const url = join('/@fs/', browserProject.testerFilepath) 68 + return await browserProject.vite.transformIndexHtml(url, testerHtml) 69 + })() 70 + const indexhtml = await browserProject.testerHtmlTransformed 69 71 const html = replacer(indexhtml, { 70 72 __VITEST_FAVICON__: globalServer.faviconUrl, 71 73 __VITEST_INJECTOR__: injector, ··· 73 75 return html 74 76 } 75 77 catch (err: any) { 78 + // don't cache a rejection — the next request should retry the transform 79 + browserProject.testerHtmlTransformed = undefined 76 80 session.fail(err) 77 81 next(err) 78 82 }
+17 -1
packages/browser/src/client/tester/runner.ts
··· 58 58 public config: SerializedConfig 59 59 public hashMap = browserHashMap 60 60 public sourceMapCache = new Map<string, any>() 61 + private sourceMapPrefetches = new Map<string, Promise<any>>() 61 62 public method = 'run' as TestExecutionMethod 62 63 private commands: CommandsManager 63 64 ··· 218 219 if (!('filepath' in suite)) { 219 220 return 220 221 } 221 - const map = await rpc().getBrowserFileSourceMap(suite.filepath) 222 + // usually resolved already: the request is fired as soon as the 223 + // file finishes importing, while collection is still running 224 + const map = await ( 225 + this.sourceMapPrefetches.get(suite.filepath) 226 + ?? rpc().getBrowserFileSourceMap(suite.filepath) 227 + ) 228 + this.sourceMapPrefetches.delete(suite.filepath) 222 229 this.sourceMapCache.set(suite.filepath, map) 223 230 const snapshotEnvironment = this.config.snapshotOptions.snapshotEnvironment 224 231 if (snapshotEnvironment instanceof VitestBrowserSnapshotEnvironment) { ··· 333 340 } 334 341 catch (err) { 335 342 throw new Error(`Failed to import test file ${filepath}`, { cause: err }) 343 + } 344 + 345 + if (mode === 'collect' && !this.sourceMapPrefetches.has(filepath)) { 346 + // the file is transformed now, so the server can hand out its map; 347 + // request it early so onBeforeRunSuite doesn't have to wait 348 + this.sourceMapPrefetches.set( 349 + filepath, 350 + rpc().getBrowserFileSourceMap(filepath).catch(() => undefined), 351 + ) 336 352 } 337 353 } 338 354