Skip to content

Commit d306e31

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 c007dd5 commit d306e31

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
@@ -3252,9 +3260,14 @@ describe('@defer', () => {
32523260
};
32533261

32543262
TestBed.configureTestingModule({
3255-
providers: [{provide: ɵDEFER_BLOCK_DEPENDENCY_INTERCEPTOR, useValue: deferDepsInterceptor}],
3263+
providers: [
3264+
{provide: ɵDEFER_BLOCK_DEPENDENCY_INTERCEPTOR, useValue: deferDepsInterceptor},
3265+
{provide: TimerScheduler, useClass: FakeTimerScheduler},
3266+
],
32563267
});
32573268

3269+
const fakeScheduler = TestBed.inject(TimerScheduler) as unknown as FakeTimerScheduler;
3270+
32583271
clearDirectiveDefs(RootCmp);
32593272

32603273
const fixture = TestBed.createComponent(RootCmp);
@@ -3267,7 +3280,7 @@ describe('@defer', () => {
32673280
// Make sure loading function is not yet invoked.
32683281
expect(loadingFnInvokedTimes).toBe(0);
32693282

3270-
await timer(1000);
3283+
fakeScheduler.invoke();
32713284
await allPendingDynamicImports(); // fetching dependencies of the defer block
32723285
fixture.detectChanges();
32733286

@@ -3310,24 +3323,26 @@ describe('@defer', () => {
33103323
})
33113324
class RootCmp {}
33123325

3313-
TestBed.configureTestingModule({});
3326+
TestBed.configureTestingModule({
3327+
providers: [{provide: TimerScheduler, useClass: FakeTimerScheduler}],
3328+
});
3329+
const fakeScheduler = TestBed.inject(TimerScheduler) as unknown as FakeTimerScheduler;
33143330

33153331
clearDirectiveDefs(RootCmp);
33163332

33173333
const fixture = TestBed.createComponent(RootCmp);
3318-
fixture.detectChanges();
33193334

33203335
expect(fixture.nativeElement.outerHTML).toContain('placeholder[top]');
33213336

3322-
await timer(110);
3323-
fixture.detectChanges();
3337+
fakeScheduler.invoke();
3338+
await allPendingDynamicImports(); // fetching dependencies of the defer block
33243339

33253340
// Verify primary blocks content after triggering top-level @defer.
33263341
expect(fixture.nativeElement.outerHTML).toContain('primary[top]');
33273342
expect(fixture.nativeElement.outerHTML).toContain('placeholder[nested]');
33283343

3329-
await timer(110);
3330-
fixture.detectChanges();
3344+
fakeScheduler.invoke();
3345+
await allPendingDynamicImports(); // fetching dependencies of the defer block
33313346

33323347
// Verify that nested @defer block was triggered as well.
33333348
expect(fixture.nativeElement.outerHTML).toContain('primary[top]');
@@ -3376,9 +3391,14 @@ describe('@defer', () => {
33763391
};
33773392

33783393
TestBed.configureTestingModule({
3379-
providers: [{provide: ɵDEFER_BLOCK_DEPENDENCY_INTERCEPTOR, useValue: deferDepsInterceptor}],
3394+
providers: [
3395+
{provide: ɵDEFER_BLOCK_DEPENDENCY_INTERCEPTOR, useValue: deferDepsInterceptor},
3396+
{provide: TimerScheduler, useClass: FakeTimerScheduler},
3397+
],
33803398
});
33813399

3400+
const fakeScheduler = TestBed.inject(TimerScheduler) as unknown as FakeTimerScheduler;
3401+
33823402
clearDirectiveDefs(RootCmp);
33833403

33843404
const fixture = TestBed.createComponent(RootCmp);
@@ -3391,7 +3411,7 @@ describe('@defer', () => {
33913411
// Make sure loading function is not yet invoked.
33923412
expect(loadingFnInvokedTimes).toBe(0);
33933413

3394-
await timer(200);
3414+
fakeScheduler.invoke();
33953415
await allPendingDynamicImports(); // fetching dependencies of the defer block
33963416
fixture.detectChanges();
33973417

0 commit comments

Comments
 (0)