Skip to content

Commit c7cacbf

Browse files
ci: fix flakey defer test (#60461)
This uses a fake timer scheduler implementation to ensure timer tests do not cause flakiness, similar to the incremental hydration tests. PR Close #60461
1 parent 7e65b9f commit c7cacbf

File tree

1 file changed

+39
-19
lines changed

1 file changed

+39
-19
lines changed

packages/core/test/acceptance/defer_spec.ts

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {getInjectorResolutionPath} from '@angular/core/src/render3/util/injector
4848
import {ActivatedRoute, provideRouter, Router, RouterOutlet} from '@angular/router';
4949
import {ChainedInjector} from '@angular/core/src/render3/chained_injector';
5050
import {global} from '../../src/util/global';
51+
import {TimerScheduler} from '@angular/core/src/defer/timer_scheduler';
5152

5253
/**
5354
* Clears all associated directive defs from a given component class.
@@ -94,15 +95,6 @@ function allPendingDynamicImports() {
9495
return dynamicImportOf(null, 10);
9596
}
9697

97-
/**
98-
* Invoke a callback function after a specified amount of time (in ms).
99-
*/
100-
function timer(delay: number): Promise<void> {
101-
return new Promise<void>((resolve) => {
102-
setTimeout(() => resolve(), delay);
103-
});
104-
}
105-
10698
/**
10799
* Allows to verify behavior of defer blocks by providing a set of
108100
* [time, expected output] pairs. Also allows to provide a function
@@ -129,6 +121,22 @@ async function verifyTimeline(
129121
}
130122
}
131123

124+
class FakeTimerScheduler {
125+
cbs: VoidFunction[] = [];
126+
add(delay: number, callback: VoidFunction) {
127+
this.cbs.push(callback);
128+
}
129+
remove(callback: VoidFunction) {
130+
/* noop */
131+
}
132+
133+
invoke() {
134+
for (const cb of this.cbs) {
135+
cb();
136+
}
137+
}
138+
}
139+
132140
/**
133141
* Given a template, creates a component fixture and returns
134142
* a set of helper functions to trigger rendering of prefetching
@@ -3161,9 +3169,14 @@ describe('@defer', () => {
31613169
};
31623170

31633171
TestBed.configureTestingModule({
3164-
providers: [{provide: ɵDEFER_BLOCK_DEPENDENCY_INTERCEPTOR, useValue: deferDepsInterceptor}],
3172+
providers: [
3173+
{provide: ɵDEFER_BLOCK_DEPENDENCY_INTERCEPTOR, useValue: deferDepsInterceptor},
3174+
{provide: TimerScheduler, useClass: FakeTimerScheduler},
3175+
],
31653176
});
31663177

3178+
const fakeScheduler = TestBed.inject(TimerScheduler) as unknown as FakeTimerScheduler;
3179+
31673180
clearDirectiveDefs(RootCmp);
31683181

31693182
const fixture = TestBed.createComponent(RootCmp);
@@ -3176,7 +3189,7 @@ describe('@defer', () => {
31763189
// Make sure loading function is not yet invoked.
31773190
expect(loadingFnInvokedTimes).toBe(0);
31783191

3179-
await timer(1000);
3192+
fakeScheduler.invoke();
31803193
await allPendingDynamicImports(); // fetching dependencies of the defer block
31813194
fixture.detectChanges();
31823195

@@ -3218,24 +3231,26 @@ describe('@defer', () => {
32183231
})
32193232
class RootCmp {}
32203233

3221-
TestBed.configureTestingModule({});
3234+
TestBed.configureTestingModule({
3235+
providers: [{provide: TimerScheduler, useClass: FakeTimerScheduler}],
3236+
});
3237+
const fakeScheduler = TestBed.inject(TimerScheduler) as unknown as FakeTimerScheduler;
32223238

32233239
clearDirectiveDefs(RootCmp);
32243240

32253241
const fixture = TestBed.createComponent(RootCmp);
3226-
fixture.detectChanges();
32273242

32283243
expect(fixture.nativeElement.outerHTML).toContain('placeholder[top]');
32293244

3230-
await timer(110);
3231-
fixture.detectChanges();
3245+
fakeScheduler.invoke();
3246+
await allPendingDynamicImports(); // fetching dependencies of the defer block
32323247

32333248
// Verify primary blocks content after triggering top-level @defer.
32343249
expect(fixture.nativeElement.outerHTML).toContain('primary[top]');
32353250
expect(fixture.nativeElement.outerHTML).toContain('placeholder[nested]');
32363251

3237-
await timer(110);
3238-
fixture.detectChanges();
3252+
fakeScheduler.invoke();
3253+
await allPendingDynamicImports(); // fetching dependencies of the defer block
32393254

32403255
// Verify that nested @defer block was triggered as well.
32413256
expect(fixture.nativeElement.outerHTML).toContain('primary[top]');
@@ -3282,9 +3297,14 @@ describe('@defer', () => {
32823297
};
32833298

32843299
TestBed.configureTestingModule({
3285-
providers: [{provide: ɵDEFER_BLOCK_DEPENDENCY_INTERCEPTOR, useValue: deferDepsInterceptor}],
3300+
providers: [
3301+
{provide: ɵDEFER_BLOCK_DEPENDENCY_INTERCEPTOR, useValue: deferDepsInterceptor},
3302+
{provide: TimerScheduler, useClass: FakeTimerScheduler},
3303+
],
32863304
});
32873305

3306+
const fakeScheduler = TestBed.inject(TimerScheduler) as unknown as FakeTimerScheduler;
3307+
32883308
clearDirectiveDefs(RootCmp);
32893309

32903310
const fixture = TestBed.createComponent(RootCmp);
@@ -3297,7 +3317,7 @@ describe('@defer', () => {
32973317
// Make sure loading function is not yet invoked.
32983318
expect(loadingFnInvokedTimes).toBe(0);
32993319

3300-
await timer(200);
3320+
fakeScheduler.invoke();
33013321
await allPendingDynamicImports(); // fetching dependencies of the defer block
33023322
fixture.detectChanges();
33033323

0 commit comments

Comments
 (0)