@@ -34,11 +34,18 @@ function createMockClock() {
3434 } ;
3535}
3636
37- // Helper to wait for async operations (command lazy loading, etc.) to complete
38- // Uses multiple iterations to ensure all microtasks and macrotasks complete
39- async function tick ( times = 50 ) : Promise < void > {
40- for ( let i = 0 ; i < times ; i ++ ) {
41- await new Promise ( ( r ) => setTimeout ( r , 0 ) ) ;
37+ // Helper to wait for a condition to be true, with polling
38+ // More reliable than a fixed number of ticks
39+ async function waitFor (
40+ condition : ( ) => boolean ,
41+ timeoutMs = 1000 ,
42+ ) : Promise < void > {
43+ const start = Date . now ( ) ;
44+ while ( ! condition ( ) ) {
45+ if ( Date . now ( ) - start > timeoutMs ) {
46+ throw new Error ( `waitFor timed out after ${ timeoutMs } ms` ) ;
47+ }
48+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
4249 }
4350}
4451
@@ -293,11 +300,8 @@ describe("exec options", () => {
293300
294301 const promise = env . exec ( "sleep 1" ) ;
295302
296- // Wait for async command loading
297- await tick ( ) ;
298-
299- // Should be pending
300- expect ( clock . pendingCount ) . toBe ( 1 ) ;
303+ // Wait for sleep to be registered
304+ await waitFor ( ( ) => clock . pendingCount === 1 ) ;
301305
302306 // Advance clock
303307 clock . advance ( 1000 ) ;
@@ -314,8 +318,7 @@ describe("exec options", () => {
314318 // Start sleep with minute suffix
315319 const promise = env . exec ( "sleep 0.5m" ) ;
316320
317- await tick ( ) ;
318- expect ( clock . pendingCount ) . toBe ( 1 ) ;
321+ await waitFor ( ( ) => clock . pendingCount === 1 ) ;
319322
320323 // 30 seconds = 0.5 minutes
321324 clock . advance ( 30000 ) ;
@@ -331,12 +334,11 @@ describe("exec options", () => {
331334 // GNU sleep sums multiple arguments
332335 const promise = env . exec ( "sleep 1 2" ) ;
333336
334- await tick ( ) ;
335- expect ( clock . pendingCount ) . toBe ( 1 ) ;
337+ await waitFor ( ( ) => clock . pendingCount === 1 ) ;
336338
337- // First advance - not enough
339+ // First advance - not enough (sleep 1 2 = 3 seconds total)
338340 clock . advance ( 2000 ) ;
339- await tick ( ) ;
341+ // pendingCount should still be 1
340342 expect ( clock . pendingCount ) . toBe ( 1 ) ;
341343
342344 // Second advance completes
@@ -357,8 +359,7 @@ describe("exec options", () => {
357359 const p2 = env . exec ( "sleep 2; echo done2" ) ;
358360 const p3 = env . exec ( "sleep 3; echo done3" ) ;
359361
360- await tick ( ) ;
361- expect ( clock . pendingCount ) . toBe ( 3 ) ;
362+ await waitFor ( ( ) => clock . pendingCount === 3 ) ;
362363
363364 // Advance 1 second - first should complete
364365 clock . advance ( 1000 ) ;
@@ -394,8 +395,7 @@ describe("exec options", () => {
394395 env : { B : "value_B" } ,
395396 } ) ;
396397
397- await tick ( ) ;
398- expect ( clock . pendingCount ) . toBe ( 2 ) ;
398+ await waitFor ( ( ) => clock . pendingCount === 2 ) ;
399399
400400 // Advance clock to complete both
401401 clock . advance ( 1000 ) ;
@@ -422,8 +422,7 @@ describe("exec options", () => {
422422 // Command 2: read VAR immediately (before p1's sleep completes)
423423 const p2 = env . exec ( "sleep 1; echo $VAR" , { env : { MARKER : "2" } } ) ;
424424
425- await tick ( ) ;
426- expect ( clock . pendingCount ) . toBe ( 2 ) ;
425+ await waitFor ( ( ) => clock . pendingCount === 2 ) ;
427426
428427 // Advance 1 second - p2 completes first
429428 clock . advance ( 1000 ) ;
@@ -458,8 +457,7 @@ describe("exec options", () => {
458457 ) ;
459458 }
460459
461- await tick ( ) ;
462- expect ( clock . pendingCount ) . toBe ( 10 ) ;
460+ await waitFor ( ( ) => clock . pendingCount === 10 ) ;
463461
464462 // Advance clock to complete all
465463 clock . advance ( 2000 ) ;
@@ -496,8 +494,7 @@ describe("exec options", () => {
496494 { env : { CMD : "2" } } ,
497495 ) ;
498496
499- await tick ( ) ;
500- expect ( clock . pendingCount ) . toBe ( 2 ) ;
497+ await waitFor ( ( ) => clock . pendingCount === 2 ) ;
501498
502499 // Advance 1 second
503500 clock . advance ( 1000 ) ;
@@ -533,8 +530,7 @@ describe("exec options", () => {
533530 const p2 = env . exec ( "sleep 1; pwd; cat file.txt" , { cwd : "/tmp" } ) ;
534531 const p3 = env . exec ( "sleep 1; pwd; cat file.txt" , { cwd : "/var" } ) ;
535532
536- await tick ( ) ;
537- expect ( clock . pendingCount ) . toBe ( 3 ) ;
533+ await waitFor ( ( ) => clock . pendingCount === 3 ) ;
538534 clock . advance ( 1000 ) ;
539535
540536 const [ r1 , r2 , r3 ] = await Promise . all ( [ p1 , p2 , p3 ] ) ;
@@ -563,7 +559,7 @@ describe("exec options", () => {
563559 env : { MARKER : "2" } ,
564560 } ) ;
565561
566- await tick ( ) ;
562+ await waitFor ( ( ) => clock . pendingCount === 2 ) ;
567563 clock . advance ( 1000 ) ;
568564 const r1 = await p1 ;
569565 expect ( r1 . stdout ) . toBe ( "from_p1\n" ) ;
@@ -590,7 +586,7 @@ describe("exec options", () => {
590586 env : { MARKER : "2" } ,
591587 } ) ;
592588
593- await tick ( ) ;
589+ await waitFor ( ( ) => clock . pendingCount === 2 ) ;
594590 clock . advance ( 1000 ) ;
595591 const r1 = await p1 ;
596592 expect ( r1 . stdout ) . toBe ( "" ) ; // errexit stopped execution
@@ -613,7 +609,7 @@ describe("exec options", () => {
613609 const p1 = env . exec ( "export COUNTER=from_p1; sleep 1; echo done1" ) ;
614610 const p2 = env . exec ( "sleep 2; echo $COUNTER" ) ;
615611
616- await tick ( ) ;
612+ await waitFor ( ( ) => clock . pendingCount === 2 ) ;
617613 clock . advance ( 1000 ) ;
618614 await p1 ;
619615
@@ -641,7 +637,7 @@ describe("exec options", () => {
641637 ) ;
642638 }
643639
644- await tick ( ) ;
640+ await waitFor ( ( ) => clock . pendingCount === 20 ) ;
645641 // Advance clock
646642 clock . advance ( 100 ) ;
647643 const results = await Promise . all ( promises ) ;
0 commit comments