diff --git a/.github/actions/maestro-ios/action.yml b/.github/actions/maestro-ios/action.yml index b5e4036d6280..299443efbdfb 100644 --- a/.github/actions/maestro-ios/action.yml +++ b/.github/actions/maestro-ios/action.yml @@ -1,5 +1,5 @@ name: Maestro E2E iOS -description: Runs E2E Tests on iOS using Maestro +description: Runs E2E Tests on iOS using Maestro against a remote simulator via sim-remote inputs: app-path: required: true @@ -33,11 +33,14 @@ runs: - 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: Install sim-remote + uses: software-mansion-labs/argent-cloud-actions/install@v1 + - name: Acquire remote simulator + # Credentials come from the calling job's env. The machine is released + # by the action's post step, even if the job fails or is cancelled. + uses: software-mansion-labs/argent-cloud-actions/acquire@v1 + with: + api-key: ${{ env.SIM_ROUTER_API_KEY }} - name: Set up JDK 17 uses: actions/setup-java@v5 with: @@ -57,6 +60,10 @@ runs: cd ${{ inputs.working-directory }} yarn start & sleep 5 # to give metro time to load + - name: Install Maestro shims + # Puts xcrun, xcodebuild, plutil, ... stand-ins on PATH so stock Maestro + # drives the remote simulator, and exports SIM_REMOTE_BIN for the script. + uses: software-mansion-labs/argent-cloud-actions/maestro-shims@v1 - name: Run tests id: run-tests shell: bash diff --git a/.github/workflow-scripts/maestro-ios.js b/.github/workflow-scripts/maestro-ios.js index a9782ec6506f..2249081d3912 100644 --- a/.github/workflow-scripts/maestro-ios.js +++ b/.github/workflow-scripts/maestro-ios.js @@ -81,7 +81,24 @@ function installAppOnSimulator(appPath, udid) { childProcess.execSync(`xcrun simctl install "${udid}" "${appPath}"`); } +// A remote simulator's localhost is the remote host, so Metro needs a tunnel. +function exposeMetroToSimulator(udid) { + const simRemote = process.env.SIM_REMOTE_BIN; + if (!simRemote) { + return; + } + + console.log('Exposing local Metro port 8081 to the remote simulator'); + childProcess.execSync(`"${simRemote}" reverse start "${udid}" 8081`, { + stdio: 'inherit', + }); +} + function bringSimulatorInForeground() { + // Nothing to foreground on a remote simulator. + if (process.env.SIM_REMOTE_BIN) { + return; + } console.log('Bringing simulator in foreground'); childProcess.execSync('open -a simulator'); } @@ -266,6 +283,9 @@ async function main(args = process.argv.slice(2)) { launchSimulator(simulator); installAppOnSimulator(appPath, simulator.udid); bringSimulatorInForeground(); + if (isDebug) { + exposeMetroToSimulator(simulator.udid); + } await launchAppOnSimulator(appId, simulator.udid, isDebug); await executeFlows(appId, simulator.udid, maestroFlow, jsengine); console.log('Test finished'); diff --git a/.github/workflows/e2e-ios-rntester.yml b/.github/workflows/e2e-ios-rntester.yml index e0f69be743ae..8c7165cb2350 100644 --- a/.github/workflows/e2e-ios-rntester.yml +++ b/.github/workflows/e2e-ios-rntester.yml @@ -16,7 +16,9 @@ on: jobs: test: - runs-on: macos-26-large + runs-on: ubuntu-latest + env: + SIM_ROUTER_API_KEY: ${{ secrets.SIM_ROUTER_API_KEY }} outputs: status: ${{ steps.report-status.outputs.status }} strategy: @@ -45,7 +47,7 @@ jobs: maestro-flow: ./packages/rn-tester/.maestro/ flavor: ${{ matrix.flavor }} device-model: iPhone-17-Pro - device-os: iOS-26-2 + device-os: iOS-26-5 - name: Report status id: report-status if: ${{ always() && steps.run-tests.outcome == 'failure' }} diff --git a/.github/workflows/e2e-ios-templateapp.yml b/.github/workflows/e2e-ios-templateapp.yml index dbaaaa2b5666..10a13395e3e0 100644 --- a/.github/workflows/e2e-ios-templateapp.yml +++ b/.github/workflows/e2e-ios-templateapp.yml @@ -15,6 +15,8 @@ on: value: ${{ jobs.report.outputs.status }} jobs: + # Building the app needs Xcode, so it stays on a macOS runner. The E2E run + # itself drives a remote simulator via sim-remote, so it runs on Linux. build: runs-on: macos-26-large strategy: @@ -99,7 +101,9 @@ jobs: test: needs: build - runs-on: macos-26-large + runs-on: ubuntu-latest + env: + SIM_ROUTER_API_KEY: ${{ secrets.SIM_ROUTER_API_KEY }} outputs: status: ${{ steps.report-status.outputs.status }} strategy: diff --git a/packages/rn-tester/.maestro/scrollview-minindex-maintainvisible.yml b/packages/rn-tester/.maestro/scrollview-minindex-maintainvisible.yml index 0925eda988fe..23a5b3b5fef1 100644 --- a/packages/rn-tester/.maestro/scrollview-minindex-maintainvisible.yml +++ b/packages/rn-tester/.maestro/scrollview-minindex-maintainvisible.yml @@ -14,6 +14,7 @@ appId: ${APP_ID} id: 'ScrollViewMaintainVisibleContentPositionExample' direction: DOWN speed: 80 + timeout: 90000 - tapOn: id: 'ScrollViewMaintainVisibleContentPositionExample' - waitForAnimationToEnd: diff --git a/packages/rn-tester/.maestro/scrollview-threshold-maintainvisible.yml b/packages/rn-tester/.maestro/scrollview-threshold-maintainvisible.yml index c865c3cb26e9..972a14d388d4 100644 --- a/packages/rn-tester/.maestro/scrollview-threshold-maintainvisible.yml +++ b/packages/rn-tester/.maestro/scrollview-threshold-maintainvisible.yml @@ -14,6 +14,7 @@ appId: ${APP_ID} id: 'ScrollViewMaintainVisibleContentPositionExample' direction: DOWN speed: 80 + timeout: 90000 - tapOn: id: 'ScrollViewMaintainVisibleContentPositionExample' - waitForAnimationToEnd: