1+ import type { ResolvedConfig } from 'vitest'
2+ import { generateHash } from '@vitest/runner/utils'
3+ import { relative } from 'pathe'
14import { channel , client } from './client'
25import { rpcDone } from './rpc'
36import { getBrowserState , getConfig } from './utils'
7+ import { getUiAPI } from './ui'
48
59const url = new URL ( location . href )
610
@@ -23,6 +27,14 @@ function createIframe(container: HTMLDivElement, file: string) {
2327 const iframe = document . createElement ( 'iframe' )
2428 iframe . setAttribute ( 'loading' , 'eager' )
2529 iframe . setAttribute ( 'src' , `${ url . pathname } __vitest_test__/__test__/${ encodeURIComponent ( file ) } ` )
30+ iframe . setAttribute ( 'data-vitest' , 'true' )
31+
32+ iframe . style . display = 'block'
33+ iframe . style . border = 'none'
34+ iframe . style . pointerEvents = 'none'
35+ iframe . setAttribute ( 'allowfullscreen' , 'true' )
36+ iframe . setAttribute ( 'allow' , 'clipboard-write;' )
37+
2638 iframes . set ( file , iframe )
2739 container . appendChild ( iframe )
2840 return iframe
@@ -47,9 +59,23 @@ interface IframeErrorEvent {
4759
4860type IframeChannelEvent = IframeDoneEvent | IframeErrorEvent
4961
62+ async function getContainer ( config : ResolvedConfig ) : Promise < HTMLDivElement > {
63+ if ( config . browser . ui ) {
64+ const element = document . querySelector ( '#tester-ui' )
65+ if ( ! element ) {
66+ return new Promise < HTMLDivElement > ( ( resolve ) => {
67+ setTimeout ( ( ) => {
68+ resolve ( getContainer ( config ) )
69+ } , 30 )
70+ } )
71+ }
72+ return element as HTMLDivElement
73+ }
74+ return document . querySelector ( '#vitest-tester' ) as HTMLDivElement
75+ }
76+
5077client . ws . addEventListener ( 'open' , async ( ) => {
5178 const config = getConfig ( )
52- const container = document . querySelector ( '#vitest-tester' ) as HTMLDivElement
5379 const testFiles = getBrowserState ( ) . files
5480
5581 debug ( 'test files' , testFiles . join ( ', ' ) )
@@ -60,6 +86,7 @@ client.ws.addEventListener('open', async () => {
6086 return
6187 }
6288
89+ const container = await getContainer ( config )
6390 const runningFiles = new Set < string > ( testFiles )
6491
6592 channel . addEventListener ( 'message' , async ( e : MessageEvent < IframeChannelEvent > ) : Promise < void > => {
@@ -70,6 +97,13 @@ client.ws.addEventListener('open', async () => {
7097 filenames . forEach ( filename => runningFiles . delete ( filename ) )
7198
7299 if ( ! runningFiles . size ) {
100+ const ui = getUiAPI ( )
101+ // in isolated mode we don't change UI because it will slow down tests,
102+ // so we only select it when the run is done
103+ if ( ui && filenames . length > 1 ) {
104+ const id = generateFileId ( filenames [ filenames . length - 1 ] )
105+ ui . setCurrentById ( id )
106+ }
73107 await done ( )
74108 }
75109 else {
@@ -103,6 +137,11 @@ client.ws.addEventListener('open', async () => {
103137 }
104138 } )
105139
140+ if ( config . browser . ui ) {
141+ container . className = ''
142+ container . textContent = ''
143+ }
144+
106145 if ( config . isolate === false ) {
107146 createIframe (
108147 container ,
@@ -113,6 +152,13 @@ client.ws.addEventListener('open', async () => {
113152 // otherwise, we need to wait for each iframe to finish before creating the next one
114153 // this is the most stable way to run tests in the browser
115154 for ( const file of testFiles ) {
155+ const ui = getUiAPI ( )
156+
157+ if ( ui ) {
158+ const id = generateFileId ( file )
159+ ui . setCurrentById ( id )
160+ }
161+
116162 createIframe (
117163 container ,
118164 file ,
@@ -129,3 +175,10 @@ client.ws.addEventListener('open', async () => {
129175 }
130176 }
131177} )
178+
179+ function generateFileId ( file : string ) {
180+ const config = getConfig ( )
181+ const project = config . name || ''
182+ const path = relative ( config . root , file )
183+ return generateHash ( `${ path } ${ project } ` )
184+ }
0 commit comments