From 6581c69e104902417304f8bf7b1dedd5bf480a67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Tue, 20 May 2025 09:54:58 -0700 Subject: [PATCH] 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 --- .../__tests__/IntersectionObserver-itest.js | 36 +++++++++++++++++++ .../__tests__/structuredClone-itest.js | 12 +++---- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/packages/react-native/src/private/webapis/intersectionobserver/__tests__/IntersectionObserver-itest.js b/packages/react-native/src/private/webapis/intersectionobserver/__tests__/IntersectionObserver-itest.js index 125343172a5..964d772b51d 100644 --- a/packages/react-native/src/private/webapis/intersectionobserver/__tests__/IntersectionObserver-itest.js +++ b/packages/react-native/src/private/webapis/intersectionobserver/__tests__/IntersectionObserver-itest.js @@ -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(); + + Fantom.runTask(() => { + root.render(); + }); + + 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); + }); }); }); diff --git a/packages/react-native/src/private/webapis/structuredClone/__tests__/structuredClone-itest.js b/packages/react-native/src/private/webapis/structuredClone/__tests__/structuredClone-itest.js index 68c6cc8dbed..eeeaa8fe466 100644 --- a/packages/react-native/src/private/webapis/structuredClone/__tests__/structuredClone-itest.js +++ b/packages/react-native/src/private/webapis/structuredClone/__tests__/structuredClone-itest.js @@ -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 = []; 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', () => {