From eb40498b83c0f148a83a9bc67ed21ed584920d63 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Fri, 18 Sep 2026 09:06:09 -0700 Subject: [PATCH 1/3] Add deep-link Maestro flows for RNTester basics (#58480) Summary: Add Maestro coverage for portable RNTester interaction scenarios using direct example deep links. The flows cover alerts, animations, appearance, filters, text input, and touchables without screenshot baselines. Changelog: [Internal] Differential Revision: D119484759 --- .github/workflows/maestro-cloud-rntester.yml | 4 +- .github/workflows/test-all.yml | 2 +- packages/rn-tester/.maestro/alert.yml | 24 ++++++++++ .../.maestro/animated-fade-in-view.yml | 20 ++++++++ packages/rn-tester/.maestro/appearance.yml | 9 ++++ .../.maestro/filter-animated-blur.yml | 14 ++++++ .../.maestro/helpers/confirm-open-link.yml | 9 ++++ .../.maestro/textinput-uncontrolled.yml | 18 ++++++++ packages/rn-tester/.maestro/touchable.yml | 46 +++++++++++++++++++ 9 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 packages/rn-tester/.maestro/alert.yml create mode 100644 packages/rn-tester/.maestro/animated-fade-in-view.yml create mode 100644 packages/rn-tester/.maestro/appearance.yml create mode 100644 packages/rn-tester/.maestro/filter-animated-blur.yml create mode 100644 packages/rn-tester/.maestro/helpers/confirm-open-link.yml create mode 100644 packages/rn-tester/.maestro/textinput-uncontrolled.yml create mode 100644 packages/rn-tester/.maestro/touchable.yml diff --git a/.github/workflows/maestro-cloud-rntester.yml b/.github/workflows/maestro-cloud-rntester.yml index 7f245bbcbebf..7c2afc24ea8b 100644 --- a/.github/workflows/maestro-cloud-rntester.yml +++ b/.github/workflows/maestro-cloud-rntester.yml @@ -46,7 +46,7 @@ jobs: test: name: Maestro Cloud ${{ inputs.platform }} RNTester runs-on: ubuntu-latest - timeout-minutes: 75 + timeout-minutes: 105 steps: - name: Checkout uses: actions/checkout@v6 @@ -84,7 +84,7 @@ jobs: env: | APP_ID=${{ inputs.app-id }} maestro-cli-version: 2.6.1 - timeout: 60 + timeout: 90 - name: Check Maestro Cloud result if: always() env: diff --git a/.github/workflows/test-all.yml b/.github/workflows/test-all.yml index c138cd5730fa..97083115acac 100644 --- a/.github/workflows/test-all.yml +++ b/.github/workflows/test-all.yml @@ -186,7 +186,7 @@ jobs: app-id: com.meta.RNTester.localDevelopment device-model: iPhone-17-Pro device-os: iOS-26-2 - exclude-tags: android-only + exclude-tags: android-only,local-screenshot-baseline secrets: inherit test_e2e_ios_rntester: diff --git a/packages/rn-tester/.maestro/alert.yml b/packages/rn-tester/.maestro/alert.yml new file mode 100644 index 000000000000..a8892ddc5da6 --- /dev/null +++ b/packages/rn-tester/.maestro/alert.yml @@ -0,0 +1,24 @@ +appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebook.react.uiapp +--- +- launchApp +- stopApp +- openLink: rntester://example/AlertExample/default-button +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'alert-with-default-button' + timeout: 120000 +- tapOn: + id: 'alert-with-default-button' +- assertVisible: 'An external USB drive has been detected!' +- tapOn: 'OK' +- openLink: rntester://example/AlertExample/three-buttons +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'alert-with-three-buttons' + timeout: 30000 +- tapOn: + id: 'alert-with-three-buttons' +- assertVisible: 'Do you want to save your changes?' +- tapOn: 'Cancel' diff --git a/packages/rn-tester/.maestro/animated-fade-in-view.yml b/packages/rn-tester/.maestro/animated-fade-in-view.yml new file mode 100644 index 000000000000..d71c1b2d2ceb --- /dev/null +++ b/packages/rn-tester/.maestro/animated-fade-in-view.yml @@ -0,0 +1,20 @@ +appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebook.react.uiapp +--- +- launchApp +- stopApp +- openLink: rntester://example/Animated/fadeInView +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'toggle-button' + timeout: 120000 +- assertVisible: + id: 'fade-in-view' +- tapOn: + id: 'toggle-button' +- assertNotVisible: + id: 'fade-in-view' +- tapOn: + id: 'toggle-button' +- assertVisible: + id: 'fade-in-view' diff --git a/packages/rn-tester/.maestro/appearance.yml b/packages/rn-tester/.maestro/appearance.yml new file mode 100644 index 000000000000..18a1f6cc4d8f --- /dev/null +++ b/packages/rn-tester/.maestro/appearance.yml @@ -0,0 +1,9 @@ +appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebook.react.uiapp +--- +- launchApp +- stopApp +- openLink: rntester://example/AppearanceExample +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: 'useColorScheme hook' + timeout: 120000 diff --git a/packages/rn-tester/.maestro/filter-animated-blur.yml b/packages/rn-tester/.maestro/filter-animated-blur.yml new file mode 100644 index 000000000000..158794ca7988 --- /dev/null +++ b/packages/rn-tester/.maestro/filter-animated-blur.yml @@ -0,0 +1,14 @@ +appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebook.react.uiapp +--- +- launchApp +- stopApp +- openLink: rntester://example/Filter/animated-blur +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'animated-blur-button' + timeout: 120000 +- tapOn: + id: 'animated-blur-button' +- assertVisible: + id: 'animated-blur-view' diff --git a/packages/rn-tester/.maestro/helpers/confirm-open-link.yml b/packages/rn-tester/.maestro/helpers/confirm-open-link.yml new file mode 100644 index 000000000000..1fa66d36dbcf --- /dev/null +++ b/packages/rn-tester/.maestro/helpers/confirm-open-link.yml @@ -0,0 +1,9 @@ +appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebook.react.uiapp +--- +- runFlow: + when: + platform: iOS + commands: + - tapOn: + text: 'Open' + optional: true diff --git a/packages/rn-tester/.maestro/textinput-uncontrolled.yml b/packages/rn-tester/.maestro/textinput-uncontrolled.yml new file mode 100644 index 000000000000..3e992b2acb3a --- /dev/null +++ b/packages/rn-tester/.maestro/textinput-uncontrolled.yml @@ -0,0 +1,18 @@ +appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebook.react.uiapp +--- +- launchApp +- stopApp +- openLink: rntester://example/TextInput/uncontrolledComponent +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'uncontrolled-textinput' + timeout: 120000 +- tapOn: + id: 'uncontrolled-textinput' +- eraseText +- inputText: 'New value' +- hideKeyboard +- assertVisible: + id: 'uncontrolled-textinput' + text: 'New value' diff --git a/packages/rn-tester/.maestro/touchable.yml b/packages/rn-tester/.maestro/touchable.yml new file mode 100644 index 000000000000..ae2dcdb6ae68 --- /dev/null +++ b/packages/rn-tester/.maestro/touchable.yml @@ -0,0 +1,46 @@ +appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebook.react.uiapp +--- +- launchApp +- stopApp +- openLink: rntester://example/TouchableExample/feedback-events +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'touchable_feedback_events_button' + timeout: 120000 +- tapOn: + id: 'touchable_feedback_events_button' +- assertVisible: + id: 'touchable_feedback_events_console' +- openLink: rntester://example/TouchableExample/highlight +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'touchable_highlight_text_button' + timeout: 30000 +- tapOn: + id: 'touchable_highlight_text_button' +- assertVisible: + id: 'touchable_highlight_console' + text: 'TouchableHighlight onPress' +- openLink: rntester://example/TouchableExample/hit-slop +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'touchable_hit_slop_button' + timeout: 30000 +- tapOn: + id: 'touchable_hit_slop_button' +- assertVisible: + id: 'touchable_hit_slop' +- openLink: rntester://example/TouchableExample/without-feedback +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'touchable_without_feedback_button' + timeout: 30000 +- tapOn: + id: 'touchable_without_feedback_button' +- assertVisible: + id: 'touchable_without_feedback_console' + text: 'TouchableWithoutFeedback onPress' From ac7a9bb58c62bae275f8299968fddec6878582cd Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Fri, 18 Sep 2026 09:06:09 -0700 Subject: [PATCH 2/3] Add deep-link Maestro flows for RNTester lists (#58481) Summary: 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] Differential Revision: D119484760 --- .../.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 000000000000..d40f2633a118 --- /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 000000000000..575ea2f7cb01 --- /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: 'Cheesecake' + 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: 'Cheesecake' + 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 11fffe822a9c..1c77581facfd 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 fb555cfe99a9..0d967cb3deed 100644 --- a/packages/rn-tester/js/examples/FlatList/FlatList-BaseOnViewableItemsChanged.js +++ b/packages/rn-tester/js/examples/FlatList/FlatList-BaseOnViewableItemsChanged.js @@ -32,6 +32,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: {changed: Array, viewableItems: Array, ...}) => @@ -65,6 +68,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 bb075043f438..bfebad3f0a9e 100644 --- a/packages/rn-tester/js/examples/SectionList/SectionList-BaseOnViewableItemsChanged.js +++ b/packages/rn-tester/js/examples/SectionList/SectionList-BaseOnViewableItemsChanged.js @@ -30,6 +30,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, @@ -62,6 +65,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 d4de9daae6c5..21c42c0ca641 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 ? ( From 3c3ef480917359838043b843640033f2710a3ce4 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Fri, 18 Sep 2026 09:06:09 -0700 Subject: [PATCH 3/3] Deep-link RNTester Pressable and Modal Maestro flows (#58482) Summary: Update the existing Pressable and Modal Maestro flows to open their RNTester examples directly. Extend the Pressable flow to cover content presses, feedback events, hit slop, and text presses. Changelog: [Internal] Differential Revision: D119484761 --- packages/rn-tester/.maestro/modal.yml | 31 ++++++------ packages/rn-tester/.maestro/pressable.yml | 59 ++++++++++++++--------- 2 files changed, 52 insertions(+), 38 deletions(-) diff --git a/packages/rn-tester/.maestro/modal.yml b/packages/rn-tester/.maestro/modal.yml index e3b7bef53886..63dc3149cfcd 100644 --- a/packages/rn-tester/.maestro/modal.yml +++ b/packages/rn-tester/.maestro/modal.yml @@ -1,25 +1,24 @@ appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebook.react.uiapp --- - launchApp -# Wait for the JS bundle to render the landing screen instead of asserting -# immediately, which races app cold start on CI. +- stopApp +- openLink: rntester://example/Modal/onShow +- runFlow: ./helpers/confirm-open-link.yml - extendedWaitUntil: - visible: 'Components' - timeout: 30000 -- scrollUntilVisible: - element: - id: 'Modal' - direction: DOWN - speed: 60 + visible: + id: 'open-modal' + timeout: 120000 - tapOn: - id: 'Modal' + id: 'open-modal' - assertVisible: - text: 'Show Modal' -- tapOn: - text: 'Show Modal' + id: 'dismiss-modal' - assertVisible: - text: 'Close' + id: 'modal-on-show-count' + text: 'onShow is called 1 times' - tapOn: - text: 'Close' + id: 'dismiss-modal' +- assertNotVisible: + id: 'dismiss-modal' - assertVisible: - text: 'Show Modal' + id: 'on-show-count' + text: 'onShow is called 1 times' diff --git a/packages/rn-tester/.maestro/pressable.yml b/packages/rn-tester/.maestro/pressable.yml index 86a8b2b00231..c38d98c7d116 100644 --- a/packages/rn-tester/.maestro/pressable.yml +++ b/packages/rn-tester/.maestro/pressable.yml @@ -1,37 +1,31 @@ appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebook.react.uiapp --- -- runFlow: ./helpers/launch-app-and-search.yml -- inputText: - text: 'Pressable' -- assertVisible: - id: 'Pressable' -- tapOn: - id: 'Pressable' -- assertVisible: - text: 'Change content based on Press' -- assertVisible: - text: 'Change style based on Press' +- launchApp +- stopApp +- openLink: rntester://example/PressableExample/content-press +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'one_press_me_button' + timeout: 120000 - tapOn: id: 'one_press_me_button' - assertVisible: + id: 'pressable_press_console' text: 'onPress' - tapOn: id: 'one_press_me_button' - assertVisible: + id: 'pressable_press_console' text: '2x onPress' -- scrollUntilVisible: - element: - id: 'pressable_feedback_events' - direction: DOWN - speed: 10 - visibilityPercentage: 100 +- openLink: rntester://example/PressableExample/feedback-events +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'pressable_feedback_events_button' + timeout: 30000 - tapOn: id: 'pressable_feedback_events_button' -- scrollUntilVisible: - element: - text: 'pressIn' - direction: DOWN - speed: 10 - assertVisible: text: 'press' - assertVisible: @@ -44,3 +38,24 @@ appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebo text: 'longPress' - assertVisible: text: 'pressOut' +- openLink: rntester://example/PressableExample/hit-slop +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'pressable_hit_slop_button' + timeout: 30000 +- tapOn: + id: 'pressable_hit_slop_button' +- assertVisible: + id: 'pressable_hit_slop' +- openLink: rntester://example/PressableExample/text-press +- runFlow: ./helpers/confirm-open-link.yml +- extendedWaitUntil: + visible: + id: 'tappable_text' + timeout: 30000 +- tapOn: + id: 'tappable_text' +- assertVisible: + id: 'tappable_text_console' + text: 'text onPress'