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 deleted file mode 100644 index e5c2d930d45..00000000000 --- a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-Legacy-itest.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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. - * - * @fantom_flags enableAccessToHostTreeInFabric:false - * @flow strict-local - * @format - */ - -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 deleted file mode 100644 index 815917cceb6..00000000000 --- a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-Modern-itest.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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. - * - * @fantom_flags enableAccessToHostTreeInFabric:true - * @flow strict-local - * @format - */ - -import setUpReactFabricPublicInstanceFantomTests from './setUpReactFabricPublicInstanceFantomTests'; - -setUpReactFabricPublicInstanceFantomTests({isModern: true}); diff --git a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-itest.js b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-itest.js new file mode 100644 index 00000000000..a2960ebdcc4 --- /dev/null +++ b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-itest.js @@ -0,0 +1,406 @@ +/** + * 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 + * @fantom_flags enableAccessToHostTreeInFabric:* + */ + +import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment'; + +import type {HostInstance} from 'react-native'; + +import ReactNativeElement from '../../../../src/private/webapis/dom/nodes/ReactNativeElement'; +import {getRawNativeDOMForTests} from '../../../../src/private/webapis/dom/nodes/specs/NativeDOM'; +import TextInputState from '../../../Components/TextInput/TextInputState'; +import View from '../../../Components/View/View'; +import ReactFabricHostComponent from '../ReactFabricHostComponent'; +import * as Fantom from '@react-native/fantom'; +import nullthrows from 'nullthrows'; +import * as React from 'react'; +import {createRef} from 'react'; +import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags'; + +// TODO: move these tests to the test file for `ReactNativeElement` when the legacy implementation is removed. +describe('ReactFabricPublicInstance', () => { + it('should provide instances of the right class as refs in host components', () => { + const nodeRef = createRef(); + + const root = Fantom.createRoot(); + Fantom.runTask(() => { + root.render(); + }); + + const node = nullthrows(nodeRef.current); + + expect(node).toBeInstanceOf( + ReactNativeFeatureFlags.enableAccessToHostTreeInFabric() + ? ReactNativeElement + : ReactFabricHostComponent, + ); + }); + + describe('blur', () => { + test('blur() invokes TextInputState', () => { + const root = Fantom.createRoot(); + const nodeRef = createRef(); + + Fantom.runTask(() => { + root.render(); + }); + + const node = nullthrows(nodeRef.current); + + const blurTextInput = jest.fn(); + + // We don't support view commands in Fantom yet, so we have to mock this. + TextInputState.blurTextInput = blurTextInput; + + Fantom.runTask(() => { + node.blur(); + }); + + expect(blurTextInput).toHaveBeenCalledTimes(1); + expect(blurTextInput.mock.calls).toEqual([[node]]); + }); + }); + + describe('focus', () => { + test('focus() invokes TextInputState', () => { + const root = Fantom.createRoot(); + const ref = createRef(); + + Fantom.runTask(() => { + root.render(); + }); + + const node = nullthrows(ref.current); + + const focusTextInput = jest.fn(); + + // We don't support view commands in Fantom yet, so we have to mock this. + TextInputState.focusTextInput = focusTextInput; + + Fantom.runTask(() => { + node.focus(); + }); + + expect(focusTextInput).toHaveBeenCalledTimes(1); + expect(focusTextInput.mock.calls).toEqual([[node]]); + }); + }); + + describe('measure', () => { + it('component.measure(...) invokes callback', () => { + const root = Fantom.createRoot(); + const ref = createRef(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + const node = nullthrows(ref.current); + + 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 = Fantom.createRoot(); + const ref = createRef(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + const node = nullthrows(ref.current); + + Fantom.runTask(() => { + root.render(<>); + }); + + const callback = jest.fn(); + node.measure(callback); + + expect(callback).not.toHaveBeenCalled(); + }); + }); + + describe('measureInWindow', () => { + it('component.measureInWindow(...) invokes callback', () => { + const root = Fantom.createRoot(); + const ref = createRef(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + const node = nullthrows(ref.current); + + 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 = Fantom.createRoot(); + const ref = createRef(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + const node = nullthrows(ref.current); + + Fantom.runTask(() => { + root.render(<>); + }); + + const callback = jest.fn(); + node.measureInWindow(callback); + + expect(callback).not.toHaveBeenCalled(); + }); + }); + + describe('measureLayout', () => { + it('component.measureLayout(component, ...) invokes callback', () => { + const root = Fantom.createRoot(); + const parentRef = createRef(); + const childRef = createRef(); + + Fantom.runTask(() => { + root.render( + + + , + ); + }); + + const parentNode = nullthrows(parentRef.current); + const childNode = nullthrows(childRef.current); + + 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 = Fantom.createRoot(); + const parentRef = createRef(); + const childRef = createRef(); + + Fantom.runTask(() => { + root.render( + + + , + ); + }); + + const parentNode = nullthrows(parentRef.current); + const childNode = nullthrows(childRef.current); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + const callback = jest.fn(); + childNode.measureLayout(parentNode, callback); + + expect(callback).not.toHaveBeenCalled(); + }); + + it('component.measureLayout(unmounted, ...) does nothing', () => { + const root = Fantom.createRoot(); + const parentRef = createRef(); + const childRef = createRef(); + + Fantom.runTask(() => { + root.render( + + + , + ); + }); + + const parentNode = nullthrows(parentRef.current); + const childNode = nullthrows(childRef.current); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + const callback = jest.fn(); + parentNode.measureLayout(childNode, callback); + + expect(callback).not.toHaveBeenCalled(); + }); + + it('unmounted.measureLayout(unmounted, ...) does nothing', () => { + const root = Fantom.createRoot(); + const parentRef = createRef(); + const childRef = createRef(); + + Fantom.runTask(() => { + root.render( + + + , + ); + }); + + const parentNode = nullthrows(parentRef.current); + const childNode = nullthrows(childRef.current); + + Fantom.runTask(() => { + root.render(<>); + }); + + const callback = jest.fn(); + childNode.measureLayout(parentNode, callback); + + expect(callback).not.toHaveBeenCalled(); + }); + }); + + describe('setNativeProps', () => { + it('should propagate changes to the host component', () => { + const root = Fantom.createRoot(); + const nodeRef = createRef(); + + Fantom.runTask(() => { + root.render(); + }); + + expect( + root + .getRenderedOutput({ + props: ['testID'], + }) + .toJSX(), + ).toEqual(); + + const element = nullthrows(nodeRef.current); + + Fantom.runTask(() => { + element.setNativeProps({testID: 'second test id'}); + }); + + expect( + root + .getRenderedOutput({ + props: ['testID'], + }) + .toJSX(), + ).toEqual(); + }); + + // TODO: delete when NativeDOM.setNativeProps is NOT nullable. + // This logic is to ensure compatibility with old app versions without the native module method. + if (ReactNativeFeatureFlags.enableAccessToHostTreeInFabric()) { + let RawNativeDOM; + let originalSetNativeProps; + + beforeAll(() => { + RawNativeDOM = nullthrows(getRawNativeDOMForTests()); + originalSetNativeProps = RawNativeDOM.setNativeProps; + }); + + beforeEach(() => { + // $FlowExpectedError[cannot-write] + RawNativeDOM.setNativeProps = originalSetNativeProps; + }); + + it('should propagate changes to the host component (when NativeDOM.setNativeProps is not available)', () => { + // $FlowExpectedError[cannot-write] + RawNativeDOM.setNativeProps = null; + + expect(RawNativeDOM.setNativeProps).toBeNull(); + + const root = Fantom.createRoot(); + const nodeRef = createRef(); + + Fantom.runTask(() => { + root.render(); + }); + + expect( + root + .getRenderedOutput({ + props: ['testID'], + }) + .toJSX(), + ).toEqual(); + + const element = nullthrows(nodeRef.current); + + Fantom.runTask(() => { + element.setNativeProps({testID: 'second test id'}); + }); + + expect( + root + .getRenderedOutput({ + props: ['testID'], + }) + .toJSX(), + ).toEqual(); + }); + } + }); +}); diff --git a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/setUpReactFabricPublicInstanceFantomTests.js b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/setUpReactFabricPublicInstanceFantomTests.js deleted file mode 100644 index 58058e3c8c4..00000000000 --- a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/setUpReactFabricPublicInstanceFantomTests.js +++ /dev/null @@ -1,404 +0,0 @@ -/** - * 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 - */ - -import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment'; - -import type {HostInstance} from 'react-native'; - -import ReactNativeElement from '../../../../src/private/webapis/dom/nodes/ReactNativeElement'; -import {getRawNativeDOMForTests} from '../../../../src/private/webapis/dom/nodes/specs/NativeDOM'; -import TextInputState from '../../../Components/TextInput/TextInputState'; -import View from '../../../Components/View/View'; -import ReactFabricHostComponent from '../ReactFabricHostComponent'; -import * as Fantom from '@react-native/fantom'; -import nullthrows from 'nullthrows'; -import * as React from 'react'; -import {createRef} 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', () => { - const nodeRef = createRef(); - - const root = Fantom.createRoot(); - Fantom.runTask(() => { - root.render(); - }); - - const node = nullthrows(nodeRef.current); - - expect(node).toBeInstanceOf( - isModern ? ReactNativeElement : ReactFabricHostComponent, - ); - }); - - describe('blur', () => { - test('blur() invokes TextInputState', () => { - const root = Fantom.createRoot(); - const nodeRef = createRef(); - - Fantom.runTask(() => { - root.render(); - }); - - const node = nullthrows(nodeRef.current); - - const blurTextInput = jest.fn(); - - // We don't support view commands in Fantom yet, so we have to mock this. - TextInputState.blurTextInput = blurTextInput; - - Fantom.runTask(() => { - node.blur(); - }); - - expect(blurTextInput).toHaveBeenCalledTimes(1); - expect(blurTextInput.mock.calls).toEqual([[node]]); - }); - }); - - describe('focus', () => { - test('focus() invokes TextInputState', () => { - const root = Fantom.createRoot(); - const ref = createRef(); - - Fantom.runTask(() => { - root.render(); - }); - - const node = nullthrows(ref.current); - - const focusTextInput = jest.fn(); - - // We don't support view commands in Fantom yet, so we have to mock this. - TextInputState.focusTextInput = focusTextInput; - - Fantom.runTask(() => { - node.focus(); - }); - - expect(focusTextInput).toHaveBeenCalledTimes(1); - expect(focusTextInput.mock.calls).toEqual([[node]]); - }); - }); - - describe('measure', () => { - it('component.measure(...) invokes callback', () => { - const root = Fantom.createRoot(); - const ref = createRef(); - - Fantom.runTask(() => { - root.render( - , - ); - }); - - const node = nullthrows(ref.current); - - 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 = Fantom.createRoot(); - const ref = createRef(); - - Fantom.runTask(() => { - root.render( - , - ); - }); - - const node = nullthrows(ref.current); - - Fantom.runTask(() => { - root.render(<>); - }); - - const callback = jest.fn(); - node.measure(callback); - - expect(callback).not.toHaveBeenCalled(); - }); - }); - - describe('measureInWindow', () => { - it('component.measureInWindow(...) invokes callback', () => { - const root = Fantom.createRoot(); - const ref = createRef(); - - Fantom.runTask(() => { - root.render( - , - ); - }); - - const node = nullthrows(ref.current); - - 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 = Fantom.createRoot(); - const ref = createRef(); - - Fantom.runTask(() => { - root.render( - , - ); - }); - - const node = nullthrows(ref.current); - - Fantom.runTask(() => { - root.render(<>); - }); - - const callback = jest.fn(); - node.measureInWindow(callback); - - expect(callback).not.toHaveBeenCalled(); - }); - }); - - describe('measureLayout', () => { - it('component.measureLayout(component, ...) invokes callback', () => { - const root = Fantom.createRoot(); - const parentRef = createRef(); - const childRef = createRef(); - - Fantom.runTask(() => { - root.render( - - - , - ); - }); - - const parentNode = nullthrows(parentRef.current); - const childNode = nullthrows(childRef.current); - - 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 = Fantom.createRoot(); - const parentRef = createRef(); - const childRef = createRef(); - - Fantom.runTask(() => { - root.render( - - - , - ); - }); - - const parentNode = nullthrows(parentRef.current); - const childNode = nullthrows(childRef.current); - - Fantom.runTask(() => { - root.render( - , - ); - }); - - const callback = jest.fn(); - childNode.measureLayout(parentNode, callback); - - expect(callback).not.toHaveBeenCalled(); - }); - - it('component.measureLayout(unmounted, ...) does nothing', () => { - const root = Fantom.createRoot(); - const parentRef = createRef(); - const childRef = createRef(); - - Fantom.runTask(() => { - root.render( - - - , - ); - }); - - const parentNode = nullthrows(parentRef.current); - const childNode = nullthrows(childRef.current); - - Fantom.runTask(() => { - root.render( - , - ); - }); - - const callback = jest.fn(); - parentNode.measureLayout(childNode, callback); - - expect(callback).not.toHaveBeenCalled(); - }); - - it('unmounted.measureLayout(unmounted, ...) does nothing', () => { - const root = Fantom.createRoot(); - const parentRef = createRef(); - const childRef = createRef(); - - Fantom.runTask(() => { - root.render( - - - , - ); - }); - - const parentNode = nullthrows(parentRef.current); - const childNode = nullthrows(childRef.current); - - Fantom.runTask(() => { - root.render(<>); - }); - - const callback = jest.fn(); - childNode.measureLayout(parentNode, callback); - - expect(callback).not.toHaveBeenCalled(); - }); - }); - - describe('setNativeProps', () => { - it('should propagate changes to the host component', () => { - const root = Fantom.createRoot(); - const nodeRef = createRef(); - - Fantom.runTask(() => { - root.render(); - }); - - expect( - root - .getRenderedOutput({ - props: ['testID'], - }) - .toJSX(), - ).toEqual(); - - const element = nullthrows(nodeRef.current); - - Fantom.runTask(() => { - element.setNativeProps({testID: 'second test id'}); - }); - - expect( - root - .getRenderedOutput({ - props: ['testID'], - }) - .toJSX(), - ).toEqual(); - }); - - // TODO: delete when NativeDOM.setNativeProps is NOT nullable. - // This logic is to ensure compatibility with old app versions without the native module method. - if (isModern) { - let RawNativeDOM; - let originalSetNativeProps; - - beforeAll(() => { - RawNativeDOM = nullthrows(getRawNativeDOMForTests()); - originalSetNativeProps = RawNativeDOM.setNativeProps; - }); - - beforeEach(() => { - // $FlowExpectedError[cannot-write] - RawNativeDOM.setNativeProps = originalSetNativeProps; - }); - - it('should propagate changes to the host component (when NativeDOM.setNativeProps is not available)', () => { - // $FlowExpectedError[cannot-write] - RawNativeDOM.setNativeProps = null; - - expect(RawNativeDOM.setNativeProps).toBeNull(); - - const root = Fantom.createRoot(); - const nodeRef = createRef(); - - Fantom.runTask(() => { - root.render(); - }); - - expect( - root - .getRenderedOutput({ - props: ['testID'], - }) - .toJSX(), - ).toEqual(); - - const element = nullthrows(nodeRef.current); - - Fantom.runTask(() => { - element.setNativeProps({testID: 'second test id'}); - }); - - expect( - root - .getRenderedOutput({ - props: ['testID'], - }) - .toJSX(), - ).toEqual(); - }); - } - }); - }); -}