Skip to content

Commit bfe7f65

Browse files
lunaleapsfacebook-github-bot
authored andcommitted
Create IntersectionObserver benchmark (#50023)
Summary: Add benchmarks for Intersection Observer Differential Revision: D66847629
1 parent adbcaef commit bfe7f65

File tree

1 file changed

+270
-0
lines changed

1 file changed

+270
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict-local
8+
* @format
9+
* @oncall react_native
10+
* @fantom_flags enableAccessToHostTreeInFabric:true
11+
*/
12+
13+
import type IntersectionObserverType from '../IntersectionObserver';
14+
import type {Root} from '@react-native/fantom';
15+
16+
import * as Fantom from '@react-native/fantom';
17+
import * as React from 'react';
18+
import ScrollView from 'react-native/Libraries/Components/ScrollView/ScrollView';
19+
import View from 'react-native/Libraries/Components/View/View';
20+
import setUpIntersectionObserver from 'react-native/src/private/setup/setUpIntersectionObserver';
21+
import ensureInstance from 'react-native/src/private/utilities/ensureInstance';
22+
import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement';
23+
24+
import 'react-native/Libraries/Core/InitializeCore';
25+
26+
declare const IntersectionObserver: Class<IntersectionObserverType>;
27+
28+
setUpIntersectionObserver();
29+
30+
let maybeNode;
31+
let node: ReactNativeElement;
32+
33+
let maybeScrollViewNode;
34+
let scrollViewNode: ReactNativeElement;
35+
let observer: IntersectionObserverType;
36+
const ROOT_HEIGHT = 1000;
37+
const root = Fantom.createRoot({
38+
viewportHeight: ROOT_HEIGHT,
39+
});
40+
let mockCallback = jest.fn();
41+
42+
function cleanup(renderedRoot: Root, testObserver: ?IntersectionObserverType) {
43+
Fantom.runTask(() => {
44+
if (testObserver != null) {
45+
testObserver.disconnect();
46+
}
47+
root.render(<></>);
48+
});
49+
}
50+
51+
function scrollDown(scrollNode: ReactNativeElement) {
52+
const scrollIncrement = ROOT_HEIGHT / 100;
53+
for (let i = 1; i <= 100; i++) {
54+
Fantom.scrollTo(scrollNode, {
55+
x: 0,
56+
y: i * scrollIncrement,
57+
});
58+
}
59+
}
60+
61+
Fantom.unstable_benchmark
62+
.suite('IntersectionObserver')
63+
.test(
64+
'Create IntersectionObserver',
65+
() => {
66+
Fantom.runTask(() => {
67+
observer = new IntersectionObserver(mockCallback, {});
68+
});
69+
},
70+
{
71+
beforeEach: () => {
72+
mockCallback = jest.fn();
73+
},
74+
afterEach: () => {
75+
expect(mockCallback.mock.calls.length).toBe(0);
76+
cleanup(root, observer);
77+
},
78+
},
79+
)
80+
.test(
81+
'Observe a mounted view',
82+
() => {
83+
Fantom.runTask(() => {
84+
observer.observe(node);
85+
});
86+
},
87+
{
88+
beforeEach: () => {
89+
mockCallback = jest.fn();
90+
Fantom.runTask(() => {
91+
root.render(
92+
<View
93+
style={{width: 100, height: 100}}
94+
ref={receivedNode => {
95+
maybeNode = receivedNode;
96+
}}
97+
/>,
98+
);
99+
observer = new IntersectionObserver(mockCallback, {});
100+
});
101+
node = ensureInstance(maybeNode, ReactNativeElement);
102+
},
103+
afterEach: () => {
104+
expect(mockCallback.mock.calls.length).toBe(1);
105+
const [entries] = mockCallback.mock.lastCall;
106+
expect(entries.length).toBe(1);
107+
expect(entries[0].isIntersecting).toBe(true);
108+
109+
cleanup(root, observer);
110+
},
111+
},
112+
)
113+
.test(
114+
'ScrollView no intersection, no observation',
115+
() => {
116+
scrollDown(scrollViewNode);
117+
},
118+
{
119+
beforeEach: () => {
120+
Fantom.runTask(() => {
121+
root.render(
122+
<ScrollView
123+
ref={receivedNode => {
124+
maybeScrollViewNode = receivedNode;
125+
}}>
126+
<View style={{width: 100, height: ROOT_HEIGHT * 2}} />
127+
<View style={{width: 100, height: 100}} />
128+
</ScrollView>,
129+
);
130+
});
131+
scrollViewNode = ensureInstance(
132+
maybeScrollViewNode,
133+
ReactNativeElement,
134+
);
135+
},
136+
afterEach: () => {
137+
cleanup(root, observer);
138+
},
139+
},
140+
)
141+
.test(
142+
'ScrollView intersection, no observation',
143+
() => {
144+
scrollDown(scrollViewNode);
145+
},
146+
{
147+
beforeEach: () => {
148+
Fantom.runTask(() => {
149+
root.render(
150+
<ScrollView
151+
ref={receivedNode => {
152+
maybeScrollViewNode = receivedNode;
153+
}}>
154+
<View style={{width: 100, height: ROOT_HEIGHT * 0.8}} />
155+
<View style={{width: 100, height: ROOT_HEIGHT * 0.5}} />
156+
<View style={{width: 100, height: ROOT_HEIGHT}} />
157+
</ScrollView>,
158+
);
159+
});
160+
scrollViewNode = ensureInstance(
161+
maybeScrollViewNode,
162+
ReactNativeElement,
163+
);
164+
},
165+
afterEach: () => {
166+
cleanup(root);
167+
},
168+
},
169+
)
170+
.test(
171+
'ScrollView no intersection, observation',
172+
() => {
173+
scrollDown(scrollViewNode);
174+
},
175+
{
176+
beforeEach: () => {
177+
mockCallback = jest.fn();
178+
179+
Fantom.runTask(() => {
180+
root.render(
181+
<ScrollView
182+
ref={receivedNode => {
183+
maybeScrollViewNode = receivedNode;
184+
}}>
185+
<View style={{width: 100, height: ROOT_HEIGHT * 2}} />
186+
<View
187+
ref={receivedNode => {
188+
maybeNode = receivedNode;
189+
}}
190+
style={{width: 100, height: 100}}
191+
/>
192+
</ScrollView>,
193+
);
194+
});
195+
scrollViewNode = ensureInstance(
196+
maybeScrollViewNode,
197+
ReactNativeElement,
198+
);
199+
node = ensureInstance(maybeNode, ReactNativeElement);
200+
Fantom.runTask(() => {
201+
observer = new IntersectionObserver(mockCallback, {});
202+
observer.observe(node);
203+
});
204+
},
205+
afterEach: () => {
206+
expect(mockCallback.mock.calls.length).toBe(1);
207+
208+
const [entries] = mockCallback.mock.lastCall;
209+
expect(entries.length).toBe(1);
210+
expect(entries[0].isIntersecting).toBe(false);
211+
212+
cleanup(root, observer);
213+
},
214+
},
215+
)
216+
.test(
217+
'ScrollView intersection, observation',
218+
() => {
219+
scrollDown(scrollViewNode);
220+
},
221+
{
222+
beforeEach: () => {
223+
mockCallback = jest.fn();
224+
225+
Fantom.runTask(() => {
226+
root.render(
227+
<ScrollView
228+
ref={receivedNode => {
229+
maybeScrollViewNode = receivedNode;
230+
}}>
231+
<View style={{width: 100, height: ROOT_HEIGHT * 0.8}} />
232+
<View
233+
ref={receivedNode => {
234+
maybeNode = receivedNode;
235+
}}
236+
style={{width: 100, height: ROOT_HEIGHT * 0.5}}
237+
/>
238+
<View style={{width: 100, height: ROOT_HEIGHT}} />
239+
</ScrollView>,
240+
);
241+
});
242+
scrollViewNode = ensureInstance(
243+
maybeScrollViewNode,
244+
ReactNativeElement,
245+
);
246+
node = ensureInstance(maybeNode, ReactNativeElement);
247+
Fantom.runTask(() => {
248+
observer = new IntersectionObserver(mockCallback, {threshold: 1});
249+
observer.observe(node);
250+
});
251+
},
252+
afterEach: () => {
253+
expect(mockCallback.mock.calls.length).toBe(3);
254+
255+
const [entries1] = mockCallback.mock.calls[0];
256+
expect(entries1.length).toBe(1);
257+
expect(entries1[0].isIntersecting).toBe(false);
258+
259+
const [entries2] = mockCallback.mock.calls[1];
260+
expect(entries2.length).toBe(1);
261+
expect(entries2[0].isIntersecting).toBe(true);
262+
263+
const [entries3] = mockCallback.mock.calls[2];
264+
expect(entries3.length).toBe(1);
265+
expect(entries3[0].isIntersecting).toBe(false);
266+
267+
cleanup(root, observer);
268+
},
269+
},
270+
);

0 commit comments

Comments
 (0)