@e2e-dev/mobile 0.8.0-canary-20260928184528

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +5 -0
  3. package/README.md +159 -0
  4. package/dist/.build.json +1 -0
  5. package/dist/actions.d.ts +41 -0
  6. package/dist/actions.d.ts.map +1 -0
  7. package/dist/actions.js +70 -0
  8. package/dist/actions.js.map +1 -0
  9. package/dist/bindings.d.ts +87 -0
  10. package/dist/bindings.d.ts.map +1 -0
  11. package/dist/bindings.js +112 -0
  12. package/dist/bindings.js.map +1 -0
  13. package/dist/device.d.ts +117 -0
  14. package/dist/device.d.ts.map +1 -0
  15. package/dist/device.js +116 -0
  16. package/dist/device.js.map +1 -0
  17. package/dist/engine.d.ts +17 -0
  18. package/dist/engine.d.ts.map +1 -0
  19. package/dist/engine.js +98 -0
  20. package/dist/engine.js.map +1 -0
  21. package/dist/errors.d.ts +61 -0
  22. package/dist/errors.d.ts.map +1 -0
  23. package/dist/errors.js +223 -0
  24. package/dist/errors.js.map +1 -0
  25. package/dist/index.d.ts +21 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +14 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/links.d.ts +41 -0
  30. package/dist/links.d.ts.map +1 -0
  31. package/dist/links.js +79 -0
  32. package/dist/links.js.map +1 -0
  33. package/dist/locate.d.ts +11 -0
  34. package/dist/locate.d.ts.map +1 -0
  35. package/dist/locate.js +119 -0
  36. package/dist/locate.js.map +1 -0
  37. package/dist/nodes.d.ts +107 -0
  38. package/dist/nodes.d.ts.map +1 -0
  39. package/dist/nodes.js +530 -0
  40. package/dist/nodes.js.map +1 -0
  41. package/dist/options.d.ts +134 -0
  42. package/dist/options.d.ts.map +1 -0
  43. package/dist/options.js +7 -0
  44. package/dist/options.js.map +1 -0
  45. package/dist/png.d.ts +4 -0
  46. package/dist/png.d.ts.map +1 -0
  47. package/dist/png.js +25 -0
  48. package/dist/png.js.map +1 -0
  49. package/dist/pool.d.ts +99 -0
  50. package/dist/pool.d.ts.map +1 -0
  51. package/dist/pool.js +235 -0
  52. package/dist/pool.js.map +1 -0
  53. package/dist/provider.d.ts +119 -0
  54. package/dist/provider.d.ts.map +1 -0
  55. package/dist/provider.js +112 -0
  56. package/dist/provider.js.map +1 -0
  57. package/dist/selector.d.ts +17 -0
  58. package/dist/selector.d.ts.map +1 -0
  59. package/dist/selector.js +84 -0
  60. package/dist/selector.js.map +1 -0
  61. package/dist/support.d.ts +63 -0
  62. package/dist/support.d.ts.map +1 -0
  63. package/dist/support.js +87 -0
  64. package/dist/support.js.map +1 -0
  65. package/dist/surface.d.ts +405 -0
  66. package/dist/surface.d.ts.map +1 -0
  67. package/dist/surface.js +1159 -0
  68. package/dist/surface.js.map +1 -0
  69. package/dist/tools.d.ts +24 -0
  70. package/dist/tools.d.ts.map +1 -0
  71. package/dist/tools.js +90 -0
  72. package/dist/tools.js.map +1 -0
  73. package/package.json +83 -0
@@ -0,0 +1,112 @@
1
+ /**
2
+ * What one worker slot drives, and how it travels from the runner's `prepare`
3
+ * to the worker's `init`. Every way a pool comes by devices (a configured
4
+ * list, the booted devices it discovered, the leases a provider granted) is
5
+ * one `DeviceSource` that ends in one `SlotBinding` per slot, so the worker
6
+ * reads one shape through one environment variable, whatever produced it.
7
+ */
8
+ import { createHash } from 'node:crypto';
9
+ import { EngineError, obj } from 'e2e/engine';
10
+ /** What a binding calls its device in progress and error lines: the name, else the id, else nothing. */
11
+ export function deviceLabel(binding) {
12
+ return binding?.device ?? binding?.deviceId;
13
+ }
14
+ /**
15
+ * The app a slot's `app.open()` launches, and its warm-up opens once: the
16
+ * `app` option, else the app the build `appPath` installed, once the suite's
17
+ * `device.installApp()` (or a device provider) has. `undefined` with a build
18
+ * nobody has installed and no `app`.
19
+ */
20
+ export function pinnedApp(options, installedApp) {
21
+ return options.app ?? installedApp;
22
+ }
23
+ /**
24
+ * The most the serialized bindings of one target may occupy in a worker's
25
+ * environment: a lease comes from third-party code, and an oversized
26
+ * environment fails the worker spawn with an opaque error.
27
+ */
28
+ const MAX_BINDINGS_ENV_BYTES = 16 * 1024;
29
+ /**
30
+ * The environment variable a target's bindings travel to the workers in: the
31
+ * name made environment-safe for reading, plus a digest of the exact name so
32
+ * `ios-a` and `ios.a` never share a key.
33
+ */
34
+ export function bindingsVariable(targetName) {
35
+ const readable = targetName.replace(/[^A-Za-z0-9]/g, '_').toUpperCase();
36
+ const digest = createHash('sha256').update(targetName).digest('hex').slice(0, 8).toUpperCase();
37
+ return `E2E_AGENT_DEVICE_POOL_${readable}_${digest}`;
38
+ }
39
+ /** Serializes the bindings for the environment: declared fields only, bounded. */
40
+ export function encodeBindings(bindings) {
41
+ const encoded = JSON.stringify(bindings.map((binding) => obj({
42
+ device: binding.device,
43
+ deviceId: binding.deviceId,
44
+ daemon: binding.daemon === undefined ? undefined : obj({ baseUrl: binding.daemon.baseUrl, authToken: binding.daemon.authToken }),
45
+ client: binding.client,
46
+ installedApp: binding.installedApp,
47
+ sessionApp: binding.sessionApp,
48
+ })));
49
+ const bytes = Buffer.byteLength(encoded);
50
+ if (bytes > MAX_BINDINGS_ENV_BYTES) {
51
+ throw new EngineError('ENGINE_FAILURE', `the device bindings take ${bytes} bytes; the worker environment carries at most ${MAX_BINDINGS_ENV_BYTES}`, { retryable: false });
52
+ }
53
+ return encoded;
54
+ }
55
+ /** The bindings a `prepare` left in the environment, or `undefined` when there are none it can read. */
56
+ export function decodeBindings(raw) {
57
+ if (raw === undefined)
58
+ return undefined;
59
+ try {
60
+ const parsed = JSON.parse(raw);
61
+ if (Array.isArray(parsed) && parsed.every(isSlotBinding))
62
+ return parsed;
63
+ }
64
+ catch {
65
+ // Not ours to read.
66
+ }
67
+ return undefined;
68
+ }
69
+ function isOptionalString(value) {
70
+ return value === undefined || typeof value === 'string';
71
+ }
72
+ /** A binding as JSON parses it back, or as a provider returned it: nothing the engine did not write is trusted. */
73
+ export function isSlotBinding(value) {
74
+ if (typeof value !== 'object' || value === null)
75
+ return false;
76
+ const { device, deviceId, daemon, client, installedApp, sessionApp } = value;
77
+ return (isOptionalString(device) &&
78
+ isOptionalString(deviceId) &&
79
+ isOptionalString(installedApp) &&
80
+ isOptionalString(sessionApp) &&
81
+ (daemon === undefined || isDaemon(daemon)) &&
82
+ (client === undefined || isClientConfig(client)));
83
+ }
84
+ /**
85
+ * Reserved for the engine and the `daemon` field, where a provider's value would be
86
+ * silently overruled; the transport is how the worker reaches the daemon, not a device fact.
87
+ */
88
+ const RESERVED_CLIENT_KEYS = new Set(['session', 'daemonBaseUrl', 'daemonAuthToken', 'daemonTransport']);
89
+ /** Plain JSON data with none of the reserved keys: what survives the environment and what the engine will not override. */
90
+ function isClientConfig(value) {
91
+ return isJsonObject(value) && Object.keys(value).every((key) => !RESERVED_CLIENT_KEYS.has(key));
92
+ }
93
+ function isJsonObject(value) {
94
+ return typeof value === 'object' && value !== null && !Array.isArray(value) && Object.values(value).every(isJsonValue);
95
+ }
96
+ /** What survives `JSON.stringify` unchanged: a non-finite number would reach the worker as `null`. */
97
+ function isJsonValue(value) {
98
+ if (value === null || typeof value === 'string' || typeof value === 'boolean')
99
+ return true;
100
+ if (typeof value === 'number')
101
+ return Number.isFinite(value);
102
+ if (Array.isArray(value))
103
+ return value.every(isJsonValue);
104
+ return isJsonObject(value);
105
+ }
106
+ function isDaemon(value) {
107
+ if (typeof value !== 'object' || value === null)
108
+ return false;
109
+ const { baseUrl, authToken } = value;
110
+ return typeof baseUrl === 'string' && isOptionalString(authToken);
111
+ }
112
+ //# sourceMappingURL=bindings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bindings.js","sourceRoot":"","sources":["../src/bindings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * The `device` fixture: deterministic device management this engine
3
+ * contributes. Not an agent tool; a test calls these directly to arrange or
4
+ * assert device state, and the harness records each call as a
5
+ * `device.<method>` step bounded by the action timeout.
6
+ */
7
+ import type { EngineFixtureContext, Locator } from 'e2e/engine';
8
+ import type { DevicePermission, PermissionState } from './options.ts';
9
+ import type { AgentDeviceSurface, InstallAppOptions, InstalledApp, OpenAppOptions } from './surface.ts';
10
+ /** Orientations `setOrientation` accepts. */
11
+ export type DeviceOrientation = 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right';
12
+ export type BiometricSensor = 'faceid' | 'touchid' | 'fingerprint';
13
+ export interface ForegroundApp {
14
+ readonly name: string;
15
+ readonly bundleId?: string;
16
+ }
17
+ /** Deterministic device management exposed to tests as `device`. */
18
+ export interface Device {
19
+ /**
20
+ * A locator from an agent-device selector (`'id=SW_VERSION_SPECIFIER'`,
21
+ * `'role=StaticText label="26.2"'`), for nodes the closed `screen` query
22
+ * vocabulary cannot name. Same polling and strictness as any locator.
23
+ */
24
+ locator(selector: string): Locator;
25
+ /** Toggles the device's Wi-Fi. */
26
+ setNetwork(state: 'online' | 'offline'): Promise<void>;
27
+ /** Toggles airplane mode. */
28
+ setAirplaneMode(enabled: boolean): Promise<void>;
29
+ /**
30
+ * Grants, denies, or resets one permission for the pinned app, brought to
31
+ * the foreground first when the session is on no app. A change terminates
32
+ * a running app on iOS, a revoke one on Android, so it goes before the
33
+ * `app.open()` a test starts with.
34
+ */
35
+ setPermission(permission: DevicePermission, state: PermissionState): Promise<void>;
36
+ /** Sets the simulated location, switching location services on first on Android. */
37
+ setLocation(coordinates: {
38
+ latitude: number;
39
+ longitude: number;
40
+ }): Promise<void>;
41
+ /** Turns simulated location off; on Android that is location services off, until the next `setLocation`. */
42
+ clearLocation(): Promise<void>;
43
+ /** Sets the system appearance. */
44
+ setAppearance(mode: 'light' | 'dark'): Promise<void>;
45
+ /** Rotates the device. */
46
+ setOrientation(orientation: DeviceOrientation): Promise<void>;
47
+ /** Simulates one biometric attempt on the open app. */
48
+ setBiometrics(sensor: BiometricSensor, result: 'match' | 'nonmatch'): Promise<void>;
49
+ /** Enrolls or unenrolls the simulator's Face ID or Touch ID. */
50
+ enrollBiometrics(sensor: 'faceid' | 'touchid', enrolled: boolean): Promise<void>;
51
+ /**
52
+ * Installs a build (an iOS `.app` bundle or an Android `.apk`, resolved
53
+ * against the project root) on the device; without a path, the engine's
54
+ * `appPath`. The engine installs nothing on its own, so a suite that runs
55
+ * against a build calls this once per device, in a fixture or a test.
56
+ * `reinstall: true` removes the app first so it starts with no data; a
57
+ * plain install replaces the binary and keeps its data. Resolves to the
58
+ * identity to `openApp` it by, which becomes the app `app.open()` launches
59
+ * when the engine's build is installed and no `app` is pinned.
60
+ */
61
+ installApp(appPath?: string, options?: InstallAppOptions): Promise<InstalledApp>;
62
+ /**
63
+ * Brings an app to the foreground; `relaunch` restarts it fresh.
64
+ * `launchArguments` and `permissions` apply to this launch alone; the
65
+ * engine's configured ones apply to a `relaunch` of the pinned app, and
66
+ * to no foreground-only open.
67
+ */
68
+ openApp(app: string, options?: OpenAppOptions): Promise<void>;
69
+ /**
70
+ * Opens a deep link (`myapp://orders/42`) or a web link (`https://...`)
71
+ * into `app` (default: the pinned app), which the session observes
72
+ * afterwards. iOS launches the app for a web link and then opens the URL;
73
+ * a web link the app does not claim goes to Safari, and the next
74
+ * observation brings the app back. Android starts the link on that
75
+ * package. Without an app, Android lets the OS route the link; iOS needs
76
+ * one (`INVALID_ARGUMENT` without). `file:`, `data:`, and `javascript:`
77
+ * links are `POLICY_DENIED`.
78
+ */
79
+ openLink(url: string, options?: {
80
+ app?: string;
81
+ }): Promise<void>;
82
+ /**
83
+ * Terminates the session's app and ends the session, so the next
84
+ * `openApp` launches it fresh. Until then the screen is unobservable and
85
+ * `foregroundApp` is `APP_NOT_OPEN`.
86
+ */
87
+ closeApp(): Promise<void>;
88
+ /**
89
+ * Resets the simulator's keychain, every app's, since simctl has no
90
+ * per-app reset: what `app.clearState()` leaves behind (a session token, a
91
+ * Firebase sign-in). iOS simulator only; Android is
92
+ * `UNSUPPORTED_CAPABILITY`. Launch the app afterwards to see it signed out.
93
+ */
94
+ clearKeychain(): Promise<void>;
95
+ /**
96
+ * The app the session is on. iOS answers from the session, not the
97
+ * device, so it names the app the session opened even after `home()`;
98
+ * Android reads the device's foreground activity. `APP_NOT_OPEN` once
99
+ * `closeApp` ended the session.
100
+ */
101
+ foregroundApp(): Promise<ForegroundApp>;
102
+ /** Sends the device to its home screen. */
103
+ home(): Promise<void>;
104
+ /** Navigates back once (in-app back). */
105
+ back(): Promise<void>;
106
+ /** Accepts or dismisses the visible system alert. */
107
+ alert(action: 'accept' | 'dismiss'): Promise<void>;
108
+ /** Dismisses the soft keyboard. */
109
+ dismissKeyboard(): Promise<void>;
110
+ /** Reads the clipboard. */
111
+ clipboard(): Promise<string>;
112
+ /** Writes the clipboard. */
113
+ setClipboard(text: string): Promise<void>;
114
+ }
115
+ /** Builds the device fixture for one attempt. */
116
+ export declare function createDeviceFixture(surface: AgentDeviceSurface, context: EngineFixtureContext): Device;
117
+ //# sourceMappingURL=device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"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"}
package/dist/device.js ADDED
@@ -0,0 +1,116 @@
1
+ /**
2
+ * The `device` fixture: deterministic device management this engine
3
+ * contributes. Not an agent tool; a test calls these directly to arrange or
4
+ * assert device state, and the harness records each call as a
5
+ * `device.<method>` step bounded by the action timeout.
6
+ */
7
+ import { linkLabel, linkTarget } from './links.js';
8
+ /** Builds the device fixture for one attempt. */
9
+ export function createDeviceFixture(surface, context) {
10
+ const device = {
11
+ locator: (selector) => context.locator({ kind: 'selector', selector }),
12
+ async setNetwork(state) {
13
+ await surface.command('device.setNetwork', (client) => client.settings.update({ setting: 'wifi', state: state === 'offline' ? 'off' : 'on' }), context.signal);
14
+ },
15
+ async setAirplaneMode(enabled) {
16
+ await surface.command('device.setAirplaneMode', (client) => client.settings.update({ setting: 'airplane', state: enabled ? 'on' : 'off' }), context.signal);
17
+ },
18
+ async setPermission(permission, state) {
19
+ await surface.setPermission(permission, state, context.signal);
20
+ },
21
+ async setLocation({ latitude, longitude }) {
22
+ // Android reads a fix only while location services are on, and
23
+ // `clearLocation` switched them off for good on the emulator (the
24
+ // setting outlives the app); iOS has no such switch.
25
+ if (surface.options.platform === 'android') {
26
+ await surface.command('device.setLocation', (client) => client.settings.update({ setting: 'location', state: 'on' }), context.signal);
27
+ }
28
+ await surface.command('device.setLocation', (client) => client.settings.update({ setting: 'location', state: 'set', latitude, longitude }), context.signal);
29
+ },
30
+ async clearLocation() {
31
+ await surface.command('device.clearLocation', (client) => client.settings.update({ setting: 'location', state: 'off' }), context.signal);
32
+ },
33
+ async setAppearance(mode) {
34
+ await surface.command('device.setAppearance', (client) => client.settings.update({ setting: 'appearance', state: mode }), context.signal);
35
+ },
36
+ async setOrientation(orientation) {
37
+ await surface.screenCommand('device.setOrientation', (client) => client.command.orientation({ orientation }), context.signal);
38
+ },
39
+ async setBiometrics(sensor, result) {
40
+ await surface.command('device.setBiometrics', (client) => sensor === 'fingerprint'
41
+ ? client.settings.update({ setting: 'fingerprint', state: result })
42
+ : client.settings.update({ setting: sensor, state: result }), context.signal);
43
+ },
44
+ async enrollBiometrics(sensor, enrolled) {
45
+ await surface.command('device.enrollBiometrics', (client) => client.settings.update({ setting: sensor, state: enrolled ? 'enroll' : 'unenroll' }), context.signal);
46
+ },
47
+ async installApp(appPath, options) {
48
+ return surface.installApp(appPath, options ?? {}, context.signal);
49
+ },
50
+ async openApp(app, options) {
51
+ await surface.openApp(app, options ?? {}, context.signal);
52
+ },
53
+ async openLink(url, options) {
54
+ await surface.openLink(linkTarget(url), options?.app, context.signal);
55
+ },
56
+ async closeApp() {
57
+ await surface.closeApp(context.signal);
58
+ },
59
+ async clearKeychain() {
60
+ await surface.clearKeychain(context.signal);
61
+ },
62
+ async foregroundApp() {
63
+ const state = await surface.command('device.foregroundApp', (client) => client.command.appState({}), context.signal);
64
+ if ('package' in state)
65
+ return { name: state.package, bundleId: state.package };
66
+ return {
67
+ name: state.appName,
68
+ ...(state.appBundleId === undefined ? {} : { bundleId: state.appBundleId }),
69
+ };
70
+ },
71
+ async home() {
72
+ await surface.screenCommand('device.home', (client) => client.command.home({}), context.signal);
73
+ },
74
+ async back() {
75
+ await surface.screenCommand('device.back', (client) => client.command.back({ ...surface.settleOptions }), context.signal);
76
+ },
77
+ async alert(action) {
78
+ await surface.screenCommand('device.alert', (client) => client.command.alert({ action }), context.signal);
79
+ },
80
+ async dismissKeyboard() {
81
+ await surface.dismissKeyboard(context.signal);
82
+ },
83
+ async clipboard() {
84
+ const result = await surface.command('device.clipboard', (client) => client.command.clipboard({ action: 'read' }), context.signal);
85
+ return result.action === 'read' ? result.text : '';
86
+ },
87
+ async setClipboard(text) {
88
+ await surface.command('device.setClipboard', (client) => client.command.clipboard({ action: 'write', text }), context.signal);
89
+ },
90
+ };
91
+ const action = { kind: 'resource' };
92
+ return context.fixture('device', device, {
93
+ setNetwork: { ...action, label: (state) => state },
94
+ setAirplaneMode: action,
95
+ setPermission: { ...action, label: (permission) => permission },
96
+ setLocation: action,
97
+ clearLocation: action,
98
+ setAppearance: { ...action, label: (mode) => mode },
99
+ setOrientation: { ...action, label: (orientation) => orientation },
100
+ setBiometrics: action,
101
+ enrollBiometrics: action,
102
+ installApp: { ...action, label: (appPath) => appPath ?? surface.options.appPath ?? 'appPath' },
103
+ openApp: { ...action, label: (app) => linkLabel(app) },
104
+ openLink: { ...action, label: (url) => linkLabel(url) },
105
+ closeApp: action,
106
+ clearKeychain: action,
107
+ foregroundApp: action,
108
+ home: action,
109
+ back: action,
110
+ alert: { ...action, label: (value) => value },
111
+ dismissKeyboard: action,
112
+ clipboard: action,
113
+ setClipboard: { ...action, label: (text) => `${text.length} chars` },
114
+ });
115
+ }
116
+ //# sourceMappingURL=device.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.js","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The agent-device engine for e2e: a mobile body built with the
3
+ * public `defineEngine`, validated by the same rules and graded by the same
4
+ * capabilities as any other engine. Core imports nothing from here; this
5
+ * package imports the contract from `e2e/engine` and contributes the
6
+ * `device` fixture the way the browser engine contributes `web`.
7
+ */
8
+ import { type EngineHandle } from 'e2e/engine';
9
+ import type { MobileOptions } from './options.ts';
10
+ import { AgentDeviceSurface } from './surface.ts';
11
+ /** Assembles the manifest for one surface. Exported for tests that script the client. */
12
+ export declare function buildEngine(surface: AgentDeviceSurface): EngineHandle;
13
+ /** Creates one agent-device engine: one device session per worker; a test launches the pinned app with `app.open()`. */
14
+ export declare function mobile(options: MobileOptions): EngineHandle;
15
+ /** The surface behind a handle this package created; undefined for any other engine. */
16
+ export declare function surfaceOf(engine: EngineHandle): AgentDeviceSurface | undefined;
17
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAgD,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAE7F,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAIlD,yFAAyF;AACzF,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,YAAY,CAgDrE;AAED,wHAAwH;AACxH,wBAAgB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CAW3D;AAaD,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,kBAAkB,GAAG,SAAS,CAE9E"}
package/dist/engine.js ADDED
@@ -0,0 +1,98 @@
1
+ /**
2
+ * The agent-device engine for e2e: a mobile body built with the
3
+ * public `defineEngine`, validated by the same rules and graded by the same
4
+ * capabilities as any other engine. Core imports nothing from here; this
5
+ * package imports the contract from `e2e/engine` and contributes the
6
+ * `device` fixture the way the browser engine contributes `web`.
7
+ */
8
+ import { createRequire } from 'node:module';
9
+ import { createAgentDeviceClient } from 'agent-device';
10
+ import { defineEngine, obj } from 'e2e/engine';
11
+ import { createDeviceFixture } from './device.js';
12
+ import { DEVICE_ACTIONS, DEVICE_POINTER_ACTIONS } from './actions.js';
13
+ import { AgentDeviceSurface } from './surface.js';
14
+ const surfaces = new WeakMap();
15
+ /** Assembles the manifest for one surface. Exported for tests that script the client. */
16
+ export function buildEngine(surface) {
17
+ const handle = defineEngine({
18
+ name: 'mobile',
19
+ version: ownVersion(),
20
+ spiVersion: 1,
21
+ platform: surface.options.platform,
22
+ ...(surface.pool.size === undefined ? {} : { workers: surface.pool.size }),
23
+ prepare: (info) => surface.pool.prepare(info),
24
+ finish: (info) => surface.pool.finish(info),
25
+ init: (info) => surface.init(info),
26
+ startAttempt: (context) => surface.startAttempt(context),
27
+ endAttempt: (context) => surface.endAttempt(context),
28
+ dispose: (context) => surface.dispose(context),
29
+ observe: (operation, options) => surface.observe(operation, options),
30
+ locate: (expression, operation) => surface.locate(expression, operation),
31
+ perform: (ref, action, operation) => surface.perform(ref, action, operation),
32
+ actions: DEVICE_ACTIONS,
33
+ performAt: (point, action, operation) => surface.performAt(point, action, operation),
34
+ pointerActions: DEVICE_POINTER_ACTIONS,
35
+ keyboard: {
36
+ type: (text, keyboardOptions, operation) => surface.typeText(text, keyboardOptions, operation),
37
+ press: (key, operation) => surface.pressFocusedKey(key, operation),
38
+ dismiss: (operation) => surface.dismissKeyboard(operation.signal),
39
+ },
40
+ app: declaredApp(surface.options),
41
+ // No `open`: a device app has no URL to open, so the runner serves
42
+ // `app.open()` with `restart`, the fresh launch of the pinned app, which
43
+ // is also the device's "recreate the context"; restart and reset need one.
44
+ session: {
45
+ back: (operation) => surface.back(operation),
46
+ ...(surface.managesApp
47
+ ? {
48
+ restart: (operation) => surface.restart(operation),
49
+ reset: (operation) => surface.reset(operation),
50
+ }
51
+ : {}),
52
+ },
53
+ artifacts: {
54
+ screenshot: (label, operation) => surface.screenshot(label, operation),
55
+ startVideo: (operation) => surface.startVideo(operation),
56
+ stopVideo: (operation) => surface.stopVideo(operation),
57
+ },
58
+ fixtures: {
59
+ device: (context) => createDeviceFixture(surface, context),
60
+ },
61
+ });
62
+ surfaces.set(handle, surface);
63
+ return handle;
64
+ }
65
+ /** Creates one agent-device engine: one device session per worker; a test launches the pinned app with `app.open()`. */
66
+ export function mobile(options) {
67
+ const factory = (session, connection) => createAgentDeviceClient(obj({
68
+ ...connection?.client,
69
+ session,
70
+ daemonBaseUrl: connection?.daemon?.baseUrl,
71
+ daemonAuthToken: connection?.daemon?.authToken,
72
+ }));
73
+ return buildEngine(new AgentDeviceSurface(options, factory));
74
+ }
75
+ /**
76
+ * What the device engine declares about its app: the pinned app (else the
77
+ * build it installs) is the identity cache and session entries key on.
78
+ */
79
+ function declaredApp(options) {
80
+ return obj({
81
+ identity: options.identity ?? options.app ?? options.appPath,
82
+ environment: options.environment,
83
+ });
84
+ }
85
+ /** The surface behind a handle this package created; undefined for any other engine. */
86
+ export function surfaceOf(engine) {
87
+ return surfaces.get(engine);
88
+ }
89
+ /** This package's published version, read through require resolution. */
90
+ function ownVersion() {
91
+ try {
92
+ return createRequire(import.meta.url)('../package.json').version;
93
+ }
94
+ catch {
95
+ return 'unknown';
96
+ }
97
+ }
98
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Translation of agent-device failures onto the engine error contract.
3
+ *
4
+ * | agent-device failure | Code |
5
+ * | ----------------------------------------------------------- | ---------------------------- |
6
+ * | already an EngineError / runner error | passed through untouched |
7
+ * | AbortError | CANCELLED |
8
+ * | no active session / session not found / requires a session | INVALID_STATE |
9
+ * | UNSUPPORTED_OPERATION, NOT_IMPLEMENTED, UNSUPPORTED_PLATFORM | UNSUPPORTED_CAPABILITY |
10
+ * | "not supported on this device" under any code | UNSUPPORTED_CAPABILITY |
11
+ * | a ref the daemon no longer knows (action paths only) | NODE_STALE (retryable) |
12
+ * | the iOS runner busy, wedged, or past its watchdog | ENGINE_FAILURE, naming the |
13
+ * | a snapshot the iOS runner acquired but could not present | runner and the recovery |
14
+ * | anything else, agent-device's own timeouts included | ENGINE_FAILURE with the text |
15
+ *
16
+ * `OPERATION_TIMEOUT` is never produced here. In the contract it means the
17
+ * harness budget expired, which `raceAbort` already reports, and core
18
+ * replaces its message with a bare `operation timed out`. A timeout
19
+ * agent-device itself reports (a simulator boot, the runner connecting, an
20
+ * app launch) is a device-side fault whose text is the diagnosis, so it
21
+ * stays `ENGINE_FAILURE` and keeps its message and hint.
22
+ */
23
+ /** A failure's message in agent-device's normalized form. */
24
+ export declare function message(cause: unknown): string;
25
+ /**
26
+ * Translates an unexpected agent-device error at the contract boundary.
27
+ * `where` names the session and device the command ran under, for the
28
+ * messages whose recovery is per device.
29
+ */
30
+ export declare function translateError(cause: unknown, operation: string, where?: string): Error;
31
+ /**
32
+ * True for a translated failure of the iOS automation runner itself: busy,
33
+ * wedged, or past its watchdog. The runner, not the app, is what needs
34
+ * attention, so a caller that tolerates an app that did not open (warm-up)
35
+ * must not tolerate this.
36
+ */
37
+ export declare function isRunnerFailure(error: unknown): boolean;
38
+ /**
39
+ * True for a translated settings command agent-device refused because its
40
+ * session is on no app: the one refusal a foreground open of the pinned app
41
+ * answers.
42
+ */
43
+ export declare function isNoSessionApp(error: unknown): boolean;
44
+ /**
45
+ * True for a translated snapshot the iOS runner acquired but could not
46
+ * present. The check is per capture and the capture is a read, so one retry
47
+ * is safe and usually enough.
48
+ */
49
+ export declare function isSnapshotPresentationFailure(error: unknown): boolean;
50
+ /**
51
+ * Runs one agent-device command under an abort signal and translates its
52
+ * failure onto the engine taxonomy: the one boundary every command crosses.
53
+ */
54
+ export declare function runCommand<T>(label: string, work: () => Promise<T>, signal: AbortSignal, where?: string): Promise<T>;
55
+ /**
56
+ * Like translateError, but a ref the daemon no longer binds becomes retryable
57
+ * `NODE_STALE`: nothing was dispatched, so the harness may re-observe and
58
+ * re-resolve the node instead of failing the action.
59
+ */
60
+ export declare function staleOr(cause: unknown, operation: string, where?: string): Error;
61
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAiBH,6DAA6D;AAC7D,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE9C;AAwHD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAkBvF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEvD;AAID;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEtD;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAErE;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAM1H;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAOhF"}