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(); + }); + }); + }); +}