> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qa.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Mobile regression testing

> Pin a test plan to the build CI uploaded for a pull request.

A regression run of a mobile app uses the build you pin, not a preview URL. Upload the binary, then pass `applicationBuildShortId` on the test plan run. The shared upload steps are on [Mobile pull request testing](/pr-testing/agents/mobile#upload-the-pr-build).

## Run a test plan on the PR

Use this when a required check should run a known plan against the PR binary.

```yaml theme={null}
name: QA.tech mobile PR tests
on:
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build APK
        run: ./gradlew assembleDebug

      - name: Upload build to QA.tech
        id: upload
        env:
          QATECH_API_TOKEN: ${{ secrets.QATECH_API_TOKEN }}
          APK_PATH: app/build/outputs/apk/debug/app-debug.apk
        run: |
          APP_ID="app_gXeBl2"
          FILE_NAME=$(basename "$APK_PATH")
          UPLOAD_RESPONSE=$(curl -sSf -X POST "https://api.qa.tech/v1/applications/$APP_ID/builds/upload-url" \
            -H "Authorization: Bearer $QATECH_API_TOKEN" \
            -H "Content-Type: application/json" \
            -d "{\"fileName\": \"$FILE_NAME\"}")
          UPLOAD_URL=$(echo "$UPLOAD_RESPONSE" | jq -r '.uploadUrl')
          BUILD_TOKEN=$(echo "$UPLOAD_RESPONSE" | jq -r '.buildToken')
          curl -sSf -X PUT "$UPLOAD_URL" \
            --upload-file "$APK_PATH" \
            -H "Content-Type: application/octet-stream"
          BUILD_RESPONSE=$(curl -sSf -X POST "https://api.qa.tech/v1/applications/$APP_ID/builds" \
            -H "Authorization: Bearer $QATECH_API_TOKEN" \
            -H "Content-Type: application/json" \
            -d "{\"platform\": \"android\", \"buildToken\": \"$BUILD_TOKEN\"}")
          echo "build_short_id=$(echo "$BUILD_RESPONSE" | jq -r '.applicationBuildShortId')" >> "$GITHUB_OUTPUT"

      - uses: QAdottech/run-action@v4
        with:
          project_short_id: 'proj_abc123'
          api_token: ${{ secrets.QATECH_API_TOKEN }}
          test_plan_short_id: 'pln_abc123'
          blocking: true
          applications_config: |
            {
              "applications": {
                "app_gXeBl2": {
                  "environment": {
                    "applicationBuildShortId": "${{ steps.upload.outputs.build_short_id }}"
                  }
                }
              }
            }
```

Replace `app_gXeBl2`, `pln_abc123`, `proj_abc123`, and the APK path. Set `blocking: true` if the workflow should fail when tests fail.

Native Android and iOS examples assume `android/` or an `.app` already exist. Expo and React Native managed apps gitignore those folders. In CI, generate them before upload:

```yaml theme={null}
- run: npm ci
- run: npx expo prebuild --platform android --non-interactive --no-install
- run: cd android && ./gradlew assembleDebug --no-daemon
```

The debug APK path is typically `android/app/build/outputs/apk/debug/app-debug.apk`. EAS preview APKs also work if you wait for the build and download the artifact. Still upload a simulator or emulator binary, not a store `.ipa`. Full build steps are in [Preparing Your App Build](/test-features/mobile-app-testing#preparing-your-app-build).

iOS builds need a macOS runner and a simulator `.app`. Zip it, use `"platform": "ios"`, and point `BUILD_FILE` at the archive. Full `xcodebuild` flags are in [Mobile App Testing](/test-features/mobile-app-testing#preparing-your-app-build).

The same `applicationBuildShortId` field works from GitLab CI and Bitrise. See [GitLab](/regression-testing/gitlab) and [Bitrise](/regression-testing/bitrise). The [Application Builds API](/api-reference/application-builds) is the upload reference.
