Skip to content
Merged
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
6 changes: 3 additions & 3 deletions src/__tests__/client-normalizers.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { test } from 'vitest';
import assert from 'node:assert/strict';
import { normalizeOpenDevice } from '../client/client-normalizers.ts';
import { PLATFORMS } from '../kernel/device.ts';
import { PUBLIC_PLATFORMS } from '../kernel/device.ts';

test('normalizeOpenDevice accepts exactly the canonical leaf platforms', () => {
for (const platform of PLATFORMS) {
for (const platform of PUBLIC_PLATFORMS) {
const result = normalizeOpenDevice({
platform,
id: 'device-1',
Expand All @@ -14,7 +14,7 @@ test('normalizeOpenDevice accepts exactly the canonical leaf platforms', () => {
assert.equal(result.platform, platform);
}
// Lock the membership so the derived check cannot silently widen/narrow.
assert.deepEqual([...PLATFORMS], ['ios', 'macos', 'android', 'linux', 'web']);
assert.deepEqual([...PUBLIC_PLATFORMS], ['ios', 'macos', 'android', 'linux', 'web']);
});

test('normalizeOpenDevice rejects the apple selector and unknown platforms', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/provider-device-runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function makeProviderRuntimeWorld() {
expiresAt: 60_001,
};
const device: DeviceInfo = {
platform: 'ios',
platform: 'apple',
kind: 'simulator',
id: 'provider:ios:lease-a',
name: 'Provider iOS',
Expand Down
16 changes: 10 additions & 6 deletions src/__tests__/test-utils/device-fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,30 @@ export const ANDROID_EMULATOR: DeviceInfo = {
};

export const IOS_SIMULATOR: DeviceInfo = {
platform: 'ios',
platform: 'apple',
id: 'sim-1',
name: 'iPhone 17 Pro',
kind: 'simulator',
appleOs: 'ios',
booted: true,
};

export const IOS_DEVICE: DeviceInfo = {
platform: 'ios',
platform: 'apple',
id: 'ios-device-1',
name: 'iPhone',
kind: 'device',
appleOs: 'ios',
booted: true,
};

export const MACOS_DEVICE: DeviceInfo = {
platform: 'macos',
platform: 'apple',
id: 'host-macos-local',
name: 'Mac',
kind: 'device',
target: 'desktop',
appleOs: 'macos',
booted: true,
};

Expand Down Expand Up @@ -59,11 +62,12 @@ export const ANDROID_TV_DEVICE: DeviceInfo = {
};

export const TVOS_SIMULATOR: DeviceInfo = {
platform: 'ios',
platform: 'apple',
id: 'tv-sim-1',
name: 'Apple TV',
kind: 'simulator',
target: 'tv',
appleOs: 'tvos',
};

// iPadOS / visionOS carry the explicit `appleOs` discriminant discovery stores, so
Expand All @@ -72,7 +76,7 @@ export const TVOS_SIMULATOR: DeviceInfo = {
// the touch iOS engine (`platform: 'ios'`, mobile target) and are capability-identical
// to iOS today.
export const IPADOS_SIMULATOR: DeviceInfo = {
platform: 'ios',
platform: 'apple',
id: 'ipad-sim-1',
name: 'iPad Pro 11-inch',
kind: 'simulator',
Expand All @@ -81,7 +85,7 @@ export const IPADOS_SIMULATOR: DeviceInfo = {
};

export const VISIONOS_SIMULATOR: DeviceInfo = {
platform: 'ios',
platform: 'apple',
id: 'vision-sim-1',
name: 'Apple Vision Pro',
kind: 'simulator',
Expand Down
7 changes: 5 additions & 2 deletions src/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { AlertAction, AlertInfo } from './alert-contract.ts';
import type { AppsFilter } from './contracts/app-inventory.ts';
import type { Point, SnapshotNode, SnapshotOptions, SnapshotState } from './kernel/snapshot.ts';
import type { NetworkIncludeMode } from './kernel/contracts.ts';
import type { DeviceTarget, Platform, PlatformSelector } from './kernel/device.ts';
import type { DeviceTarget, Platform, PlatformSelector, PublicPlatform } from './kernel/device.ts';
import type { BackMode } from './core/back-mode.ts';
import type { RepeatedInput } from './commands/command-input.ts';
import type { ClickButton } from './core/click-button.ts';
Expand All @@ -18,7 +18,10 @@ import type {
} from './snapshot-capture-annotations.ts';
import type { ScreenshotResultData } from './utils/screenshot-result.ts';

export type AgentDeviceBackendPlatform = Platform;
// The backend's public leaf platform (approach b): backends distinguish iOS from
// macOS (e.g. snapshot backend routing, the macOS surface guard), so this carries the
// leaf string, not the internal collapsed `apple`.
export type AgentDeviceBackendPlatform = PublicPlatform;

export const BACKEND_CAPABILITY_NAMES = [
'android.shell',
Expand Down
16 changes: 11 additions & 5 deletions src/cli/commands/connection-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import { resolveDaemonPaths } from '../../daemon/config.ts';
import { stopReactDevtoolsCompanion } from '../../client/client-react-devtools-companion.ts';
import { stopMetroTunnel } from '../../metro/metro.ts';
import { resolveRemoteConfigProfile } from '../../remote/remote-config.ts';
import { resolveDevice, type DeviceInfo } from '../../kernel/device.ts';
import {
deviceFieldsFromPublicPlatform,
isIosFamily,
publicPlatformString,
resolveDevice,
type DeviceInfo,
} from '../../kernel/device.ts';
import { shouldAgentCdpUseRemoteBridgeUrl } from './agent-cdp.ts';
import type { MetroBridgeScope } from '../../client/client-companion-tunnel-contract.ts';
import {
Expand Down Expand Up @@ -718,7 +724,7 @@ async function resolveProxyLeaseState(options: {
function applyResolvedDeviceSelector(flags: CliFlags, device: DeviceInfo): void {
flags.platform = device.platform;
flags.target = device.target ?? flags.target;
if (device.platform === 'ios') {
if (isIosFamily(device)) {
flags.udid = device.id;
return;
}
Expand All @@ -742,7 +748,7 @@ async function resolveSelectedDevice(
});
return await resolveDevice(
devices.map((device) => ({
platform: device.platform,
...deviceFieldsFromPublicPlatform(device.platform),
id: device.id,
name: device.name,
kind: device.kind,
Expand All @@ -760,11 +766,11 @@ async function resolveSelectedDevice(
}

function buildProxyDeviceKey(device: DeviceInfo): string {
return `${device.platform}:${device.target ?? 'mobile'}:${device.id}`;
return `${publicPlatformString(device)}:${device.target ?? 'mobile'}:${device.id}`;
}

function leaseBackendForDevice(device: DeviceInfo): LeaseBackend | undefined {
if (device.platform === 'ios') return 'ios-instance';
if (isIosFamily(device)) return 'ios-instance';
if (device.platform === 'android') return 'android-instance';
return undefined;
}
Expand Down
4 changes: 2 additions & 2 deletions src/client/client-normalizers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { DaemonRequest, SessionRuntimeHints } from '../daemon/types.ts';
import { AppError, type NormalizedError } from '../kernel/errors.ts';
import type { SnapshotNode } from '../kernel/snapshot.ts';
import { buildAppIdentifiers, buildDeviceIdentifiers } from './client-shared.ts';
import { isPlatform } from '../kernel/device.ts';
import { isPublicPlatform } from '../kernel/device.ts';
import {
leaseScopeFromOptions,
leaseScopeToCommandFlags,
Expand Down Expand Up @@ -184,7 +184,7 @@ export function normalizeOpenDevice(
const platform = value.platform;
const id = readOptionalString(value, 'id');
const name = readOptionalString(value, 'device');
if (!isPlatform(platform) || !id || !name) {
if (!isPublicPlatform(platform) || !id || !name) {
return undefined;
}
const target = readDeviceTarget(value, 'target');
Expand Down
4 changes: 2 additions & 2 deletions src/client/client-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
publicSnapshotCaptureAnnotations,
type SnapshotCaptureAnnotations,
} from '../snapshot-capture-annotations.ts';
import type { Platform } from '../kernel/device.ts';
import type { PublicPlatform } from '../kernel/device.ts';
import { successText, withSuccessText } from '../utils/success-text.ts';

export function buildAppIdentifiers(params: {
Expand All @@ -33,7 +33,7 @@ export function buildAppIdentifiers(params: {
}

export function buildDeviceIdentifiers(
platform: Platform,
platform: PublicPlatform,
id: string,
name: string,
): AgentDeviceIdentifiers {
Expand Down
13 changes: 9 additions & 4 deletions src/client/client-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import type {
SessionIsolationMode,
SessionRuntimeHints,
} from '../kernel/contracts.ts';
import type { DeviceKind, DeviceTarget, Platform, PlatformSelector } from '../kernel/device.ts';
import type {
DeviceKind,
DeviceTarget,
PublicPlatform,
PlatformSelector,
} from '../kernel/device.ts';
import type { BackMode } from '../core/back-mode.ts';
import type { ClickButton } from '../core/click-button.ts';
import type { RecordingExportQuality } from '../core/recording-export-quality.ts';
Expand Down Expand Up @@ -151,7 +156,7 @@ export type AgentDeviceSelectionOptions = {
};

export type AgentDeviceDevice = {
platform: Platform;
platform: PublicPlatform;
target: DeviceTarget;
kind: DeviceKind;
id: string;
Expand All @@ -167,7 +172,7 @@ export type AgentDeviceDevice = {
};

export type AgentDeviceSessionDevice = {
platform: Platform;
platform: PublicPlatform;
target: DeviceTarget;
id: string;
name: string;
Expand Down Expand Up @@ -225,7 +230,7 @@ export type AppDeployOptions = AgentDeviceRequestOverrides &
export type AppDeployResult = {
app: string;
appPath: string;
platform: Platform;
platform: PublicPlatform;
appId?: string;
bundleId?: string;
package?: string;
Expand Down
12 changes: 8 additions & 4 deletions src/cloud-webdriver/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import type {
ProviderDeviceInstallResult,
ProviderDeviceRuntime,
} from '../provider-device-runtime.ts';
import type { DeviceInfo, Platform } from '../kernel/device.ts';
import {
deviceFieldsFromPublicPlatform,
publicPlatformString,
type DeviceInfo,
} from '../kernel/device.ts';
import { AppError } from '../kernel/errors.ts';
import { unavailableCloudArtifactsResult } from './artifact-results.ts';
import {
Expand All @@ -31,7 +35,7 @@ import {
} from './webdriver-client.ts';
import { createWebDriverInteractor } from './webdriver-interactor.ts';

export type CloudWebDriverPlatform = Extract<Platform, 'android' | 'ios'>;
export type CloudWebDriverPlatform = 'android' | 'ios';

export type CloudWebDriverUploadResult = ProviderDeviceInstallResult & {
appReference: string;
Expand Down Expand Up @@ -337,7 +341,7 @@ class CloudWebDriverRuntime implements ProviderDeviceRuntime {

private deviceForLease(lease: DeviceLease, prepared: CloudWebDriverPreparedSession): DeviceInfo {
return {
platform: prepared.platform,
...deviceFieldsFromPublicPlatform(prepared.platform),
id:
prepared.deviceId ??
this.options.deviceId?.(lease) ??
Expand All @@ -364,7 +368,7 @@ class CloudWebDriverRuntime implements ProviderDeviceRuntime {
throw new AppError(
'UNSUPPORTED_OPERATION',
unsupportedCapabilityMessage(session.capabilities, 'install'),
{ provider: this.provider, deviceId: device.id, platform: device.platform },
{ provider: this.provider, deviceId: device.id, platform: publicPlatformString(device) },
);
}
const uploadApp = session.prepared.uploadApp ?? this.options.uploadApp;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/__tests__/command-surface-metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ test('common command input accepts web platform selector', () => {

const platformSchema = snapshotMetadata.inputSchema.properties?.platform;
const input = snapshotMetadata.readInput({ platform: 'web' }) as { platform?: unknown };
assert.deepEqual(platformSchema?.enum, ['ios', 'macos', 'android', 'linux', 'web', 'apple']);
assert.deepEqual(platformSchema?.enum, ['apple', 'android', 'linux', 'web', 'ios', 'macos']);
assert.equal(input.platform, 'web');
});

Expand Down
19 changes: 9 additions & 10 deletions src/compat/maestro/runtime-targets.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { Platform } from '../../kernel/device.ts';
import type { ElementSelectorKey } from '../../core/interactor-types.ts';
import type { Rect, SnapshotNode, SnapshotState } from '../../kernel/snapshot.ts';
import { parseSelectorChain } from '../../daemon/selectors.ts';
Expand Down Expand Up @@ -82,7 +81,7 @@ export function resolveMaestroNodeFromSnapshot(
snapshot: SnapshotState,
selector: string,
options: MaestroTapOnOptions,
platform: Platform,
platform: 'ios' | 'android',
frame: TouchReferenceFrame | undefined,
resolutionOptions: MaestroMatchResolutionOptions = {},
): { ok: true; node: SnapshotNode; rect: Rect } | { ok: false; message: string } {
Expand Down Expand Up @@ -132,7 +131,7 @@ export function resolveMaestroNodeFromSnapshot(
export function resolveMaestroFuzzyTextNodeFromSnapshot(
snapshot: SnapshotState,
query: string,
platform: Platform,
platform: 'ios' | 'android',
frame: TouchReferenceFrame | undefined,
resolutionOptions: MaestroMatchResolutionOptions = {},
): { ok: true; node: SnapshotNode; rect: Rect } | { ok: false; message: string } {
Expand Down Expand Up @@ -161,7 +160,7 @@ export function resolveMaestroFuzzyTextNodeFromSnapshot(
export function resolveVisibleMaestroNodeFromSnapshot(
snapshot: SnapshotState,
selector: string,
platform: Platform,
platform: 'ios' | 'android',
frame: TouchReferenceFrame | undefined,
): { ok: true; node: SnapshotNode; rect: Rect; matches: number } | { ok: false; message: string } {
const matches = findMaestroSelectorMatches(snapshot, selector, platform, {
Expand Down Expand Up @@ -201,7 +200,7 @@ export function resolveVisibleMaestroNodeFromSnapshot(
function filterVisibleMaestroMatches(params: {
nodes: SnapshotState['nodes'];
matches: SnapshotNode[];
platform: Platform;
platform: 'ios' | 'android';
}): { matches: SnapshotNode[]; blockedByReactNativeOverlay: boolean } {
const visibleMatches = params.matches.filter(
(node) =>
Expand All @@ -226,7 +225,7 @@ function filterVisibleMaestroMatches(params: {
function filterReactNativeOverlayBlockedMatches(
nodes: SnapshotState['nodes'],
matches: SnapshotNode[],
platform: Platform,
platform: 'ios' | 'android',
): ReactNativeOverlayFilterResult {
const overlay = detectReactNativeOverlay(nodes);
if (!overlay.detected) {
Expand Down Expand Up @@ -259,7 +258,7 @@ function filterReactNativeOverlayBlockedMatches(
};
}

export function readMaestroSelectorPlatform(flags: DaemonRequest['flags']): Platform {
export function readMaestroSelectorPlatform(flags: DaemonRequest['flags']): 'ios' | 'android' {
return flags?.platform === 'android' ? 'android' : 'ios';
}

Expand All @@ -280,7 +279,7 @@ export function extractMaestroVisibleTextQuery(selectorExpression: string): stri
function findMaestroSelectorMatches(
snapshot: SnapshotState,
selectorExpression: string,
platform: Platform,
platform: 'ios' | 'android',
options: MaestroSelectorMatchOptions = {},
): SnapshotNode[] {
const chain = parseSelectorChain(selectorExpression);
Expand Down Expand Up @@ -315,7 +314,7 @@ function findMaestroFuzzyTextMatches(snapshot: SnapshotState, query: string): Sn
function matchesMaestroSelector(
node: SnapshotNode,
selector: Selector,
platform: Platform,
platform: 'ios' | 'android',
options: MaestroSelectorMatchOptions,
): boolean {
if (matchesSelector(node, selector, platform)) return true;
Expand All @@ -325,7 +324,7 @@ function matchesMaestroSelector(
function matchesMaestroTerm(
node: SnapshotNode,
term: SelectorTerm,
platform: Platform,
platform: 'ios' | 'android',
options: MaestroSelectorMatchOptions,
): boolean {
if (typeof term.value !== 'string' || !isMaestroRegexTextKey(term.key)) {
Expand Down
Loading
Loading