From f200b70354562c82ca8a9fa2d4f11852fd17ab9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Fri, 21 Feb 2025 03:21:07 -0800 Subject: [PATCH] Add Fantom test for synchronous state updates + UI consistency (#49572) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/49572 Changelog: [internal] This adds a test to verify the behavior of 2 feature flags: synchronous state updates and UI consistency. Reviewed By: javache Differential Revision: D69932313 fbshipit-source-id: 341cfff3fa533503a293f6ccd0282442ba63d430 --- .../__tests__/UIConsistency-itest.js | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 packages/react-native/src/private/renderer/consistency/__tests__/UIConsistency-itest.js diff --git a/packages/react-native/src/private/renderer/consistency/__tests__/UIConsistency-itest.js b/packages/react-native/src/private/renderer/consistency/__tests__/UIConsistency-itest.js new file mode 100644 index 00000000000..fa82907105a --- /dev/null +++ b/packages/react-native/src/private/renderer/consistency/__tests__/UIConsistency-itest.js @@ -0,0 +1,158 @@ +/** + * 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 + * @fantom_flags enableAccessToHostTreeInFabric:true + * @fantom_flags enableUIConsistency:true + * @fantom_flags enableSynchronousStateUpdates:true + */ + +import ScrollView from '../../../../../Libraries/Components/ScrollView/ScrollView'; +import Text from '../../../../../Libraries/Text/Text'; +import ensureInstance from '../../../utilities/ensureInstance'; +import ReactNativeElement from '../../../webapis/dom/nodes/ReactNativeElement'; +import Fantom from '@react-native/fantom'; +import * as React from 'react'; +import {useLayoutEffect} from 'react'; + +import '../../../../../Libraries/Core/InitializeCore'; + +describe('UIConsistency', () => { + it('should provide consistent data from the tree within the same synchronous function', () => { + const root = Fantom.createRoot(); + + let maybeScrollViewNode; + + Fantom.runTask(() => { + root.render( + { + maybeScrollViewNode = node; + }} + style={{height: 100}} + contentContainerStyle={{height: 1000}} + />, + ); + }); + + const scrollViewNode = ensureInstance( + maybeScrollViewNode, + ReactNativeElement, + ); + + Fantom.runTask(() => { + expect(scrollViewNode.scrollTop).toBe(0); + + Fantom.runOnUIThread(() => { + Fantom.scrollTo(scrollViewNode, {x: 0, y: 100}); + }); + + expect(scrollViewNode.scrollTop).toBe(0); + }); + + expect(scrollViewNode.scrollTop).toBe(100); + }); + + it('should provide up-to-date data in the first access to the tree', () => { + const root = Fantom.createRoot(); + + let maybeScrollViewNode; + + Fantom.runTask(() => { + root.render( + { + maybeScrollViewNode = node; + }} + style={{height: 100}} + contentContainerStyle={{height: 1000}} + />, + ); + }); + + const scrollViewNode = ensureInstance( + maybeScrollViewNode, + ReactNativeElement, + ); + + Fantom.runTask(() => { + // We never accessed the tree before the state update + + Fantom.runOnUIThread(() => { + Fantom.scrollTo(scrollViewNode, {x: 0, y: 100}); + }); + + // The value is up-to-date immediately + expect(scrollViewNode.scrollTop).toBe(100); + }); + }); + + it('should provide up-to-date data after commit', () => { + const root = Fantom.createRoot(); + + let maybeScrollViewNode; + + function InnerComponent(props: { + text: string, + renderFn?: () => void, + effectFn?: () => void, + }): React.Node { + const {text, renderFn, effectFn} = props; + + renderFn?.(); + + useLayoutEffect(() => { + effectFn?.(); + }, [text, effectFn]); + + return {text}; + } + + Fantom.runTask(() => { + root.render( + { + maybeScrollViewNode = node; + }} + style={{height: 100}} + contentContainerStyle={{height: 1000}}> + + , + ); + }); + + const scrollViewNode = ensureInstance( + maybeScrollViewNode, + ReactNativeElement, + ); + + Fantom.runTask(() => { + root.render( + + { + expect(scrollViewNode.scrollTop).toBe(0); + + Fantom.runOnUIThread(() => { + Fantom.scrollTo(scrollViewNode, {x: 0, y: 100}); + }); + + expect(scrollViewNode.scrollTop).toBe(0); + }} + effectFn={() => { + expect(scrollViewNode.scrollTop).toBe(100); + }} + /> + , + ); + }); + }); +});