Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,25 @@ jobs:
exit 1
fi

swift-runner-unit-compile:
name: Swift Runner Unit Compile
runs-on: macos-26
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup toolchain
uses: ./.github/actions/setup-node-pnpm

- name: Compile Swift runner unit-test surface
uses: ./.github/actions/setup-apple-replay
with:
derived-path: ${{ github.workspace }}/.tmp/swift-runner-unit-derived
cache-key-prefix: swift-runner-unit
build-command: AGENT_DEVICE_XCUITEST_INCLUDE_UNIT_TESTS=1 pnpm build:xcuitest:macos
xcuitest-platform: macos

no-test-di-seams:
name: No test-only DI seams
runs-on: ubuntu-latest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,6 @@
ALWAYS_SEARCH_USER_PATHS = NO;
AGENT_DEVICE_IOS_RUNNER_APP_BUNDLE_ID = com.callstack.agentdevice.runner;
AGENT_DEVICE_IOS_RUNNER_TEST_BUNDLE_ID = "$(AGENT_DEVICE_IOS_RUNNER_APP_BUNDLE_ID).uitests";
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
Expand Down Expand Up @@ -273,7 +272,6 @@
ALWAYS_SEARCH_USER_PATHS = NO;
AGENT_DEVICE_IOS_RUNNER_APP_BUNDLE_ID = com.callstack.agentdevice.runner;
AGENT_DEVICE_IOS_RUNNER_TEST_BUNDLE_ID = "$(AGENT_DEVICE_IOS_RUNNER_APP_BUNDLE_ID).uitests";
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
Expand Down Expand Up @@ -330,8 +328,6 @@
20EA2EE82F2CFC7C001CF0EF /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 2S799L9W4M;
Expand Down Expand Up @@ -366,8 +362,6 @@
20EA2EE92F2CFC7C001CF0EF /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 2S799L9W4M;
Expand Down

This file was deleted.

This file was deleted.

Binary file not shown.

This file was deleted.

This file was deleted.

Binary file not shown.

This file was deleted.

Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ extension RunnerTests {
}
}

#if AGENT_DEVICE_RUNNER_UNIT_TESTS
// MARK: - In-bundle unit tests

extension RunnerTests {
Expand Down Expand Up @@ -325,3 +326,4 @@ extension RunnerTests {
XCTAssertFalse(labels.contains("Admin settings"))
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ extension RunnerTests {
return Response(ok: true, data: data)
}

#if AGENT_DEVICE_RUNNER_UNIT_TESTS
func testGestureResponseIncludesSynthesizedTapFallbackDiagnostics() {
let response = gestureResponse(
message: "tapped",
Expand Down Expand Up @@ -180,6 +181,7 @@ extension RunnerTests {
)
XCTAssertNil(xctestRecordedFailureResponse(command: tapCommand, response: runnerFatalResponse))
}
#endif

func execute(command: Command) throws -> Response {
if command.command == .status {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ final class RunnerCommandJournal {
}
}

#if AGENT_DEVICE_RUNNER_UNIT_TESTS
extension RunnerTests {
func testUptimeBypassesCommandJournal() throws {
let command = runnerJournalCommand("uptime", id: "uptime-probe")
Expand Down Expand Up @@ -439,3 +440,4 @@ extension RunnerTests {
return try JSONDecoder().decode(Response.self, from: Data(responseJson.utf8))
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ struct FlatSnapshotFilterNode {
let valueText: String?
let visible: Bool

var hasContent: Bool {
return !label.isEmpty || !identifier.isEmpty || valueText != nil
}

func matchesScope(_ scope: String) -> Bool {
let haystack = [label, identifier, valueText ?? ""].joined(separator: "\n")
return haystack.localizedCaseInsensitiveContains(scope)
Expand Down Expand Up @@ -68,6 +64,7 @@ extension RunnerTests {
return type
}

#if AGENT_DEVICE_RUNNER_UNIT_TESTS
func testFlatSnapshotFilterDecisionMatrixCoversOptions() {
let visibleContent = FlatSnapshotFilterNode(
isRoot: false,
Expand Down Expand Up @@ -168,4 +165,5 @@ extension RunnerTests {
"private AX marks scroll containers as interactive candidates"
)
}
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,7 @@ extension RunnerTests {
return element.exists ? element : nil
}

#if AGENT_DEVICE_RUNNER_UNIT_TESTS
// Identity in portrait/unknown, 90° per landscape, 180° upside-down.
func testNativeSynthesizedPointRotatesByInterfaceOrientation() {
let portrait = CGRect(x: 0, y: 0, width: 834, height: 1210)
Expand Down Expand Up @@ -1333,4 +1334,5 @@ extension RunnerTests {
XCTAssertEqual(events.count, 1)
XCTAssertEqual(events.first?.vertical, -200)
}
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func runnerScrollGesturePlan(
}
}

#if AGENT_DEVICE_RUNNER_UNIT_TESTS
extension RunnerTests {
// Cross-language parity vectors mirroring src/core/__tests__/scroll-gesture.test.ts. Keep these
// in sync with the vitest vectors so the two buildScrollGesturePlan implementations cannot drift.
Expand Down Expand Up @@ -216,3 +217,4 @@ extension RunnerTests {
)
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ extension RunnerTests {
}
}

#if AGENT_DEVICE_RUNNER_UNIT_TESTS
// MARK: - In-bundle unit tests (device-free)

extension RunnerTests {
Expand Down Expand Up @@ -448,3 +449,4 @@ extension RunnerTests {
return try! JSONDecoder().decode(Command.self, from: data)
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ extension RunnerTests {
)
}

#if AGENT_DEVICE_RUNNER_UNIT_TESTS
func testSnapshotAccessibilityUnavailableMarksSparseSnapshotRunnerFatal() {
currentApp = app
currentBundleId = "com.example.app"
Expand Down Expand Up @@ -468,6 +469,7 @@ extension RunnerTests {
XCTAssertTrue(failure.message.contains("\(Self.rawSnapshotMaxNodes) nodes"))
XCTAssertEqual(failure.hint, Self.rawSnapshotTooLargeHint)
}
#endif

private func interactiveRootNode(rect: CGRect) -> SnapshotNode {
SnapshotNode(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ extension RunnerTests {
}
}

#if AGENT_DEVICE_RUNNER_UNIT_TESTS
// MARK: - In-bundle unit tests

extension RunnerTests {
Expand Down Expand Up @@ -423,3 +424,4 @@ extension RunnerTests {
XCTAssertEqual(payload.snapshotQuality?.reasonCode, "ax-rejected")
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ extension RunnerTests {

enum TextEntryTiming {
static let focusTimeout: TimeInterval = 0.4
static let repairReadinessTimeout: TimeInterval = 1.0
static let readinessTimeout: TimeInterval = 2.0
static let hardwareKeyboardFallbackTimeout: TimeInterval = 0.35
static let pollInterval: TimeInterval = 0.02
Expand Down
45 changes: 42 additions & 3 deletions scripts/build-xcuitest-apple.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ is_truthy() {
resolve_default_destination() {
case "$PLATFORM" in
ios)
printf '%s\n' 'generic/platform=iOS Simulator'
resolve_simulator_destination 'iOS' 'iPhone' || printf '%s\n' 'generic/platform=iOS Simulator'
;;
macos)
printf 'platform=macOS,arch=%s\n' "$(uname -m)"
;;
tvos)
printf '%s\n' 'generic/platform=tvOS Simulator'
resolve_simulator_destination 'tvOS' 'Apple TV' || printf '%s\n' 'generic/platform=tvOS Simulator'
;;
*)
echo "Unsupported AGENT_DEVICE_XCUITEST_PLATFORM: $PLATFORM" >&2
Expand All @@ -40,6 +40,40 @@ resolve_default_destination() {
esac
}

resolve_simulator_destination() {
command -v node >/dev/null 2>&1 || return 1
node -e '
const { execFileSync } = require("node:child_process");
const platformName = process.argv[1];
const deviceNamePattern = new RegExp(process.argv[2]);
const platformNameLower = platformName.toLowerCase();
try {
const output = execFileSync("xcrun", ["simctl", "list", "devices", "available", "-j"], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
timeout: 3000,
});
const parsed = JSON.parse(output);
const devices = Object.entries(parsed.devices ?? {})
.filter(([runtime]) => runtime.toLowerCase().includes(platformNameLower))
.flatMap(([, runtimeDevices]) => Array.isArray(runtimeDevices) ? runtimeDevices : [])
.filter(
(device) =>
device &&
device.isAvailable !== false &&
typeof device.udid === "string" &&
typeof device.name === "string" &&
deviceNamePattern.test(device.name),
);
const selected = devices.find((device) => device.state === "Booted") ?? devices[0];
if (!selected) process.exit(1);
console.log(`platform=${platformName} Simulator,id=${selected.udid}`);
} catch {
process.exit(1);
}
' "$1" "$2"
}

resolve_default_derived_path() {
case "$PLATFORM" in
ios)
Expand Down Expand Up @@ -93,6 +127,11 @@ if is_truthy "${AGENT_DEVICE_IOS_CLEAN_DERIVED:-}"; then
rm -rf "$CLEAN_PATH"
fi

SWIFT_FLAGS='$(inherited) -disable-sandbox'
if is_truthy "${AGENT_DEVICE_XCUITEST_INCLUDE_UNIT_TESTS:-}"; then
SWIFT_FLAGS="$SWIFT_FLAGS -D AGENT_DEVICE_RUNNER_UNIT_TESTS"
fi

xcodebuild build-for-testing \
-project "$PROJECT_PATH" \
-scheme "$SCHEME" \
Expand All @@ -108,7 +147,7 @@ xcodebuild build-for-testing \
-IDEPackageSupportDisableManifestSandbox=1 \
-IDEPackageSupportDisablePluginExecutionSandbox=1 \
ENABLE_USER_SCRIPT_SANDBOXING=NO \
OTHER_SWIFT_FLAGS='$(inherited) -disable-sandbox' \
OTHER_SWIFT_FLAGS="$SWIFT_FLAGS" \
$SIGNING_BUILD_SETTINGS

node --experimental-strip-types scripts/patch-xcuitest-runner-icon.ts "$DERIVED_PATH"
Expand Down
Loading
Loading