···576576 )
577577}
578578579579+let currentViewport: { width: number; height: number } | undefined
580580+579581async function setIframeViewport(
580582 width: number,
581583 height: number,
···589591 document.body.style.setProperty('--viewport-width', `${width}px`)
590592 document.body.style.setProperty('--viewport-height', `${height}px`)
591593594594+ // playwright emulates the viewport per page, so it keeps its size
595595+ // between test files and the command round trip is only needed when
596596+ // the size actually changes; other providers resize the window, which
597597+ // outside code can move under us, so they always re-pin
598598+ const cacheable = getBrowserState().provider === 'playwright'
599599+ if (
600600+ cacheable
601601+ && currentViewport?.width === width
602602+ && currentViewport?.height === height
603603+ ) {
604604+ return
605605+ }
606606+592607 await client.rpc.triggerCommand(
593608 getBrowserState().sessionId,
594609 '__vitest_viewport',
595610 undefined,
596611 [{ width, height }],
597612 )
613613+ currentViewport = cacheable ? { width, height } : undefined
598614 }
599615}
600616
+4
packages/browser/src/node/project.ts
···2222export class ProjectBrowser implements IProjectBrowser {
2323 public testerHtml: Promise<string> | string
2424 public testerFilepath: string
2525+ // the tester template is read once per server, so the transformed HTML
2626+ // is stable too — computing it for every iframe request pays the whole
2727+ // transformIndexHtml plugin pipeline each time
2828+ public testerHtmlTransformed: Promise<string> | undefined
25292630 public provider!: BrowserProvider
2731 public vitest: Vitest
···5858 public config: SerializedConfig
5959 public hashMap = browserHashMap
6060 public sourceMapCache = new Map<string, any>()
6161+ private sourceMapPrefetches = new Map<string, Promise<any>>()
6162 public method = 'run' as TestExecutionMethod
6263 private commands: CommandsManager
6364···218219 if (!('filepath' in suite)) {
219220 return
220221 }
221221- const map = await rpc().getBrowserFileSourceMap(suite.filepath)
222222+ // usually resolved already: the request is fired as soon as the
223223+ // file finishes importing, while collection is still running
224224+ const map = await (
225225+ this.sourceMapPrefetches.get(suite.filepath)
226226+ ?? rpc().getBrowserFileSourceMap(suite.filepath)
227227+ )
228228+ this.sourceMapPrefetches.delete(suite.filepath)
222229 this.sourceMapCache.set(suite.filepath, map)
223230 const snapshotEnvironment = this.config.snapshotOptions.snapshotEnvironment
224231 if (snapshotEnvironment instanceof VitestBrowserSnapshotEnvironment) {
···333340 }
334341 catch (err) {
335342 throw new Error(`Failed to import test file ${filepath}`, { cause: err })
343343+ }
344344+345345+ if (mode === 'collect' && !this.sourceMapPrefetches.has(filepath)) {
346346+ // the file is transformed now, so the server can hand out its map;
347347+ // request it early so onBeforeRunSuite doesn't have to wait
348348+ this.sourceMapPrefetches.set(
349349+ filepath,
350350+ rpc().getBrowserFileSourceMap(filepath).catch(() => undefined),
351351+ )
336352 }
337353 }
338354