From eba89ccc99e2599d68946f8af4492f97303eabe8 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Wed, 23 Sep 2026 03:08:07 -0700 Subject: [PATCH] Add deep-link Maestro flows for RNTester lists (#58481) Summary: Pull Request resolved: https://github.com/react/react-native/pull/58481 Add Maestro coverage for FlatList and SectionList viewability behavior using direct RNTester example deep links. Use explicit gestures inside the virtualized lists and wait for the callback output. Add nonvisual, route-specific readiness IDs so successive deep links cannot match the previously mounted list. Changelog: [Internal] Reviewed By: Abbondanzo Differential Revision: D119484760 fbshipit-source-id: 4fccf77355265728b7574f53600105de7ff34daa --- .../.maestro/flatlist-viewability.yml | 51 ++++++++++++++ .../.maestro/sectionlist-viewability.yml | 66 +++++++++++++++++++ .../examples/FlatList/BaseFlatListExample.js | 3 +- .../FlatList-BaseOnViewableItemsChanged.js | 4 ++ .../SectionList-BaseOnViewableItemsChanged.js | 4 ++ .../SectionList/SectionListBaseExample.js | 3 +- 6 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 packages/rn-tester/.maestro/flatlist-viewability.yml create mode 100644 packages/rn-tester/.maestro/sectionlist-viewability.yml diff --git a/packages/rn-tester/.maestro/flatlist-viewability.yml b/packages/rn-tester/.maestro/flatlist-viewability.yml new file mode 100644 index 00000000000..d40f2633a11 --- /dev/null +++ b/packages/rn-tester/.maestro/flatlist-viewability.yml @@ -0,0 +1,51 @@ +appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebook.react.uiapp +--- +- launchApp +- stopApp +- openLink: rntester://example/FlatListExample/onViewableItemsChanged-horizontal-noWaitForInteraction +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'flat-list-viewability-horizontal-no-wait' + timeout: 120000 +- assertVisible: + id: 'output' + text: '.*Pizza, Burger.*' +- openLink: rntester://example/FlatListExample/onViewableItemsChanged-horizontal-waitForInteraction +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'flat-list-viewability-horizontal-wait' + timeout: 30000 +- scrollUntilVisible: + element: + text: 'Cheesecake' + direction: RIGHT + speed: 60 + visibilityPercentage: 50 + centerElement: true + timeout: 60000 +- extendedWaitUntil: + visible: + id: 'output' + text: '.*Cheesecake.*' + timeout: 30000 +- openLink: rntester://example/FlatListExample/onViewableItemsChanged-waitForInteraction +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'flat-list-viewability-vertical-wait' + timeout: 30000 +- scrollUntilVisible: + element: + text: 'Cheesecake' + direction: DOWN + speed: 60 + visibilityPercentage: 50 + centerElement: true + timeout: 60000 +- extendedWaitUntil: + visible: + id: 'output' + text: '.*Cheesecake, Brownie.*' + timeout: 30000 diff --git a/packages/rn-tester/.maestro/sectionlist-viewability.yml b/packages/rn-tester/.maestro/sectionlist-viewability.yml new file mode 100644 index 00000000000..60e71bcbb06 --- /dev/null +++ b/packages/rn-tester/.maestro/sectionlist-viewability.yml @@ -0,0 +1,66 @@ +appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebook.react.uiapp +--- +- launchApp +- stopApp +- openLink: rntester://example/SectionList/onEndReached +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'start_test' + timeout: 120000 +- tapOn: + id: 'start_test' +- assertVisible: + id: 'output' + text: 'onEndReached' +- openLink: rntester://example/SectionList/onViewableItemsChanged-horizontal-noWaitForInteraction +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'section-list-viewability-horizontal-no-wait' + timeout: 30000 +- assertVisible: + id: 'output' + text: '.*Pizza.*' +- openLink: rntester://example/SectionList/onViewableItemsChanged-horizontal-waitForInteraction +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'section-list-viewability-horizontal-wait' + timeout: 30000 +- scrollUntilVisible: + element: + text: 'Brownie' + direction: RIGHT + speed: 60 + visibilityPercentage: 50 + centerElement: true + timeout: 60000 +- extendedWaitUntil: + visible: + id: 'output' + text: '.*Cheesecake.*' + timeout: 30000 +- runFlow: + when: + platform: Android + commands: + - openLink: rntester://example/SectionList/onViewableItemsChanged-waitForInteraction + - runFlow: ./helpers/confirm-open-link.yml + - extendedWaitUntil: + visible: + id: 'section-list-viewability-vertical-wait' + timeout: 30000 + - scrollUntilVisible: + element: + text: 'Brownie' + direction: DOWN + speed: 60 + visibilityPercentage: 50 + centerElement: true + timeout: 60000 + - extendedWaitUntil: + visible: + id: 'output' + text: '.*Cheesecake.*' + timeout: 30000 diff --git a/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js b/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js index 11fffe822a9..1c77581facf 100644 --- a/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js +++ b/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js @@ -61,6 +61,7 @@ const Item = ({item, separators}: ListRenderItemInfo) => { type Props = Readonly<{ exampleProps: Partial>, + exampleTestID?: ?string, onTest?: ?() => void, testLabel?: ?string, testOutput?: ?string, @@ -72,7 +73,7 @@ const BaseFlatListExample: component( ...props: Props ) = ({ref, ...props}: {ref: React.RefSetter>, ...Props}) => { return ( - + {props.testOutput != null ? ( diff --git a/packages/rn-tester/js/examples/FlatList/FlatList-BaseOnViewableItemsChanged.js b/packages/rn-tester/js/examples/FlatList/FlatList-BaseOnViewableItemsChanged.js index 8c83d2a6938..94022c335ab 100644 --- a/packages/rn-tester/js/examples/FlatList/FlatList-BaseOnViewableItemsChanged.js +++ b/packages/rn-tester/js/examples/FlatList/FlatList-BaseOnViewableItemsChanged.js @@ -29,6 +29,9 @@ export function FlatList_BaseOnViewableItemsChanged(props: { waitForInteraction?: ?boolean, }): React.Node { const {offScreen, horizontal, useScrollRefScroll, waitForInteraction} = props; + const exampleTestID = `flat-list-viewability-${ + horizontal === true ? 'horizontal' : 'vertical' + }-${waitForInteraction === true ? 'wait' : 'no-wait'}`; const [output, setOutput] = useState(''); const onViewableItemsChanged = useCallback( (info: { @@ -66,6 +69,7 @@ export function FlatList_BaseOnViewableItemsChanged(props: { {offScreen === true ? : null} diff --git a/packages/rn-tester/js/examples/SectionList/SectionList-BaseOnViewableItemsChanged.js b/packages/rn-tester/js/examples/SectionList/SectionList-BaseOnViewableItemsChanged.js index add7f95b125..69e459970f3 100644 --- a/packages/rn-tester/js/examples/SectionList/SectionList-BaseOnViewableItemsChanged.js +++ b/packages/rn-tester/js/examples/SectionList/SectionList-BaseOnViewableItemsChanged.js @@ -27,6 +27,9 @@ export function SectionList_BaseOnViewableItemsChanged(props: { waitForInteraction?: ?boolean, }): React.Node { const {offScreen, horizontal, useScrollRefScroll, waitForInteraction} = props; + const exampleTestID = `section-list-viewability-${ + horizontal === true ? 'horizontal' : 'vertical' + }-${waitForInteraction === true ? 'wait' : 'no-wait'}`; const [output, setOutput] = useState(''); const viewabilityConfig: ViewabilityConfig = { ...BASE_VIEWABILITY_CONFIG, @@ -59,6 +62,7 @@ export function SectionList_BaseOnViewableItemsChanged(props: { {offScreen === true ? : null} diff --git a/packages/rn-tester/js/examples/SectionList/SectionListBaseExample.js b/packages/rn-tester/js/examples/SectionList/SectionListBaseExample.js index d4de9daae6c..21c42c0ca64 100644 --- a/packages/rn-tester/js/examples/SectionList/SectionListBaseExample.js +++ b/packages/rn-tester/js/examples/SectionList/SectionListBaseExample.js @@ -67,6 +67,7 @@ const Item = ({item, section, separators}) => { type Props = Readonly<{ // $FlowFixMe[unclear-type] exampleProps: Partial>>, + exampleTestID?: ?string, onTest?: ?() => void, testLabel?: ?string, testOutput?: ?string, @@ -86,7 +87,7 @@ const SectionListBaseExample: component( ...Props, }): React.Node => { return ( - + {props.testOutput != null ? (