Skip to content

Commit 2f992e9

Browse files
authored
test(cdk/observers): reduce flakiness in resize observer tests (#27233)
The resize observer tests have been causing some flakiness, because it's up to the browser when to fire the callback and iOS appears to be inconsistent with all other browsers. These changes mock out the `ResizeObserver` API.
1 parent 6e5217f commit 2f992e9

File tree

1 file changed

+50
-15
lines changed

1 file changed

+50
-15
lines changed

src/cdk/observers/private/shared-resize-observer.spec.ts

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,50 @@ import {Component, ElementRef, inject, ViewChild} from '@angular/core';
33
import {SharedResizeObserver} from './shared-resize-observer';
44

55
describe('SharedResizeObserver', () => {
6+
const currentObservers = new Map<ResizeObserverBoxOptions, MockResizeObserver>();
67
let fixture: ComponentFixture<TestComponent>;
78
let instance: TestComponent;
89
let resizeObserver: SharedResizeObserver;
10+
let originalResizeObserver: typeof ResizeObserver;
911
let el1: Element;
1012
let el2: Element;
1113

12-
async function waitForResize() {
13-
fixture.detectChanges();
14-
await new Promise(r => setTimeout(r, 16));
14+
/**
15+
* Mocked out resize observer that allows us to trigger the callback manually. It helps reduce
16+
* test flakines caused by browsers that invoke the callbacks with inconsistent timings.
17+
*/
18+
class MockResizeObserver implements ResizeObserver {
19+
constructor(private _callback: ResizeObserverCallback, options?: ResizeObserverOptions) {
20+
currentObservers.set(options?.box || 'content-box', this);
21+
}
22+
23+
observe(element: Element) {
24+
// The native observer triggers the callback when an element is observed for the first time.
25+
this.triggerCallback(element);
26+
}
27+
28+
unobserve() {}
29+
disconnect() {}
30+
31+
triggerCallback(target: Element) {
32+
this._callback(
33+
[
34+
{
35+
target,
36+
borderBoxSize: [],
37+
contentBoxSize: [],
38+
devicePixelContentBoxSize: [],
39+
contentRect: {} as DOMRect,
40+
},
41+
],
42+
this,
43+
);
44+
}
1545
}
1646

1747
beforeEach(() => {
48+
originalResizeObserver = ResizeObserver;
49+
window.ResizeObserver = MockResizeObserver;
1850
TestBed.configureTestingModule({
1951
declarations: [TestComponent],
2052
});
@@ -26,6 +58,11 @@ describe('SharedResizeObserver', () => {
2658
el2 = instance.el2.nativeElement;
2759
});
2860

61+
afterEach(() => {
62+
window.ResizeObserver = originalResizeObserver;
63+
currentObservers.clear();
64+
});
65+
2966
it('should return the same observable for the same element and same box', () => {
3067
const observable1 = resizeObserver.observe(el1);
3168
const observable2 = resizeObserver.observe(el1);
@@ -60,21 +97,21 @@ describe('SharedResizeObserver', () => {
6097
const observable = resizeObserver.observe(el1);
6198
const resizeSpy1 = jasmine.createSpy('resize handler 1');
6299
observable.subscribe(resizeSpy1);
63-
await waitForResize();
100+
fixture.detectChanges();
64101
expect(resizeSpy1).toHaveBeenCalled();
65102
const resizeSpy2 = jasmine.createSpy('resize handler 2');
66103
observable.subscribe(resizeSpy2);
67-
await waitForResize();
104+
fixture.detectChanges();
68105
expect(resizeSpy2).toHaveBeenCalled();
69106
}));
70107

71108
it('should receive events on resize', waitForAsync(async () => {
72109
const resizeSpy = jasmine.createSpy('resize handler');
73110
resizeObserver.observe(el1).subscribe(resizeSpy);
74-
await waitForResize();
111+
fixture.detectChanges();
75112
resizeSpy.calls.reset();
76-
instance.el1Width = 1;
77-
await waitForResize();
113+
currentObservers.get('content-box')?.triggerCallback(el1);
114+
fixture.detectChanges();
78115
expect(resizeSpy).toHaveBeenCalled();
79116
}));
80117

@@ -83,26 +120,24 @@ describe('SharedResizeObserver', () => {
83120
const resizeSpy2 = jasmine.createSpy('resize handler 2');
84121
resizeObserver.observe(el1).subscribe(resizeSpy1);
85122
resizeObserver.observe(el2).subscribe(resizeSpy2);
86-
await waitForResize();
123+
fixture.detectChanges();
87124
resizeSpy1.calls.reset();
88125
resizeSpy2.calls.reset();
89-
instance.el1Width = 1;
90-
await waitForResize();
126+
currentObservers.get('content-box')?.triggerCallback(el1);
127+
fixture.detectChanges();
91128
expect(resizeSpy1).toHaveBeenCalled();
92129
expect(resizeSpy2).not.toHaveBeenCalled();
93130
}));
94131
});
95132

96133
@Component({
97134
template: `
98-
<div #el1 [style.height.px]="1" [style.width.px]="el1Width"></div>
99-
<div #el2 [style.height.px]="1" [style.width.px]="el2Width"></div>
135+
<div #el1></div>
136+
<div #el2></div>
100137
`,
101138
})
102139
export class TestComponent {
103140
@ViewChild('el1') el1: ElementRef<Element>;
104141
@ViewChild('el2') el2: ElementRef<Element>;
105142
resizeObserver = inject(SharedResizeObserver);
106-
el1Width = 0;
107-
el2Width = 0;
108143
}

0 commit comments

Comments
 (0)