diff --git a/.github/actions/maestro-ios/action.yml b/.github/actions/maestro-ios/action.yml index 8e1a11c9a25..f7f3feac346 100644 --- a/.github/actions/maestro-ios/action.yml +++ b/.github/actions/maestro-ios/action.yml @@ -37,22 +37,23 @@ inputs: runs: using: composite + # Expects Node.js to be set up by the calling job. steps: + # Booting takes a couple of minutes; it finishes while the next steps run. + - name: Boot simulator + shell: bash + run: | + node .github/workflow-scripts/maestro-ios.js boot \ + "${{ inputs.device-model }}" \ + "${{ inputs.device-os }}" - name: Installing Maestro shell: bash run: export MAESTRO_VERSION=2.6.1; curl -Ls "https://get.maestro.mobile.dev" | bash - - name: Installing Maestro dependencies - shell: bash - run: | - brew tap facebook/fb - brew install facebook/fb/idb-companion - name: Set up JDK 17 uses: actions/setup-java@v5 with: java-version: '17' distribution: 'zulu' - - name: Setup node.js - uses: ./.github/actions/setup-node - name: Run yarn install uses: ./.github/actions/yarn-install - name: Start Metro in Debug diff --git a/.github/workflow-scripts/__tests__/maestro-android-test.js b/.github/workflow-scripts/__tests__/maestro-android-test.js index c07def601b2..bf353061d97 100644 --- a/.github/workflow-scripts/__tests__/maestro-android-test.js +++ b/.github/workflow-scripts/__tests__/maestro-android-test.js @@ -47,22 +47,16 @@ describe('Maestro Android runner', () => { }); it('excludes flows with matching tags', () => { - const releaseOnlyFlow = path.join( - temporaryDirectory, - 'android-release-only.yml', - ); + const releaseOnlyFlow = path.join(temporaryDirectory, 'release-only.yml'); const regularFlow = path.join(temporaryDirectory, 'regular.yml'); fs.writeFileSync( releaseOnlyFlow, - 'appId: x\ntags:\n - android-release-only\n---\n- launchApp\n', + 'appId: x\ntags:\n - release-only\n---\n- launchApp\n', ); fs.writeFileSync(regularFlow, 'appId: x\n---\n- launchApp\n'); expect( - filterFlowsByTags( - [releaseOnlyFlow, regularFlow], - ['android-release-only'], - ), + filterFlowsByTags([releaseOnlyFlow, regularFlow], ['release-only']), ).toEqual([regularFlow]); }); diff --git a/.github/workflow-scripts/maestro-ios.js b/.github/workflow-scripts/maestro-ios.js index fb2c26836d1..bbc870dd56b 100644 --- a/.github/workflow-scripts/maestro-ios.js +++ b/.github/workflow-scripts/maestro-ios.js @@ -25,6 +25,11 @@ node maestro-ios.js 2) { + throw new Error(`Invalid number of arguments.\n${usage}`); + } + launchSimulator(findAvailableSimulator(args[0] || null, args[1] || null)); +} + if (require.main === module) { - main().catch(error => { - console.error(error); - process.exitCode = 1; - }); + const args = process.argv.slice(2); + const command = args[0] === 'boot' ? () => boot(args.slice(1)) : main; + Promise.resolve() + .then(command) + .catch(error => { + console.error(error); + process.exitCode = 1; + }); } module.exports = { diff --git a/.github/workflows/e2e-android-rntester.yml b/.github/workflows/e2e-android-rntester.yml index f3ff2fc0dc9..ef91256ce37 100644 --- a/.github/workflows/e2e-android-rntester.yml +++ b/.github/workflows/e2e-android-rntester.yml @@ -74,7 +74,7 @@ jobs: # These timing-sensitive flows remain covered by the release APK. # Running the debug ARM64 APK through native translation can delay # React commits long enough for Maestro to miss taps and assertions. - exclude-tags: ${{ matrix.flavor == 'debug' && 'android-release-only' || '' }} + exclude-tags: ${{ matrix.flavor == 'debug' && 'release-only' || '' }} - name: Store per-flow test state if: always() uses: ./.github/actions/upload-artifact diff --git a/.github/workflows/e2e-ios-rntester.yml b/.github/workflows/e2e-ios-rntester.yml index 4fba45819de..9acfe081b7f 100644 --- a/.github/workflows/e2e-ios-rntester.yml +++ b/.github/workflows/e2e-ios-rntester.yml @@ -52,10 +52,9 @@ jobs: SHOULD_RUN=false fi echo "should-run=$SHOULD_RUN" >> "$GITHUB_OUTPUT" - - name: Setup Node.js - if: steps.test-state.outputs.should-run == 'true' - uses: ./.github/actions/setup-node + # First, so that a missing app fails the job before any other setup. - name: Download App + id: download-app if: steps.test-state.outputs.should-run == 'true' uses: ./.github/actions/download-artifact with: @@ -64,6 +63,9 @@ jobs: - name: Check downloaded folder content if: steps.test-state.outputs.should-run == 'true' run: ls -lR /tmp/RNTesterBuild + - name: Setup Node.js + if: steps.test-state.outputs.should-run == 'true' + uses: ./.github/actions/setup-node - name: Run E2E Tests id: run-tests if: steps.test-state.outputs.should-run == 'true' @@ -77,7 +79,11 @@ jobs: device-model: iPhone-17-Pro device-os: iOS-26-2 test-state-path: /tmp/maestro-ios-state/results.json - exclude-tags: android-only + # The `release-only` flows (FlatList maintainVisibleContentPosition) + # are half of the suite and test scroll behavior that doesn't depend + # on the build flavor. Nearly 90% of their failed attempts were in + # Debug, so like on Android they only run against Release. + exclude-tags: ${{ matrix.flavor == 'Debug' && 'android-only,release-only' || 'android-only' }} - name: Store per-flow test state if: always() uses: ./.github/actions/upload-artifact @@ -91,14 +97,19 @@ jobs: if: always() shell: bash # passed: every flow passed. - # failed: a flow failed all of its attempts; retrying the job would only - # repeat the same failure. + # failed: a flow failed all of its attempts, or the app to test is + # missing; retrying the job would only repeat the same failure. # incomplete: the job stopped before running every flow (runner or # setup failure); a retry can pick up the remaining flows. run: | STATE=/tmp/maestro-ios-state/results.json if [[ "${{ steps.test-state.outputs.should-run }}" == "false" || "${{ steps.run-tests.outcome }}" == "success" ]]; then RESULT=passed + elif [[ "${{ steps.download-app.outcome }}" == "failure" ]]; then + # test_ios_rntester uses continue-on-error, so it reports success + # even when the build that uploads this app failed. + echo "::error::RNTesterApp-NewArch-${{ matrix.flavor }} is missing. Check the test_ios_rntester (${{ matrix.flavor }}, frameworks: false) job." + RESULT=failed elif [[ -f "$STATE" ]] && jq -e 'any(.flows[]; .status == "failed")' "$STATE" > /dev/null; then RESULT=failed else diff --git a/packages/rn-tester/.maestro/flatlist-append-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-append-maintainvisible.yml index 47a3097fe68..e477f1ce16f 100644 --- a/packages/rn-tester/.maestro/flatlist-append-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-append-maintainvisible.yml @@ -2,7 +2,7 @@ # Appending items should NOT affect scroll offset (delta ~0) appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-complex-mutations-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-complex-mutations-maintainvisible.yml index 72781f1a9a3..188ccf4570b 100644 --- a/packages/rn-tester/.maestro/flatlist-complex-mutations-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-complex-mutations-maintainvisible.yml @@ -2,7 +2,7 @@ # Tests prepend + append + delete in sequence appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp - setOrientation: portrait diff --git a/packages/rn-tester/.maestro/flatlist-delete-anchor-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-delete-anchor-maintainvisible.yml index 7e6dd3a5f6c..162f8c1f812 100644 --- a/packages/rn-tester/.maestro/flatlist-delete-anchor-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-delete-anchor-maintainvisible.yml @@ -2,7 +2,7 @@ # When the anchor item (first visible) is deleted, MVCP should select a new anchor appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp - setOrientation: portrait diff --git a/packages/rn-tester/.maestro/flatlist-delete-middle-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-delete-middle-maintainvisible.yml index 8ca036af020..03620b2dd22 100644 --- a/packages/rn-tester/.maestro/flatlist-delete-middle-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-delete-middle-maintainvisible.yml @@ -2,7 +2,7 @@ # When items are deleted from the middle, MVCP should adjust scroll offset appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp - setOrientation: portrait diff --git a/packages/rn-tester/.maestro/flatlist-empty-list-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-empty-list-maintainvisible.yml index be79c35b7a6..f2c549e5355 100644 --- a/packages/rn-tester/.maestro/flatlist-empty-list-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-empty-list-maintainvisible.yml @@ -4,7 +4,7 @@ # correction. appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-first-prepend-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-first-prepend-maintainvisible.yml index 8305cbc874c..b7d032a94b9 100644 --- a/packages/rn-tester/.maestro/flatlist-first-prepend-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-first-prepend-maintainvisible.yml @@ -2,7 +2,7 @@ # Single prepend with fixed-height items: delta should be ~44px (40px height + 4px margin) appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-horizontal-add50-reset-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-horizontal-add50-reset-maintainvisible.yml index 04d9c3c2072..44b9e863afa 100644 --- a/packages/rn-tester/.maestro/flatlist-horizontal-add50-reset-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-horizontal-add50-reset-maintainvisible.yml @@ -3,7 +3,7 @@ # reset returns offset to 0. appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp - setOrientation: portrait diff --git a/packages/rn-tester/.maestro/flatlist-horizontal-inverted-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-horizontal-inverted-maintainvisible.yml index c53fcd8db23..bc629d5155d 100644 --- a/packages/rn-tester/.maestro/flatlist-horizontal-inverted-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-horizontal-inverted-maintainvisible.yml @@ -3,7 +3,7 @@ # In inverted mode, prepending adds items to the right end appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-horizontal-inverted-recycle-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-horizontal-inverted-recycle-maintainvisible.yml index ce9234cae13..2d8e39090dd 100644 --- a/packages/rn-tester/.maestro/flatlist-horizontal-inverted-recycle-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-horizontal-inverted-recycle-maintainvisible.yml @@ -2,7 +2,7 @@ # Items are 200px wide + 4px margin = 204px each appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-horizontal-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-horizontal-maintainvisible.yml index 8258d117230..e625f861ace 100644 --- a/packages/rn-tester/.maestro/flatlist-horizontal-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-horizontal-maintainvisible.yml @@ -2,7 +2,7 @@ # Horizontal: items are 200px wide, delta should be ~204px (200px + 4px margin) appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-horizontal-recycle-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-horizontal-recycle-maintainvisible.yml index c63531017e3..b80c7f975b6 100644 --- a/packages/rn-tester/.maestro/flatlist-horizontal-recycle-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-horizontal-recycle-maintainvisible.yml @@ -2,7 +2,7 @@ # Items are 200px wide + 4px margin = 204px each appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-inverted-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-inverted-maintainvisible.yml index 28f585f95f4..132565d9cce 100644 --- a/packages/rn-tester/.maestro/flatlist-inverted-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-inverted-maintainvisible.yml @@ -3,7 +3,7 @@ # not logical order. Prepending shifts anchor view frame downward in both modes. appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-inverted-recycle-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-inverted-recycle-maintainvisible.yml index ded9fe4c987..ed0041bdedb 100644 --- a/packages/rn-tester/.maestro/flatlist-inverted-recycle-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-inverted-recycle-maintainvisible.yml @@ -4,7 +4,7 @@ # In inverted mode, items display in reverse order but MVCP delta behavior is the same appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-maintainvisible.yml index d0e05fc6287..f767288857c 100644 --- a/packages/rn-tester/.maestro/flatlist-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-maintainvisible.yml @@ -8,7 +8,7 @@ # - Delta should equal height of prepended items (~44px per item with margin) appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait @@ -90,7 +90,7 @@ tags: # Test that user scroll is not interrupted during prepend appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp - assertVisible: 'Components' diff --git a/packages/rn-tester/.maestro/flatlist-momentum-scroll-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-momentum-scroll-maintainvisible.yml index 820c6586db2..cb1baa52e5a 100644 --- a/packages/rn-tester/.maestro/flatlist-momentum-scroll-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-momentum-scroll-maintainvisible.yml @@ -3,7 +3,7 @@ # post-prepend. MVCP correction runs asynchronously in didMountItems. appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-orientation-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-orientation-maintainvisible.yml index 8ecefd77df5..5f1a5f00c25 100644 --- a/packages/rn-tester/.maestro/flatlist-orientation-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-orientation-maintainvisible.yml @@ -7,7 +7,7 @@ # to work correctly after the change. appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-prepend-delete-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-prepend-delete-maintainvisible.yml index ad116f7a61e..c42ca0bee0a 100644 --- a/packages/rn-tester/.maestro/flatlist-prepend-delete-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-prepend-delete-maintainvisible.yml @@ -5,7 +5,7 @@ # the first visible view, but re-ordering edge case is untested. appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-pull-to-refresh-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-pull-to-refresh-maintainvisible.yml index 31445c02d9a..a4d561a73bc 100644 --- a/packages/rn-tester/.maestro/flatlist-pull-to-refresh-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-pull-to-refresh-maintainvisible.yml @@ -2,7 +2,7 @@ # Simulates scroll-to-top then prepend (like pull-to-refresh with new items) appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp - setOrientation: portrait diff --git a/packages/rn-tester/.maestro/flatlist-rapid-prepends-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-rapid-prepends-maintainvisible.yml index c7434fb6a12..ac203a99c83 100644 --- a/packages/rn-tester/.maestro/flatlist-rapid-prepends-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-rapid-prepends-maintainvisible.yml @@ -3,7 +3,7 @@ # promptly, blocking render window updates. All prepends fired without waiting. appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-recycle-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-recycle-maintainvisible.yml index 16cb6cf3132..69072f4afc5 100644 --- a/packages/rn-tester/.maestro/flatlist-recycle-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-recycle-maintainvisible.yml @@ -3,7 +3,7 @@ # With windowSize=3, only ~3 pages of items are rendered appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-scrolltooffset-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-scrolltooffset-maintainvisible.yml index db298cc2b2f..c3736d81676 100644 --- a/packages/rn-tester/.maestro/flatlist-scrolltooffset-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-scrolltooffset-maintainvisible.yml @@ -4,7 +4,7 @@ # of the scrollTo target). appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-throttle-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-throttle-maintainvisible.yml index 5b0e4103f5e..02ffcf6c982 100644 --- a/packages/rn-tester/.maestro/flatlist-throttle-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-throttle-maintainvisible.yml @@ -2,7 +2,7 @@ # Throttle affects timing but not final delta: should be ~44px appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-variable-height-first-prepend-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-variable-height-first-prepend-maintainvisible.yml index 385b1d20952..2c69fd301fc 100644 --- a/packages/rn-tester/.maestro/flatlist-variable-height-first-prepend-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-variable-height-first-prepend-maintainvisible.yml @@ -2,7 +2,7 @@ # Single prepend with variable height: delta should be 28-112px appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait diff --git a/packages/rn-tester/.maestro/flatlist-variable-height-maintainvisible.yml b/packages/rn-tester/.maestro/flatlist-variable-height-maintainvisible.yml index da034ec4b2b..26b4d01ed96 100644 --- a/packages/rn-tester/.maestro/flatlist-variable-height-maintainvisible.yml +++ b/packages/rn-tester/.maestro/flatlist-variable-height-maintainvisible.yml @@ -2,7 +2,7 @@ # Delta should be between 28-112px (random height from [30,50,70,90,110]) appId: ${APP_ID} tags: - - android-release-only + - release-only --- - launchApp # Change to portrait