Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cd1ef03
feat(platforms): add run-level runner lifecycle hooks
V3RON Apr 21, 2026
e9a68bd
feat(platform-ios): scaffold the xctest agent project
V3RON Apr 21, 2026
426a3fb
feat(platform-ios): orchestrate the xctest agent lifecycle
V3RON Apr 21, 2026
2bbaaf1
feat(platform-ios): add permission prompt agent capability
V3RON Apr 21, 2026
791fb37
feat(jest): lazily prepare run helpers before app launch
V3RON Apr 21, 2026
5fb5d30
test(platform-ios): harden xctest agent validation
V3RON Apr 21, 2026
e2a73f8
feat(platform-ios): integrate xctest permission agent
V3RON Apr 24, 2026
cd4040f
refactor(platform-ios): remove unused shutdown endpoint and prepareRu…
V3RON Apr 28, 2026
fc5a014
feat(platform-ios): correct dispose of the resources
V3RON Apr 28, 2026
1ce2d81
feat(platform-ios): use loop-based alert detection
V3RON Apr 28, 2026
8d3f7af
chore(platform-ios): remove unneeded file
V3RON Apr 28, 2026
e14bce1
chore(platform-ios): remove devlogs plumbing
V3RON Apr 28, 2026
f04607c
feat(platform-ios): use direct communication
V3RON Apr 28, 2026
3380be6
fix: avoid eager iOS device crash diagnostics
V3RON Apr 28, 2026
65c2dfb
feat: gate permission automation behind config
V3RON Apr 28, 2026
f658867
docs: add permissions guide
V3RON Apr 28, 2026
2fcfa0c
feat(platform-android): implement permission automation via adb
V3RON Apr 28, 2026
2e30e86
fix: harden device crash and permission handling
V3RON Apr 28, 2026
5becc03
fix: use grouped permission listing for adb grants
V3RON Apr 29, 2026
4e732ae
fix: satisfy xctest agent test lint rule
V3RON Apr 29, 2026
24a7760
fix: clear playground iOS development team
V3RON Apr 29, 2026
bd7e0fc
feat(platform-android): grant declared app permissions
V3RON Apr 29, 2026
8380f1d
fix: skip camera permission test on web
V3RON Apr 29, 2026
7caa577
chore(platform-android): add debug logs for permission grants
V3RON Apr 29, 2026
2ad3539
chore(playground): update xcodeproj
V3RON Apr 29, 2026
4d393e8
ci: use xcode 26 for e2e jobs
V3RON Apr 29, 2026
e2b2c87
fix(platform-android): keep emulator camera enabled
V3RON Apr 29, 2026
a38828e
fix(platform-android): reinstall apps on cached emulators
V3RON Apr 30, 2026
4f92d79
fix(platform-ios): persist xctest startup logs
V3RON Apr 30, 2026
415de1d
fix(tools): narrow harness artifact path segments
V3RON Apr 30, 2026
dbccf77
chore: bump timeout
V3RON Apr 30, 2026
2d0ef7d
fix(platform-ios): lower xctest agent deployment target
V3RON Apr 30, 2026
d7d99c6
docs: fix permissions guide
V3RON Apr 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ jobs:
echo "HARNESS_RUNNER=$HARNESS_RUNNER"
echo "HARNESS_EXIT_CODE=$HARNESS_EXIT_CODE"

- name: Upload Harness logs
if: always()
uses: actions/upload-artifact@v4
with:
name: harness-logs-e2e-android
path: apps/playground/.harness/logs
if-no-files-found: ignore

e2e-ios:
name: E2E iOS
runs-on: macos-latest
Expand All @@ -130,6 +138,11 @@ jobs:
node-version: '24.10.0'
cache: 'pnpm'

- name: Setup Xcode 26
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '26.0'

- name: Install Watchman
run: brew install watchman

Expand Down Expand Up @@ -192,6 +205,14 @@ jobs:
echo "HARNESS_RUNNER=$HARNESS_RUNNER"
echo "HARNESS_EXIT_CODE=$HARNESS_EXIT_CODE"

- name: Upload Harness logs
if: always()
uses: actions/upload-artifact@v4
with:
name: harness-logs-e2e-ios
path: apps/playground/.harness/logs
if-no-files-found: ignore

e2e-web:
name: E2E Web
runs-on: ubuntu-22.04
Expand Down Expand Up @@ -238,6 +259,14 @@ jobs:
echo "HARNESS_RUNNER=$HARNESS_RUNNER"
echo "HARNESS_EXIT_CODE=$HARNESS_EXIT_CODE"

- name: Upload Harness logs
if: always()
uses: actions/upload-artifact@v4
with:
name: harness-logs-e2e-web
path: apps/playground/.harness/logs
if-no-files-found: ignore

crash-validate-android:
name: Crash Validation Android
runs-on: ubuntu-22.04
Expand Down Expand Up @@ -344,6 +373,14 @@ jobs:
exit 1
fi

- name: Upload Harness logs
if: always()
uses: actions/upload-artifact@v4
with:
name: harness-logs-crash-validate-android
path: apps/playground/.harness/logs
if-no-files-found: ignore

crash-validate-ios:
name: Crash Validation iOS
runs-on: macos-latest
Expand All @@ -370,6 +407,11 @@ jobs:
node-version: '24.10.0'
cache: 'pnpm'

- name: Setup Xcode 26
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '26.0'

- name: Install Watchman
run: brew install watchman

Expand Down Expand Up @@ -455,3 +497,11 @@ jobs:
echo "ERROR: No crash report artifacts found in $CRASH_DIR"
exit 1
fi

- name: Upload Harness logs
if: always()
uses: actions/upload-artifact@v4
with:
name: harness-logs-crash-validate-ios
path: apps/playground/.harness/logs
if-no-files-found: ignore
5 changes: 5 additions & 0 deletions .nx/version-plans/version-plan-1777573200000.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
__default__: patch
---

Add the `permissions` config flag for cross-platform permission automation, using the iOS XCTest agent for prompt auto-accept and Android `adb pm grant` for requested dangerous permissions.
215 changes: 215 additions & 0 deletions PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# iOS XCTest Agent MVP Plan

## Goal

Implement an iOS XCTest-based agent that can run against both simulators and physical devices, and use it in the MVP to auto-accept permission prompts on a best-effort basis.

This should be a generic XCTest integration for Harness, not a permission-specific helper, so it can be reused for other iOS system-level automation later.

## MVP Scope

- iOS only
- Support both simulator and physical device targets
- Start the XCTest agent once per Harness run
- Stop the XCTest agent during Harness teardown
- Best-effort auto-accept of permission prompts
- Unknown prompts are ignored silently
- No public testing API changes
- No deny/override behavior
- No Android implementation in this phase
- No `simctl privacy` optimization in this phase

## Architecture Direction

- Add a generic run-level lifecycle hook so platform runners can prepare and dispose auxiliary tooling needed for the run.
- Implement the iOS side using a generic `XCTest agent` concept owned by `platform-ios`.
- Package the XCTest agent as a small Xcode project generated with `xcodegen`.
- Use the same XCTest agent concept for both iOS simulators and physical devices.
- Keep permission prompt handling as the first XCTest agent capability, not the only one.

## Phase 1: Lifecycle Integration

Status: Completed

Objective: create the Harness and platform lifecycle seam needed to run auxiliary tooling once per run.

Deliverables:

- Run-level prepare/dispose hooks available on platform runners
- Harness wired to invoke those hooks once per run
- Coverage for success, error, and teardown paths

Notes:

- This phase should remain generic and not mention XCTest directly in shared abstractions.
- The outcome should be reusable by any future platform-owned run helper.

Parallelization:

- Can be done independently from XCTest project creation
- Must land before full end-to-end iOS wiring is completed

## Phase 2: XCTest Agent Project

Status: Completed

Objective: create the reusable iOS XCTest agent project and prove it can be generated reproducibly.

Deliverables:

- New internal `xctest-agent` project inside `packages/platform-ios`
- Project generated from `xcodegen` spec rather than manually maintained project internals
- Minimal shared project structure suitable for both simulator and physical-device builds
- Documented build assumptions and cache inputs

Notes:

- This phase focuses on project packaging and generation, not Harness integration.
- The top-level naming should stay generic so additional XCTest-driven capabilities can be added later.

Parallelization:

- Can proceed in parallel with Phase 1
- Can also proceed in parallel with the host-side iOS orchestration design work in Phase 3

## Phase 3: iOS XCTest Agent Orchestration

Status: Completed

Objective: add host-side orchestration in `platform-ios` to build, cache, start, and stop the XCTest agent.

Deliverables:

- Internal `platform-ios` orchestration for the XCTest agent
- Support for simulator destinations
- Support for physical-device destinations
- Artifact reuse strategy for simulator and device builds
- Clear separation between agent lifecycle management and agent behaviors

Notes:

- Simulator and physical device should share the same orchestration model, even if build artifacts differ.
- The orchestration should treat the agent as a long-lived run-level helper, not something restarted per test file.

Parallelization:

- Depends on enough output from Phase 2 to know what project is being built and launched
- Can be developed in parallel with Phase 4 if the behavior contract is kept narrow

## Phase 4: Permission Prompt Capability

Status: Completed

Objective: implement the first XCTest agent capability: best-effort auto-accept of permission prompts.

Deliverables:

- Permission prompt interruption handling inside the XCTest agent
- Best-effort positive-action tapping behavior
- Silent ignore behavior for unrecognized prompts
- Capability scoped so it can later live beside other XCTest agent behaviors

Notes:

- This phase should not introduce any public Harness API.
- The implementation should be framed as one capability of the generic agent.

Parallelization:

- Can proceed in parallel with most of Phase 3 once the lifecycle between host and agent is understood
- Final validation depends on Phase 3 integration

## Phase 5: End-to-End iOS Wiring

Status: Completed

Objective: connect the generic lifecycle, iOS orchestration, and permission capability into the actual Harness run flow.

Deliverables:

- iOS simulator runs start the XCTest agent before first app launch
- iOS physical-device runs start the XCTest agent before first app launch
- Both stop the agent during teardown
- Existing app launch and restart behavior remains unchanged
- No per-file permission synchronization is introduced

Notes:

- The agent should be started lazily before the first app launch, not eagerly at Harness creation time.
- This phase is where the MVP becomes functionally available.

Parallelization:

- Depends on Phases 1 through 4
- Should be kept small by reusing the outputs of earlier phases rather than adding new concepts

## Phase 6: Validation And Hardening

Objective: verify the MVP works on real targets and stabilize the integration.

Deliverables:

- Automated coverage for host-side lifecycle and orchestration behavior
- Manual validation on at least one iOS simulator
- Manual validation on at least one physical iOS device
- Basic operational documentation for future contributors

Validation focus:

- First-run build experience
- Reuse of cached artifacts on later runs
- Permission prompt auto-accept for at least one real prompt source such as camera
- No obvious teardown leaks or stuck background processes

Parallelization:

- Automated coverage can be built alongside Phase 5
- Manual validation happens after end-to-end wiring is in place

## Suggested Parallel Workstreams

### Stream A: Shared Lifecycle

- Phase 1

### Stream B: XCTest Agent Project

- Phase 2

### Stream C: iOS Agent Runtime Orchestration

- Phase 3

### Stream D: Permission Capability

- Phase 4

### Stream E: Final Wiring And Validation

- Phase 5
- Phase 6

## Dependency Summary

- Phase 1 is required before final integration
- Phase 2 is required before full orchestration can be finalized
- Phase 3 depends on Phase 2
- Phase 4 can begin before Phase 3 is finished, but depends on the agent project shape from Phase 2
- Phase 5 depends on Phases 1 through 4
- Phase 6 depends on Phase 5

## Explicit Non-Goals For This Plan

- Public permission configuration API
- Per-test or per-file permission overrides
- Deny behavior
- Android permission automation
- Simulator fast-path optimization through `simctl privacy`
- Strict unsupported-permission detection or reporting

## Follow-Up After MVP

- Add Android best-effort pregrant support via `adb`
- Add `simctl privacy` fast path for the iOS simulator where supported
- Add more XCTest agent capabilities beyond permission prompts
- Revisit public API design once internal behavior is proven in practice
4 changes: 4 additions & 0 deletions actions/shared/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4194,6 +4194,9 @@ var coerce = {
};
var NEVER = INVALID;

// ../tools/dist/net.js
var import_node_net = __toESM(require("net"), 1);

// ../tools/dist/logger.js
var import_node_util = __toESM(require("util"), 1);
var import_picocolors = __toESM(require_picocolors(), 1);
Expand Down Expand Up @@ -4412,6 +4415,7 @@ var ConfigSchema = external_exports.object({
resetEnvironmentBetweenTestFiles: external_exports.boolean().optional().default(true),
unstable__skipAlreadyIncludedModules: external_exports.boolean().optional().default(false),
unstable__enableMetroCache: external_exports.boolean().optional().default(false),
permissions: external_exports.boolean().optional().default(false).describe("Enable platform-specific permission prompt automation. When false, Harness does not start permission-handling helpers such as the iOS XCTest agent."),
detectNativeCrashes: external_exports.boolean().optional().default(true),
crashDetectionInterval: external_exports.number().min(100, "Crash detection interval must be at least 100ms").default(500),
disableViewFlattening: external_exports.boolean().optional().default(false).describe("Disable view flattening in React Native. This will set collapsable={true} for all View components to ensure they are not flattened by the native layout engine."),
Expand Down
1 change: 1 addition & 0 deletions apps/playground/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />

<application
android:name=".MainApplication"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = HarnessPlayground/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
Expand Down Expand Up @@ -288,6 +289,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = HarnessPlayground/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = (
Expand Down Expand Up @@ -376,7 +378,7 @@
"-DFOLLY_CFG_NO_COROUTINES=1",
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
USE_HERMES = true;
Expand Down Expand Up @@ -444,7 +446,7 @@
"-DFOLLY_CFG_NO_COROUTINES=1",
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
VALIDATE_PRODUCT = YES;
Expand Down
2 changes: 2 additions & 0 deletions apps/playground/ios/HarnessPlayground/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
<key>NSCameraUsageDescription</key>
<string>Harness Playground uses the camera to validate permission handling.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>RCTNewArchEnabled</key>
Expand Down
Loading
Loading