1
+ import type { ResolvedConfig } from 'vitest'
2
+ import { generateHash } from '@vitest/runner/utils'
3
+ import { relative } from 'pathe'
1
4
import { channel , client } from './client'
2
5
import { rpcDone } from './rpc'
3
6
import { getBrowserState , getConfig } from './utils'
7
+ import { getUiAPI } from './ui'
4
8
5
9
const url = new URL ( location . href )
6
10
@@ -23,6 +27,14 @@ function createIframe(container: HTMLDivElement, file: string) {
23
27
const iframe = document . createElement ( 'iframe' )
24
28
iframe . setAttribute ( 'loading' , 'eager' )
25
29
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
+
26
38
iframes . set ( file , iframe )
27
39
container . appendChild ( iframe )
28
40
return iframe
@@ -47,9 +59,23 @@ interface IframeErrorEvent {
47
59
48
60
type IframeChannelEvent = IframeDoneEvent | IframeErrorEvent
49
61
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
+
50
77
client . ws . addEventListener ( 'open' , async ( ) => {
51
78
const config = getConfig ( )
52
- const container = document . querySelector ( '#vitest-tester' ) as HTMLDivElement
53
79
const testFiles = getBrowserState ( ) . files
54
80
55
81
debug ( 'test files' , testFiles . join ( ', ' ) )
@@ -60,6 +86,7 @@ client.ws.addEventListener('open', async () => {
60
86
return
61
87
}
62
88
89
+ const container = await getContainer ( config )
63
90
const runningFiles = new Set < string > ( testFiles )
64
91
65
92
channel . addEventListener ( 'message' , async ( e : MessageEvent < IframeChannelEvent > ) : Promise < void > => {
@@ -70,6 +97,13 @@ client.ws.addEventListener('open', async () => {
70
97
filenames . forEach ( filename => runningFiles . delete ( filename ) )
71
98
72
99
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
+ }
73
107
await done ( )
74
108
}
75
109
else {
@@ -103,6 +137,11 @@ client.ws.addEventListener('open', async () => {
103
137
}
104
138
} )
105
139
140
+ if ( config . browser . ui ) {
141
+ container . className = ''
142
+ container . textContent = ''
143
+ }
144
+
106
145
if ( config . isolate === false ) {
107
146
createIframe (
108
147
container ,
@@ -113,6 +152,13 @@ client.ws.addEventListener('open', async () => {
113
152
// otherwise, we need to wait for each iframe to finish before creating the next one
114
153
// this is the most stable way to run tests in the browser
115
154
for ( const file of testFiles ) {
155
+ const ui = getUiAPI ( )
156
+
157
+ if ( ui ) {
158
+ const id = generateFileId ( file )
159
+ ui . setCurrentById ( id )
160
+ }
161
+
116
162
createIframe (
117
163
container ,
118
164
file ,
@@ -129,3 +175,10 @@ client.ws.addEventListener('open', async () => {
129
175
}
130
176
}
131
177
} )
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