refactor: migrate get to the request-bound device runtime #6484
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: iOS | |
| on: | |
| pull_request: | |
| paths-ignore: | |
| - 'docs/**' | |
| - 'website/**' | |
| - 'README.md' | |
| - 'AGENTS.md' | |
| - 'CHANGELOG.md' | |
| - 'CONTEXT.md' | |
| - 'CONTRIBUTING.md' | |
| - 'LICENSE' | |
| - 'SECURITY.md' | |
| - '.github/actions/build-docs/action.yml' | |
| - '.github/workflows/deploy.yml' | |
| - '.github/workflows/pr-preview.yml' | |
| - '.github/workflows/pr-preview-cleanup.yml' | |
| # Routing (#1781 A9-2). Everything below is derived from the affected selector's | |
| # device-lane ownership (scripts/check-affected/device-lanes.ts): unit tests no device | |
| # lane runs, and trees another platform family owns. `pnpm check:gate-manifest` asserts | |
| # this list against the selector over every tracked path, both ways — a path the selector | |
| # routes to the iOS lane (or fails open on: tooling, workflows, package manifests) must not | |
| # appear here, and a path it classifies as another family's or as a unit test must. The | |
| # `push` trigger has no filter: main runs every lane unconditionally. | |
| - '**/*.md' | |
| - 'src/**/*.test.ts' | |
| - 'src/**/__tests__/**/*.ts' | |
| - 'packages/*/src/**/*.test.ts' | |
| - 'packages/*/src/**/__tests__/**/*.ts' | |
| - 'src/platforms/android/**' | |
| - 'src/platforms/harmonyos/**' | |
| - 'src/platforms/linux/**' | |
| - 'src/platforms/vega/**' | |
| - 'src/platforms/web/**' | |
| - 'packages/platform-android/src/**' | |
| - 'packages/platform-harmonyos/src/**' | |
| - 'packages/platform-linux/src/**' | |
| - 'packages/platform-vega/src/**' | |
| - 'packages/platform-web/src/**' | |
| - 'android/**' | |
| - 'linux/**' | |
| - 'test/integration/replays/android/**' | |
| - 'test/integration/replays/linux/**' | |
| - 'test/integration/replays/macos/**' | |
| - 'test/integration/android-**' | |
| - 'test/integration/smoke-android-**' | |
| - 'test/integration/smoke-web-**' | |
| push: | |
| branches: | |
| - main | |
| permissions: | |
| contents: read | |
| actions: read | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| smoke-ios: | |
| name: Smoke Tests | |
| runs-on: macos-26 | |
| timeout-minutes: 80 | |
| env: | |
| IOS_RUNTIME_VERSION: '26.2' | |
| AGENT_DEVICE_STATE_DIR: ${{ github.workspace }}/.tmp/agent-device-state | |
| AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH: ${{ github.workspace }}/.tmp/ios-runner-derived | |
| AGENT_DEVICE_IOS_PREPARE_TIMEOUT_MS: '420000' | |
| AGENT_DEVICE_XCUITEST_INCLUDE_UNIT_TESTS: '1' | |
| AGENT_DEVICE_IOS_APP_EVENT_URL_TEMPLATE: agent-device-test-app:///automation?event={event}&payload={payload} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Set fixture producer wait policy | |
| id: fixture-producer | |
| run: | | |
| set -euo pipefail | |
| WAIT_SECONDS=1800 | |
| if [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then | |
| echo "Fork pull request cannot publish the shared fixture artifact; a cache miss will build inline." | |
| WAIT_SECONDS=0 | |
| fi | |
| echo "wait-seconds=$WAIT_SECONDS" >> "$GITHUB_OUTPUT" | |
| - name: Setup toolchain | |
| uses: ./.github/actions/setup-node-pnpm | |
| with: | |
| cache-dependency-path: | | |
| pnpm-lock.yaml | |
| examples/test-app/pnpm-lock.yaml | |
| - name: Establish host focus canary | |
| id: host-focus-canary | |
| run: | | |
| set -euo pipefail | |
| osascript -e 'tell application "Finder" to activate' | |
| sleep 1 | |
| FRONTMOST="$(osascript -e 'tell application "System Events" to get bundle identifier of first application process whose frontmost is true')" | |
| test "$FRONTMOST" = "com.apple.finder" | |
| mkdir -p .tmp | |
| echo "$FRONTMOST" > .tmp/ios-host-focus-before | |
| - name: Restore and build iOS XCTest runner | |
| uses: ./.github/actions/setup-apple-runner-build | |
| with: | |
| derived-path: ${{ env.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH }} | |
| cache-key-prefix: ios-runner-prebuilt | |
| cache-key-suffix: -ios-${{ env.IOS_RUNTIME_VERSION }} | |
| gate: swift-runner-ios | |
| xcuitest-platform: ios | |
| xcuitest-destination: generic/platform=iOS Simulator | |
| - name: Boot iOS test simulator | |
| id: ios-simulator | |
| uses: ./.github/actions/boot-ios-test-simulator | |
| with: | |
| runtime-version: ${{ env.IOS_RUNTIME_VERSION }} | |
| preferred-device-name: iPhone 17 Pro | |
| - name: Run targeted iOS runner XCTest regressions | |
| run: | | |
| XCTESTRUN_PATH="$(find "$AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH/Build/Products" -maxdepth 1 -name '*.xctestrun' -print -quit)" | |
| test -n "$XCTESTRUN_PATH" | |
| xcodebuild test-without-building \ | |
| -xctestrun "$XCTESTRUN_PATH" \ | |
| -destination "platform=iOS Simulator,id=${{ steps.ios-simulator.outputs.simulator-udid }}" \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testSinglePointerFlingFallsBackToXCTestCoordinateDragWhenPrivateSynthesisFails \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testTypeWithoutResolvedInputReturnsTypedFailureBeforeDispatchingText \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testBareTypeUsesTappedInputWhenSoftwareKeyboardIsHidden \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testPenalizedCoordinateTapOnNonTextControlDoesNotAuthorizeBareType \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testBareDelayedTypeFailsWhenTappedInputDisappearsMidCommand \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testSynthesizedTextCommitProgressWalksExpectedPrefixOnly \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testTextEntryTapWitnessIsBoundToTargetIdentity \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testCoordinateTapTextInputProbeSkipsPenalizedXCTestChannel \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testCoordinateTextInputCandidateMustBeEnabledAndContainTheTouchPoint \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testQuerySelectorPrefersHittableMatchOverNonHittableDuplicate \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testActivateTargetSkipsForegroundAndActivatesNonForegroundApplication \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testMissingBundleCommandInvalidatesCompleteCachedTargetState \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testCachedTargetInvalidationClearsProcessBoundState \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testAlertResolutionCannotBypassRequestedDeadline \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testAlertAcceptTreatsOpenAsAffirmative \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testSystemModalProbeSliceSharesAndClampsToPlanDeadline \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testDispatchRecoverySkipsBookkeepingWhileXCTestChannelOccupied \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testBoundedSystemModalProbeTimeoutRecoversThenReleasesOnDrain \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testBoundedSystemModalProbeTimeoutRecoversThenReleasesOnDrainForSnapshotRaw \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testSnapshotTraversalIdentityPreservesSameOriginNodesWithDifferentBounds \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testSnapshotPresentationPreservesCurrentWireShape \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testSnapshotPresentationOwnsBackendNeutralEligibility \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testSnapshotPresentationOwnsScopeAndRelativeDepth \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testSnapshotScopePolicyMatchesGoldenParityTable \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testFlatSnapshotProjectionMatchesElementReverseScrollCapture \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testPrivateAXDepthLimitedRequiresEveryFrontierResolved \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testDeepExtensionCountsMissedFrontiers \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testPreferredPrivateAXBackendPlansAsPenalized \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testPrivateAXRegularPresentationProjectsToViewportAndKeepsScrollHint \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testPrivateAXPresentationKeepsOffscreenSubtreeExcludedWhenChildFramesAreClamped \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testPrivateAXGeometrylessSemanticsAreNeverActionableOrScrollContexts \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testPrivateAXAcquisitionDoesNotInterpretScope \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testPrivateAXInteractiveFiltersLoginLikeHiddenDrawer \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testDecodedPreferredBackendReachesOptionsAndApplicablePlan \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testSparsePayloadReasonMatrix \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testRunnerScreenshotStabilitySettledNeedsEnoughSamples \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testRunnerScreenshotStabilitySettledTrueWhenWindowMatches \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testRunnerScreenshotStabilitySettledFalseOnMidWindowMismatch \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testRunnerScreenshotStabilitySettledFalseOnFailedCapture \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testRunnerScreenshotStabilitySettledOnlyLooksAtTheTrailingWindow \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testRunnerScreenshotStabilitySettledRejectsDegenerateRequirement \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testCustomActionsRequestPinsPrivateAXBackend \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testPrivateAXNodesCarryAnnotatedCustomActions \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testRequestPinnedBackendReportsItsOwnReason \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testCustomActionCoverageParsesOnlyCompletePairs \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testPartialCustomActionPassIsDisclosedAndCompleteOneIsNot \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testActionNamesAreCappedPerElementAndReported \ | |
| -only-testing:AgentDeviceRunnerUITests/RunnerTests/testHungCustomActionReadIsContainedAndRecovers | |
| - name: Preflight iOS runner through public CLI | |
| run: | | |
| pnpm clean:daemon | |
| node --experimental-strip-types src/bin.ts prepare ios-runner --platform ios --udid "${{ steps.ios-simulator.outputs.simulator-udid }}" --timeout "$AGENT_DEVICE_IOS_PREPARE_TIMEOUT_MS" --json | |
| pnpm clean:daemon | |
| - name: Run iOS Settings replay smoke test | |
| run: | | |
| node --experimental-strip-types src/bin.ts test test/integration/replays/ios/simulator/01-settings.ad --udid "${{ steps.ios-simulator.outputs.simulator-udid }}" --retries 2 --artifacts-dir test/artifacts/replays-ios-simulator-smoke --report-junit test/artifacts/replays-ios-simulator-smoke.junit.xml | |
| - name: Fetch current fixture app | |
| id: fixture-app | |
| uses: ./.github/actions/setup-fixture-app | |
| with: | |
| install: 'false' | |
| wait-for-artifact-seconds: ${{ steps.fixture-producer.outputs.wait-seconds }} | |
| - name: Report fixture cache source | |
| run: | | |
| echo "Fixture app source: ${{ steps.fixture-app.outputs.source }}" >> "$GITHUB_STEP_SUMMARY" | |
| - name: Run fixture-backed iOS simulator E2E smoke | |
| env: | |
| AGENT_DEVICE_FIXTURE_APP_ID: ${{ steps.fixture-app.outputs.app-id }} | |
| AGENT_DEVICE_FIXTURE_APP_PATH: ${{ steps.fixture-app.outputs.app-path }} | |
| AGENT_DEVICE_IOS_E2E: '1' | |
| AGENT_DEVICE_IOS_E2E_TIER: smoke | |
| AGENT_DEVICE_IOS_UDID: ${{ steps.ios-simulator.outputs.simulator-udid }} | |
| run: | | |
| pnpm gate build | |
| node --experimental-strip-types scripts/node-test-tmpdir.ts --test test/integration/smoke-ios-simulator-coverage.test.ts test/integration/smoke-ios-simulator.test.ts | |
| - name: Assert simulator automation preserved host focus | |
| if: ${{ always() }} | |
| run: | | |
| set -euo pipefail | |
| test "$(cat .tmp/ios-host-focus-before)" = "com.apple.finder" | |
| FRONTMOST="$(osascript -e 'tell application "System Events" to get bundle identifier of first application process whose frontmost is true')" | |
| test "$FRONTMOST" = "com.apple.finder" | |
| - name: Run iOS physical device smoke replay | |
| if: env.IOS_UDID != '' | |
| env: | |
| IOS_UDID: ${{ vars.IOS_UDID }} | |
| run: | | |
| pnpm clean:daemon | |
| node --experimental-strip-types src/bin.ts test test/integration/replays/ios/device/01-physical-lifecycle.ad --udid "$IOS_UDID" --retries 2 --artifacts-dir test/artifacts/replays-ios-device-smoke --report-junit test/artifacts/replays-ios-device-smoke.junit.xml | |
| - name: Upload iOS artifacts | |
| if: always() | |
| uses: ./.github/actions/upload-agent-device-artifacts | |
| with: | |
| artifact-name: ios-artifacts | |
| agent-state-dir: ${{ env.AGENT_DEVICE_STATE_DIR }} | |
| runner-derived-path: ${{ env.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH }} |