Fix structuredClone test and added test to clarify behavior in IntersectionObserver (#51476)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51476

Changelog: [internal]

When we added integration of IntersectionObserver in the event loop by default, we changed the timing of the observer notification:
- Before, it was scheduled synchronously from the `observe` call.  That means that, if you schedule another task immediately after the observe call, the order is IO callback then task.
- After, it was scheduled at the end of the current event loop tick. That means that, if you schedule another task immediately after the observe call, the order is task then IO callback.

This change in order wasn't accounted for in the tests for IO (which it's added here) and made a test for `structuredClone` break because it was using incorrect assumptions.

This fixes that test.

Reviewed By: rshest

Differential Revision: D75073118

fbshipit-source-id: 38ad2d03686891daf4caa836e1f597917910ddd0
This commit is contained in:
Rubén Norte
2025-05-20 09:54:58 -07:00
committed by Facebook GitHub Bot
parent 0ac82ef83a
commit 6581c69e10
2 changed files with 42 additions and 6 deletions
@@ -1577,5 +1577,41 @@ describe('IntersectionObserver', () => {
expect(callback).not.toHaveBeenCalled();
});
it('should not dispatch pending entries when disconnecting', () => {
const root = Fantom.createRoot({
viewportWidth: 1000,
viewportHeight: 1000,
});
const nodeRef = createRef<HostInstance>();
Fantom.runTask(() => {
root.render(<View ref={nodeRef} style={{width: 100, height: 100}} />);
});
const node = ensureReactNativeElement(nodeRef.current);
const intersectionObserverCallback = jest.fn();
Fantom.runTask(() => {
observer = new IntersectionObserver(intersectionObserverCallback);
// At the end of the current tick, we schedule the execution of the
// intersection observer callback with the initial state of the
// target.
observer.observe(node);
// This is executed in the next tick, before the intersection observer
// callback is called.
Fantom.scheduleTask(() => {
expect(intersectionObserverCallback).not.toHaveBeenCalled();
observer.disconnect();
});
});
expect(intersectionObserverCallback).toHaveBeenCalledTimes(0);
});
});
});
@@ -23,6 +23,7 @@ import EventTarget from 'react-native/src/private/webapis/dom/events/EventTarget
import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement';
import DOMException from 'react-native/src/private/webapis/errors/DOMException';
import IntersectionObserver from 'react-native/src/private/webapis/intersectionobserver/IntersectionObserver';
import IntersectionObserverEntry from 'react-native/src/private/webapis/intersectionobserver/IntersectionObserverEntry';
import MutationObserver from 'react-native/src/private/webapis/mutationobserver/MutationObserver';
import structuredClone from 'react-native/src/private/webapis/structuredClone/structuredClone';
@@ -390,18 +391,17 @@ describe('structuredClone', () => {
const entries: Array<mixed> = [];
Fantom.runTask(() => {
const observer = new IntersectionObserver(e => {
const observer = new IntersectionObserver((e, self) => {
entries.push(...e);
self.disconnect();
});
observer.observe(ensureInstance(ref.current, ReactNativeElement));
Fantom.scheduleTask(() => {
observer.disconnect();
});
});
expectDataCloneError(() => structuredClone(entries[0]));
const entry = ensureInstance(entries[0], IntersectionObserverEntry);
expectDataCloneError(() => structuredClone(entry));
});
it('does NOT clone MutationObserver', () => {