11import type * as http from 'node:http'
2- import { dirname , join , resolve } from 'node:path'
3- import os from 'node:os'
2+ import path , { dirname , resolve } from 'node:path'
43import fs from 'fs-extra'
54import { chromium } from 'playwright-chromium'
65import type {
6+ ConfigEnv ,
77 InlineConfig ,
88 Logger ,
99 PluginOption ,
@@ -21,8 +21,8 @@ import {
2121 preview ,
2222} from 'vite'
2323import type { Browser , Page } from 'playwright-chromium'
24- import type { File } from 'vitest'
25- import { beforeAll } from 'vitest'
24+ import type { RunnerTestFile } from 'vitest'
25+ import { beforeAll , inject } from 'vitest'
2626
2727// #region env
2828
@@ -75,32 +75,28 @@ export let browser: Browser = undefined!
7575export let viteTestUrl : string = ''
7676export let watcher : Rollup . RollupWatcher | undefined = undefined
7777
78- declare module 'vite' {
79- interface InlineConfig {
80- testConfig ?: {
81- // relative base output use relative path
82- // rewrite the url to truth file path
83- baseRoute : string
84- }
85- }
86- }
87-
8878export function setViteUrl ( url : string ) : void {
8979 viteTestUrl = url
9080}
9181
9282// #endregion
9383
94- const DIR = join ( os . tmpdir ( ) , 'vitest_playwright_global_setup' )
95-
9684beforeAll ( async ( s ) => {
97- const suite = s as File
85+ const suite = s as RunnerTestFile
86+
87+ testPath = suite . filepath !
88+ testName = slash ( testPath ) . match ( / p l a y g r o u n d \/ ( [ \w - ] + ) \/ / ) ?. [ 1 ]
89+ testDir = dirname ( testPath )
90+ if ( testName ) {
91+ testDir = path . resolve ( workspaceRoot , 'playground-temp' , testName )
92+ }
93+
9894 // skip browser setup for non-playground tests
9995 if ( ! suite . filepath . includes ( 'playground' ) ) {
10096 return
10197 }
10298
103- const wsEndpoint = fs . readFileSync ( join ( DIR , 'wsEndpoint' ) , 'utf-8 ')
99+ const wsEndpoint = inject ( 'wsEndpoint' )
104100 if ( ! wsEndpoint ) {
105101 throw new Error ( 'wsEndpoint not found' )
106102 }
@@ -133,19 +129,22 @@ beforeAll(async (s) => {
133129 browserErrors . push ( error )
134130 } )
135131
136- testPath = suite . filepath !
137- testName = slash ( testPath ) . match ( / p l a y g r o u n d \/ ( [ \w - ] + ) \/ / ) ?. [ 1 ]
138- testDir = dirname ( testPath )
139-
140132 // if this is a test placed under playground/xxx/__tests__
141133 // start a vite server in that directory.
142134 if ( testName ) {
143- testDir = resolve ( workspaceRoot , 'playground-temp' , testName )
144-
145135 // when `root` dir is present, use it as vite's root
146136 const testCustomRoot = resolve ( testDir , 'root' )
147137 rootDir = fs . existsSync ( testCustomRoot ) ? testCustomRoot : testDir
148138
139+ // separate rootDir for variant
140+ const variantName = path . basename ( path . dirname ( testPath ) )
141+ if ( variantName !== '__tests__' ) {
142+ const variantTestDir = testDir + '__' + variantName
143+ if ( fs . existsSync ( variantTestDir ) ) {
144+ rootDir = testDir = variantTestDir
145+ }
146+ }
147+
149148 const testCustomServe = [
150149 resolve ( dirname ( testPath ) , 'serve.ts' ) ,
151150 resolve ( dirname ( testPath ) , 'serve.js' ) ,
@@ -162,7 +161,6 @@ beforeAll(async (s) => {
162161 if ( serve ) {
163162 server = await serve ( )
164163 viteServer = mod . viteServer
165- return
166164 }
167165 } else {
168166 await startDefaultServe ( )
@@ -189,27 +187,26 @@ beforeAll(async (s) => {
189187 }
190188} )
191189
192- function loadConfigFromDir ( dir : string ) {
193- return loadConfigFromFile (
194- {
195- command : isBuild ? 'build' : 'serve' ,
196- mode : isBuild ? 'production' : 'development' ,
197- } ,
198- undefined ,
199- dir ,
200- )
201- }
202-
203- export async function startDefaultServe ( ) : Promise < void > {
190+ async function loadConfig ( configEnv : ConfigEnv ) {
204191 let config : UserConfig | null = null
205- // config file near the *.spec.ts
206- const res = await loadConfigFromDir ( dirname ( testPath ) )
207- if ( res ) {
208- config = res . config
192+
193+ // config file named by convention as the *.spec.ts folder
194+ const variantName = path . basename ( path . dirname ( testPath ) )
195+ if ( variantName !== '__tests__' ) {
196+ const configVariantPath = path . resolve (
197+ rootDir ,
198+ `vite.config-${ variantName } .js` ,
199+ )
200+ if ( fs . existsSync ( configVariantPath ) ) {
201+ const res = await loadConfigFromFile ( configEnv , configVariantPath )
202+ if ( res ) {
203+ config = res . config
204+ }
205+ }
209206 }
210207 // config file from test root dir
211208 if ( ! config ) {
212- const res = await loadConfigFromDir ( rootDir )
209+ const res = await loadConfigFromFile ( configEnv , undefined , rootDir )
213210 if ( res ) {
214211 config = res . config
215212 }
@@ -226,7 +223,6 @@ export async function startDefaultServe(): Promise<void> {
226223 usePolling : true ,
227224 interval : 100 ,
228225 } ,
229- host : true ,
230226 fs : {
231227 strict : ! isBuild ,
232228 } ,
@@ -240,21 +236,20 @@ export async function startDefaultServe(): Promise<void> {
240236 } ,
241237 customLogger : createInMemoryLogger ( serverLogs ) ,
242238 }
239+ return mergeConfig ( options , config || { } )
240+ }
243241
242+ export async function startDefaultServe ( ) : Promise < void > {
244243 setupConsoleWarnCollector ( serverLogs )
245244
246245 if ( ! isBuild ) {
247246 process . env . VITE_INLINE = 'inline-serve'
248- const testConfig = mergeConfig ( options , config || { } )
249- viteConfig = testConfig
250- process . chdir ( rootDir )
251- viteServer = server = await ( await createServer ( testConfig ) ) . listen ( )
252- // use resolved port/base from server
253- const devBase = server . config . base
254- viteTestUrl = `http://localhost:${ server . config . server . port } ${
255- devBase === '/' ? '' : devBase
256- } `
257- setViteUrl ( viteTestUrl )
247+ const config = await loadConfig ( { command : 'serve' , mode : 'development' } )
248+ viteServer = server = await ( await createServer ( config ) ) . listen ( )
249+ viteTestUrl = stripTrailingSlashIfNeeded (
250+ server . resolvedUrls . local [ 0 ] ,
251+ server . config . base ,
252+ )
258253 await page . goto ( viteTestUrl )
259254 } else {
260255 process . env . VITE_INLINE = 'inline-build'
@@ -265,32 +260,41 @@ export async function startDefaultServe(): Promise<void> {
265260 resolvedConfig = config
266261 } ,
267262 } )
268- options . plugins = [ resolvedPlugin ( ) ]
269- const testConfig = mergeConfig ( options , config || { } )
270- viteConfig = testConfig
271- process . chdir ( rootDir )
272- if ( testConfig . builder ) {
273- const builder = await createBuilder ( testConfig )
263+ const buildConfig = mergeConfig (
264+ await loadConfig ( { command : 'build' , mode : 'production' } ) ,
265+ {
266+ plugins : [ resolvedPlugin ( ) ] ,
267+ } ,
268+ )
269+ if ( buildConfig . builder ) {
270+ const builder = await createBuilder ( buildConfig )
274271 await builder . buildApp ( )
275272 } else {
276- const rollupOutput = await build ( testConfig )
273+ const rollupOutput = await build ( buildConfig )
277274 const isWatch = ! ! resolvedConfig ! . build . watch
278275 // in build watch,call startStaticServer after the build is complete
279276 if ( isWatch ) {
280277 watcher = rollupOutput as Rollup . RollupWatcher
281278 await notifyRebuildComplete ( watcher )
282279 }
280+ if ( buildConfig . __test__ ) {
281+ buildConfig . __test__ ( )
282+ }
283283 }
284- if ( config && config . __test__ ) {
285- config . __test__ ( )
286- }
284+
285+ const previewConfig = await loadConfig ( {
286+ command : 'serve' ,
287+ mode : 'development' ,
288+ isPreview : true ,
289+ } )
287290 const _nodeEnv = process . env . NODE_ENV
288- const previewServer = await preview ( testConfig )
291+ const previewServer = await preview ( previewConfig )
289292 // prevent preview change NODE_ENV
290293 process . env . NODE_ENV = _nodeEnv
291- viteTestUrl = previewServer . resolvedUrls . local [ 0 ]
292- viteTestUrl = viteTestUrl . replace ( / \/ + $ / , '' )
293- setViteUrl ( viteTestUrl )
294+ viteTestUrl = stripTrailingSlashIfNeeded (
295+ previewServer . resolvedUrls . local [ 0 ] ,
296+ previewServer . config . base ,
297+ )
294298 await page . goto ( viteTestUrl )
295299 }
296300}
@@ -349,7 +353,7 @@ function createInMemoryLogger(logs: string[]): Logger {
349353function setupConsoleWarnCollector ( logs : string [ ] ) {
350354 const warn = console . warn
351355 console . warn = ( ...args ) => {
352- serverLogs . push ( args . join ( ' ' ) )
356+ logs . push ( args . join ( ' ' ) )
353357 return warn . call ( console , ...args )
354358 }
355359}
@@ -358,6 +362,13 @@ export function slash(p: string): string {
358362 return p . replace ( / \\ / g, '/' )
359363}
360364
365+ function stripTrailingSlashIfNeeded ( url : string , base : string ) : string {
366+ if ( base === '/' ) {
367+ return url . replace ( / \/ $ / , '' )
368+ }
369+ return url
370+ }
371+
361372declare module 'vite' {
362373 export interface UserConfig {
363374 /**
@@ -368,3 +379,9 @@ declare module 'vite' {
368379 __test__ ?: ( ) => void
369380 }
370381}
382+
383+ declare module 'vitest' {
384+ export interface ProvidedContext {
385+ wsEndpoint : string
386+ }
387+ }
0 commit comments