@@ -10,6 +10,7 @@ import { WebRunner } from '../runner';
1010import { BasicCaseCreator } from '../test/creator' ;
1111import type {
1212 IModuleScope ,
13+ ITestCompilerManager ,
1314 ITestContext ,
1415 ITestEnv ,
1516 ITestRunner ,
@@ -25,6 +26,46 @@ type TWatchContext = {
2526 watchState : Record < string , any > ;
2627} ;
2728
29+ const WATCH_BUILD_TIMEOUT_CODE = 'WATCH_BUILD_TIMEOUT' ;
30+
31+ function waitForBuildEvent (
32+ compiler : ITestCompilerManager ,
33+ timeoutMs ?: number ,
34+ ) : Promise < unknown > {
35+ return new Promise ( ( resolve , reject ) => {
36+ const emitter = compiler . getEmitter ( ) ;
37+ let timeout : ReturnType < typeof setTimeout > | undefined ;
38+ const onBuild = ( error : Error | null , stats : unknown ) => {
39+ if ( timeout ) {
40+ clearTimeout ( timeout ) ;
41+ }
42+ emitter . off ( ECompilerEvent . Build , onBuild ) ;
43+ if ( error ) {
44+ reject ( error ) ;
45+ } else {
46+ resolve ( stats ) ;
47+ }
48+ } ;
49+ emitter . on ( ECompilerEvent . Build , onBuild ) ;
50+ if ( typeof timeoutMs === 'number' ) {
51+ timeout = setTimeout ( ( ) => {
52+ emitter . off ( ECompilerEvent . Build , onBuild ) ;
53+ const err = new Error ( `Watch build timeout after ${ timeoutMs } ms` ) ;
54+ ( err as NodeJS . ErrnoException ) . code = WATCH_BUILD_TIMEOUT_CODE ;
55+ reject ( err ) ;
56+ } , timeoutMs ) ;
57+ }
58+ } ) ;
59+ }
60+
61+ function isWatchBuildTimeout ( error : unknown ) {
62+ return (
63+ ! ! error &&
64+ typeof error === 'object' &&
65+ ( error as NodeJS . ErrnoException ) . code === WATCH_BUILD_TIMEOUT_CODE
66+ ) ;
67+ }
68+
2869export function createWatchInitialProcessor (
2970 name : string ,
3071 tempDir : string ,
@@ -286,12 +327,6 @@ export function createWatchStepProcessor(
286327 } ;
287328 processor . build = async ( context : ITestContext ) => {
288329 const compiler = context . getCompiler ( ) ;
289- const task = new Promise ( ( resolve , reject ) => {
290- compiler . getEmitter ( ) . once ( ECompilerEvent . Build , ( e , stats ) => {
291- if ( e ) return reject ( e ) ;
292- resolve ( stats ) ;
293- } ) ;
294- } ) ;
295330 // wait compiler to ready watch the files and diretories
296331
297332 // Native Watcher using [notify](https://github.com/notify-rs/notify) to watch files.
@@ -301,10 +336,28 @@ export function createWatchStepProcessor(
301336 // which will cause the compiler not rebuild when the files change.
302337 // The timeout is set to 400ms for windows OS and 100ms for other OS.
303338 // TODO: This is a workaround, we can remove it when notify support windows better.
304- const timeout = nativeWatcher && process . platform === 'win32' ? 400 : 100 ;
305- await new Promise ( ( resolve ) => setTimeout ( resolve , timeout ) ) ;
306- copyDiff ( path . join ( context . getSource ( ) , step ) , tempDir , false ) ;
307- await task ;
339+ const readyDelay =
340+ nativeWatcher && process . platform === 'win32' ? 400 : 100 ;
341+ const buildTimeout = nativeWatcher ? 10000 : undefined ;
342+ const maxRetries = nativeWatcher ? 1 : 0 ;
343+ const retryDelay = nativeWatcher ? 250 : 0 ;
344+
345+ for ( let attempt = 0 ; attempt <= maxRetries ; attempt ++ ) {
346+ const task = waitForBuildEvent ( compiler , buildTimeout ) ;
347+ const delay = attempt === 0 ? readyDelay : retryDelay ;
348+ if ( delay > 0 ) {
349+ await new Promise ( ( resolve ) => setTimeout ( resolve , delay ) ) ;
350+ }
351+ copyDiff ( path . join ( context . getSource ( ) , step ) , tempDir , false ) ;
352+ try {
353+ await task ;
354+ break ;
355+ } catch ( error ) {
356+ if ( ! isWatchBuildTimeout ( error ) || attempt === maxRetries ) {
357+ throw error ;
358+ }
359+ }
360+ }
308361 } ;
309362 return processor ;
310363}
0 commit comments