From 60a4d22307f230dff88e7b667d64b3fc92a6e515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Mon, 2 Dec 2024 04:47:17 -0800 Subject: [PATCH] Migrate ReactFabricPublicInstance tests to Fantom (#48025) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/48025 Changelog: [internal] This migrates the existing tests we have for the current public API for host component refs to Fantom. After this, the only remaining test to migrate before we can clean up the legacy mocks for Fabric, etc. is the one for IntersectionObserer. Reviewed By: sammy-SC Differential Revision: D66599070 fbshipit-source-id: 67da1cd3b360ac79aed6fe6ad2a8bd5273754174 --- .../ReactFabricPublicInstance-Legacy-itest.js | 14 + .../ReactFabricPublicInstance-Modern-itest.js | 15 + .../__tests__/setUpFeatureFlags.js | 16 + ...tUpReactFabricPublicInstanceFantomTests.js | 365 ++++++++++++++++++ 4 files changed, 410 insertions(+) create mode 100644 packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-Legacy-itest.js create mode 100644 packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-Modern-itest.js create mode 100644 packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/setUpFeatureFlags.js create mode 100644 packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/setUpReactFabricPublicInstanceFantomTests.js diff --git a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-Legacy-itest.js b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-Legacy-itest.js new file mode 100644 index 00000000000..6c1477aa7b5 --- /dev/null +++ b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-Legacy-itest.js @@ -0,0 +1,14 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall react_native + */ + +import setUpReactFabricPublicInstanceFantomTests from './setUpReactFabricPublicInstanceFantomTests'; + +setUpReactFabricPublicInstanceFantomTests({isModern: false}); diff --git a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-Modern-itest.js b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-Modern-itest.js new file mode 100644 index 00000000000..9f66d43eacc --- /dev/null +++ b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-Modern-itest.js @@ -0,0 +1,15 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall react_native + */ + +import './setUpFeatureFlags'; +import setUpReactFabricPublicInstanceFantomTests from './setUpReactFabricPublicInstanceFantomTests'; + +setUpReactFabricPublicInstanceFantomTests({isModern: true}); diff --git a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/setUpFeatureFlags.js b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/setUpFeatureFlags.js new file mode 100644 index 00000000000..a5a75c44ac4 --- /dev/null +++ b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/setUpFeatureFlags.js @@ -0,0 +1,16 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall react_native + */ + +import * as ReactNativeFeatureFlags from '../../../../src/private/featureflags/ReactNativeFeatureFlags'; + +ReactNativeFeatureFlags.override({ + enableAccessToHostTreeInFabric: () => true, +}); diff --git a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/setUpReactFabricPublicInstanceFantomTests.js b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/setUpReactFabricPublicInstanceFantomTests.js new file mode 100644 index 00000000000..c62b508a2a8 --- /dev/null +++ b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/setUpReactFabricPublicInstanceFantomTests.js @@ -0,0 +1,365 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall react_native + */ + +import '../../../Core/InitializeCore.js'; + +import * as ReactNativeTester from '../../../../src/private/__tests__/ReactNativeTester'; +import ReactNativeElement from '../../../../src/private/webapis/dom/nodes/ReactNativeElement'; +import TextInputState from '../../../Components/TextInput/TextInputState'; +import View from '../../../Components/View/View'; +import ReactFabricHostComponent from '../ReactFabricHostComponent'; +import nullthrows from 'nullthrows'; +import * as React from 'react'; + +export default function setUpTests({isModern}: {isModern: boolean}) { + // TODO: move these tests to the test file for `ReactNativeElement` when the legacy implementation is removed. + describe(`ReactFabricPublicInstance (${isModern ? 'modern' : 'legacy'})`, () => { + it('should provide instances of the right class as refs in host components', () => { + let node; + + const root = ReactNativeTester.createRoot(); + ReactNativeTester.runTask(() => { + root.render( + { + node = receivedNode; + }} + />, + ); + }); + + expect(node).toBeInstanceOf( + isModern ? ReactNativeElement : ReactFabricHostComponent, + ); + }); + + describe('blur', () => { + test('blur() invokes TextInputState', () => { + const root = ReactNativeTester.createRoot(); + + let maybeNode; + + ReactNativeTester.runTask(() => { + root.render( + { + maybeNode = node; + }} + />, + ); + }); + + const node = nullthrows(maybeNode); + + const blurTextInput = jest.fn(); + + // We don't support view commands in Fantom yet, so we have to mock this. + TextInputState.blurTextInput = blurTextInput; + + ReactNativeTester.runTask(() => { + node.blur(); + }); + + expect(blurTextInput).toHaveBeenCalledTimes(1); + expect(blurTextInput.mock.calls).toEqual([[node]]); + }); + }); + + describe('focus', () => { + test('focus() invokes TextInputState', () => { + const root = ReactNativeTester.createRoot(); + + let maybeNode; + + ReactNativeTester.runTask(() => { + root.render( + { + maybeNode = node; + }} + />, + ); + }); + + const node = nullthrows(maybeNode); + + const focusTextInput = jest.fn(); + + // We don't support view commands in Fantom yet, so we have to mock this. + TextInputState.focusTextInput = focusTextInput; + + ReactNativeTester.runTask(() => { + node.focus(); + }); + + expect(focusTextInput).toHaveBeenCalledTimes(1); + expect(focusTextInput.mock.calls).toEqual([[node]]); + }); + }); + + describe('measure', () => { + it('component.measure(...) invokes callback', () => { + const root = ReactNativeTester.createRoot(); + + let maybeNode; + + ReactNativeTester.runTask(() => { + root.render( + { + maybeNode = node; + }} + />, + ); + }); + + const node = nullthrows(maybeNode); + + const callback = jest.fn(); + node.measure(callback); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback.mock.calls).toEqual([[10, 10, 100, 100, 10, 10]]); + }); + + it('unmounted.measure(...) does nothing', () => { + const root = ReactNativeTester.createRoot(); + + let maybeNode; + + ReactNativeTester.runTask(() => { + root.render( + { + maybeNode = node; + }} + />, + ); + }); + + const node = nullthrows(maybeNode); + + ReactNativeTester.runTask(() => { + root.render(<>); + }); + + const callback = jest.fn(); + node.measure(callback); + + expect(callback).not.toHaveBeenCalled(); + }); + }); + + describe('measureInWindow', () => { + it('component.measureInWindow(...) invokes callback', () => { + const root = ReactNativeTester.createRoot(); + + let maybeNode; + + ReactNativeTester.runTask(() => { + root.render( + { + maybeNode = node; + }} + />, + ); + }); + + const node = nullthrows(maybeNode); + + const callback = jest.fn(); + node.measureInWindow(callback); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback.mock.calls).toEqual([[10, 10, 100, 100]]); + }); + + it('unmounted.measureInWindow(...) does nothing', () => { + const root = ReactNativeTester.createRoot(); + + let maybeNode; + + ReactNativeTester.runTask(() => { + root.render( + { + maybeNode = node; + }} + />, + ); + }); + + const node = nullthrows(maybeNode); + + ReactNativeTester.runTask(() => { + root.render(<>); + }); + + const callback = jest.fn(); + node.measureInWindow(callback); + + expect(callback).not.toHaveBeenCalled(); + }); + }); + + describe('measureLayout', () => { + it('component.measureLayout(component, ...) invokes callback', () => { + const root = ReactNativeTester.createRoot(); + + let maybeParentNode; + let maybeChildNode; + + ReactNativeTester.runTask(() => { + root.render( + { + maybeParentNode = node; + }}> + { + maybeChildNode = node; + }} + /> + , + ); + }); + + const parentNode = nullthrows(maybeParentNode); + const childNode = nullthrows(maybeChildNode); + + const callback = jest.fn(); + childNode.measureLayout(parentNode, callback); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback.mock.calls).toEqual([[20, 20, 10, 10]]); + }); + + it('unmounted.measureLayout(component, ...) does nothing', () => { + const root = ReactNativeTester.createRoot(); + + let maybeParentNode; + let maybeChildNode; + + ReactNativeTester.runTask(() => { + root.render( + { + maybeParentNode = node; + }}> + { + maybeChildNode = node; + }} + /> + , + ); + }); + + const parentNode = nullthrows(maybeParentNode); + const childNode = nullthrows(maybeChildNode); + + ReactNativeTester.runTask(() => { + root.render( + , + ); + }); + + const callback = jest.fn(); + childNode.measureLayout(parentNode, callback); + + expect(callback).not.toHaveBeenCalled(); + }); + + it('component.measureLayout(unmounted, ...) does nothing', () => { + const root = ReactNativeTester.createRoot(); + + let maybeParentNode; + let maybeChildNode; + + ReactNativeTester.runTask(() => { + root.render( + { + maybeParentNode = node; + }}> + { + maybeChildNode = node; + }} + /> + , + ); + }); + + const parentNode = nullthrows(maybeParentNode); + const childNode = nullthrows(maybeChildNode); + + ReactNativeTester.runTask(() => { + root.render( + , + ); + }); + + const callback = jest.fn(); + parentNode.measureLayout(childNode, callback); + + expect(callback).not.toHaveBeenCalled(); + }); + + it('unmounted.measureLayout(unmounted, ...) does nothing', () => { + const root = ReactNativeTester.createRoot(); + + let maybeParentNode; + let maybeChildNode; + + ReactNativeTester.runTask(() => { + root.render( + { + maybeParentNode = node; + }}> + { + maybeChildNode = node; + }} + /> + , + ); + }); + + const parentNode = nullthrows(maybeParentNode); + const childNode = nullthrows(maybeChildNode); + + ReactNativeTester.runTask(() => { + root.render(<>); + }); + + const callback = jest.fn(); + childNode.measureLayout(parentNode, callback); + + expect(callback).not.toHaveBeenCalled(); + }); + }); + }); +}