@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.
- package/LICENSE +202 -0
- package/NOTICE +5 -0
- package/README.md +159 -0
- package/dist/.build.json +1 -0
- package/dist/actions.d.ts +41 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +70 -0
- package/dist/actions.js.map +1 -0
- package/dist/bindings.d.ts +87 -0
- package/dist/bindings.d.ts.map +1 -0
- package/dist/bindings.js +112 -0
- package/dist/bindings.js.map +1 -0
- package/dist/device.d.ts +117 -0
- package/dist/device.d.ts.map +1 -0
- package/dist/device.js +116 -0
- package/dist/device.js.map +1 -0
- package/dist/engine.d.ts +17 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +98 -0
- package/dist/engine.js.map +1 -0
- package/dist/errors.d.ts +61 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +223 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/links.d.ts +41 -0
- package/dist/links.d.ts.map +1 -0
- package/dist/links.js +79 -0
- package/dist/links.js.map +1 -0
- package/dist/locate.d.ts +11 -0
- package/dist/locate.d.ts.map +1 -0
- package/dist/locate.js +119 -0
- package/dist/locate.js.map +1 -0
- package/dist/nodes.d.ts +107 -0
- package/dist/nodes.d.ts.map +1 -0
- package/dist/nodes.js +530 -0
- package/dist/nodes.js.map +1 -0
- package/dist/options.d.ts +134 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +7 -0
- package/dist/options.js.map +1 -0
- package/dist/png.d.ts +4 -0
- package/dist/png.d.ts.map +1 -0
- package/dist/png.js +25 -0
- package/dist/png.js.map +1 -0
- package/dist/pool.d.ts +99 -0
- package/dist/pool.d.ts.map +1 -0
- package/dist/pool.js +235 -0
- package/dist/pool.js.map +1 -0
- package/dist/provider.d.ts +119 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +112 -0
- package/dist/provider.js.map +1 -0
- package/dist/selector.d.ts +17 -0
- package/dist/selector.d.ts.map +1 -0
- package/dist/selector.js +84 -0
- package/dist/selector.js.map +1 -0
- package/dist/support.d.ts +63 -0
- package/dist/support.d.ts.map +1 -0
- package/dist/support.js +87 -0
- package/dist/support.js.map +1 -0
- package/dist/surface.d.ts +405 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +1159 -0
- package/dist/surface.js.map +1 -0
- package/dist/tools.d.ts +24 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +90 -0
- package/dist/tools.js.map +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seam a hosted device service plugs into: a `DeviceProvider` leases one
|
|
3
|
+
* device per worker slot for the run and hands back the agent-device daemon
|
|
4
|
+
* that drives it. The engine acquires in `prepare`, drives each lease from
|
|
5
|
+
* its worker, and releases in `finish`, on every exit path. Nothing here
|
|
6
|
+
* knows any vendor: a hosted simulator service, a device farm, or a daemon on
|
|
7
|
+
* a machine down the hall are each one small provider in user code.
|
|
8
|
+
*/
|
|
9
|
+
import { type EngineFinishInfo, type EnginePrepareInfo } from 'e2e/engine';
|
|
10
|
+
import { type DeviceClientConfig, type DeviceDaemon, type DeviceSource, type SlotBinding } from './bindings.ts';
|
|
11
|
+
import type { MobileOptions, MobilePlatform } from './options.ts';
|
|
12
|
+
/** What the engine asks a provider for: one device for one worker slot of a run. */
|
|
13
|
+
export interface DeviceRequest {
|
|
14
|
+
/** Platform the target's device must run. */
|
|
15
|
+
readonly platform: MobilePlatform;
|
|
16
|
+
/** The run id, for naming the session at the provider. */
|
|
17
|
+
readonly runId: string;
|
|
18
|
+
/** The target the device serves. */
|
|
19
|
+
readonly targetName: string;
|
|
20
|
+
/** Worker slot the device serves, `0` to `slots - 1`; `slots` leases are acquired at once. */
|
|
21
|
+
readonly slot: number;
|
|
22
|
+
/** Devices the run acquires for this target, in all. */
|
|
23
|
+
readonly slots: number;
|
|
24
|
+
/** The `app` option: the bundle id or package `app.open()` launches, when the config names one. */
|
|
25
|
+
readonly app?: string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* The build the engine would install, resolved to an absolute path, when
|
|
28
|
+
* the config names one. A provider that installs it itself (uploading it,
|
|
29
|
+
* or naming a build it already hosts) reports `installedApp` on the lease
|
|
30
|
+
* and the engine skips its own install.
|
|
31
|
+
*/
|
|
32
|
+
readonly appPath?: string | undefined;
|
|
33
|
+
/** The run's environment: where a provider reads its token from, never `process.env`. */
|
|
34
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
35
|
+
/** Aborts on interrupt only; acquiring has no budget of its own. */
|
|
36
|
+
readonly signal: AbortSignal;
|
|
37
|
+
/** Reports one line of progress to the run's reporter (a session URL to watch). */
|
|
38
|
+
readonly log: (line: string) => void;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* One leased device, as `acquire` returns it and `release` gets it back.
|
|
42
|
+
* JSON data only: a lease travels from the runner process to the worker that
|
|
43
|
+
* drives it through the environment.
|
|
44
|
+
*/
|
|
45
|
+
export interface DeviceLease extends SlotBinding {
|
|
46
|
+
/**
|
|
47
|
+
* The provider's handle on the lease (a session id); named in progress
|
|
48
|
+
* lines and handed back to `release`. A provider may keep further fields on
|
|
49
|
+
* the object it returns: `release` gets that same object, while only the
|
|
50
|
+
* fields declared here travel to the worker.
|
|
51
|
+
*/
|
|
52
|
+
readonly id: string;
|
|
53
|
+
/**
|
|
54
|
+
* The agent-device daemon the worker connects to instead of its local one.
|
|
55
|
+
* A lease names a daemon, `client` configuration, or both.
|
|
56
|
+
*/
|
|
57
|
+
readonly daemon?: DeviceDaemon | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* agent-device client configuration the worker's client is created with:
|
|
60
|
+
* how a daemon that runs agent-device's own device-cloud runtimes tells the
|
|
61
|
+
* leased device apart, as its `leases.allocate` returned it (`tenant`,
|
|
62
|
+
* `runId`, `leaseId`, `leaseBackend`, `leaseProvider`), or the `stateDir` of
|
|
63
|
+
* a daemon the provider started. JSON data only; `session`, `daemonBaseUrl`,
|
|
64
|
+
* and `daemonAuthToken` are refused, the engine and `daemon` own those.
|
|
65
|
+
*/
|
|
66
|
+
readonly client?: DeviceClientConfig | undefined;
|
|
67
|
+
/** Device to select inside that daemon by name, when it hosts more than one. */
|
|
68
|
+
readonly device?: string | undefined;
|
|
69
|
+
/** Device to select inside that daemon by the id agent-device lists it under: a simulator UDID or an Android serial. */
|
|
70
|
+
readonly deviceId?: string | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Bundle id or package of the app the provider installed from the
|
|
73
|
+
* request's `appPath`, and only then: with it the engine installs nothing
|
|
74
|
+
* and, without an `app` option, `app.open()` launches this app. A lease
|
|
75
|
+
* reporting one for a request without `appPath` fails the run.
|
|
76
|
+
*/
|
|
77
|
+
readonly installedApp?: string | undefined;
|
|
78
|
+
}
|
|
79
|
+
/** Handed to `release`, once per lease at the end of the run. */
|
|
80
|
+
export interface DeviceReleaseContext {
|
|
81
|
+
readonly runId: string;
|
|
82
|
+
readonly targetName: string;
|
|
83
|
+
/** The run's environment, the same `acquire` saw. */
|
|
84
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
85
|
+
/** Aborts when the cleanup budget is spent. */
|
|
86
|
+
readonly signal: AbortSignal;
|
|
87
|
+
readonly log: (line: string) => void;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A source of hosted devices. `acquire` is called once per worker slot, for
|
|
91
|
+
* every slot at once; `release` once per lease acquired, at the end of the
|
|
92
|
+
* run, also after an `acquire` of another slot failed. Both run in the
|
|
93
|
+
* runner process, so a provider may keep state between them.
|
|
94
|
+
*/
|
|
95
|
+
export interface DeviceProvider {
|
|
96
|
+
/** Label in progress lines and error messages. */
|
|
97
|
+
readonly name: string;
|
|
98
|
+
acquire(request: DeviceRequest): Promise<DeviceLease>;
|
|
99
|
+
release(lease: DeviceLease, context: DeviceReleaseContext): Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
/** Narrows an intended provider, or names what it is missing. */
|
|
102
|
+
export declare function asDeviceProvider(device: object): DeviceProvider;
|
|
103
|
+
/**
|
|
104
|
+
* Devices a provider leases, one per slot, all at once: hosted sessions start
|
|
105
|
+
* in parallel and are billed from the moment they do, so nothing waits on a
|
|
106
|
+
* sibling. Every lease granted is held for `finish` before the outcome is
|
|
107
|
+
* known, so a slot that fails to lease fails the run and the others are
|
|
108
|
+
* still released, each one whatever happened to the rest.
|
|
109
|
+
*/
|
|
110
|
+
export declare class LeasedDevices implements DeviceSource {
|
|
111
|
+
private readonly provider;
|
|
112
|
+
private readonly options;
|
|
113
|
+
/** Leases granted so far, per target, filled as each `acquire` settles; a lease the engine then rejects is among them. */
|
|
114
|
+
private readonly held;
|
|
115
|
+
constructor(provider: DeviceProvider, options: Pick<MobileOptions, 'platform' | 'app' | 'appPath'>);
|
|
116
|
+
bind(info: EnginePrepareInfo): Promise<readonly SlotBinding[]>;
|
|
117
|
+
finish(info: EngineFinishInfo): Promise<void>;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seam a hosted device service plugs into: a `DeviceProvider` leases one
|
|
3
|
+
* device per worker slot for the run and hands back the agent-device daemon
|
|
4
|
+
* that drives it. The engine acquires in `prepare`, drives each lease from
|
|
5
|
+
* its worker, and releases in `finish`, on every exit path. Nothing here
|
|
6
|
+
* knows any vendor: a hosted simulator service, a device farm, or a daemon on
|
|
7
|
+
* a machine down the hall are each one small provider in user code.
|
|
8
|
+
*/
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { ConfigurationError, EngineError, obj } from 'e2e/engine';
|
|
11
|
+
import { isSlotBinding } from './bindings.js';
|
|
12
|
+
import { message } from './errors.js';
|
|
13
|
+
import { isLink } from './links.js';
|
|
14
|
+
/** Narrows an intended provider, or names what it is missing. */
|
|
15
|
+
export function asDeviceProvider(device) {
|
|
16
|
+
const candidate = device;
|
|
17
|
+
if (typeof candidate.name !== 'string' || candidate.name.trim() === '') {
|
|
18
|
+
throw new ConfigurationError('INVALID_CONFIG', 'mobile: a `device` provider needs a non-empty `name`');
|
|
19
|
+
}
|
|
20
|
+
for (const member of ['acquire', 'release']) {
|
|
21
|
+
if (typeof candidate[member] !== 'function') {
|
|
22
|
+
throw new ConfigurationError('INVALID_CONFIG', `mobile: device provider "${candidate.name}" must implement ${member}()`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return device;
|
|
26
|
+
}
|
|
27
|
+
/** An object with a lease id: something the provider granted and `release` is owed, whatever the field check makes of it. */
|
|
28
|
+
function isGrantedLease(value) {
|
|
29
|
+
return typeof value === 'object' && value !== null && typeof value.id === 'string';
|
|
30
|
+
}
|
|
31
|
+
/** A granted lease checked field by field: the engine trusts nothing it did not write. */
|
|
32
|
+
function isDeviceLease(lease) {
|
|
33
|
+
return isSlotBinding(lease) && (lease.daemon !== undefined || lease.client !== undefined);
|
|
34
|
+
}
|
|
35
|
+
const REJECTED_LEASE = 'returned a lease without an id and a daemon baseUrl or JSON client configuration (no `session` or daemon keys)';
|
|
36
|
+
/** Runs every task, then reports the first failure: nothing is skipped because a sibling failed. */
|
|
37
|
+
async function allOrFirstFailure(tasks, describe) {
|
|
38
|
+
// `async` so a task that throws before its first await is a rejection like any other, not an escape.
|
|
39
|
+
const settled = await Promise.allSettled(tasks.map(async (task) => task()));
|
|
40
|
+
const failed = settled.find((result) => result.status === 'rejected');
|
|
41
|
+
if (failed !== undefined) {
|
|
42
|
+
throw new EngineError('ENGINE_FAILURE', `${describe}: ${message(failed.reason)}`, { retryable: false, cause: failed.reason });
|
|
43
|
+
}
|
|
44
|
+
return settled.flatMap((result) => (result.status === 'fulfilled' ? [result.value] : []));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Devices a provider leases, one per slot, all at once: hosted sessions start
|
|
48
|
+
* in parallel and are billed from the moment they do, so nothing waits on a
|
|
49
|
+
* sibling. Every lease granted is held for `finish` before the outcome is
|
|
50
|
+
* known, so a slot that fails to lease fails the run and the others are
|
|
51
|
+
* still released, each one whatever happened to the rest.
|
|
52
|
+
*/
|
|
53
|
+
export class LeasedDevices {
|
|
54
|
+
provider;
|
|
55
|
+
options;
|
|
56
|
+
/** Leases granted so far, per target, filled as each `acquire` settles; a lease the engine then rejects is among them. */
|
|
57
|
+
held = new Map();
|
|
58
|
+
constructor(provider, options) {
|
|
59
|
+
this.provider = provider;
|
|
60
|
+
this.options = options;
|
|
61
|
+
}
|
|
62
|
+
async bind(info) {
|
|
63
|
+
const { provider } = this;
|
|
64
|
+
const { platform, app } = this.options;
|
|
65
|
+
const appPath = this.options.appPath === undefined ? undefined : path.resolve(info.projectRoot, this.options.appPath);
|
|
66
|
+
const held = [];
|
|
67
|
+
this.held.set(info.targetName, held);
|
|
68
|
+
info.log(`leasing ${info.slots} ${platform} device(s) from ${provider.name}`);
|
|
69
|
+
const leases = await allOrFirstFailure(Array.from({ length: info.slots }, (_, slot) => async () => {
|
|
70
|
+
const lease = await provider.acquire({
|
|
71
|
+
platform,
|
|
72
|
+
runId: info.runId,
|
|
73
|
+
targetName: info.targetName,
|
|
74
|
+
slot,
|
|
75
|
+
slots: info.slots,
|
|
76
|
+
app,
|
|
77
|
+
appPath,
|
|
78
|
+
env: info.env,
|
|
79
|
+
signal: info.signal,
|
|
80
|
+
log: (line) => info.log(`${provider.name} (${slot + 1} of ${info.slots}): ${line}`),
|
|
81
|
+
});
|
|
82
|
+
if (!isGrantedLease(lease))
|
|
83
|
+
throw new Error(REJECTED_LEASE);
|
|
84
|
+
// Held before it is checked: the provider bills for what it granted,
|
|
85
|
+
// and `release` is owed this same object however the check goes.
|
|
86
|
+
held.push(lease);
|
|
87
|
+
if (!isDeviceLease(lease))
|
|
88
|
+
throw new Error(REJECTED_LEASE);
|
|
89
|
+
if (lease.installedApp !== undefined && appPath === undefined) {
|
|
90
|
+
throw new Error('reported an installed app for a request without `appPath`');
|
|
91
|
+
}
|
|
92
|
+
// The pool opens this on the device in `prepare`; agent-device would open a link as a URL.
|
|
93
|
+
if (lease.installedApp !== undefined && isLink(lease.installedApp)) {
|
|
94
|
+
throw new Error('reported an installed app that is a link, not a bundle id or package');
|
|
95
|
+
}
|
|
96
|
+
info.log(`${provider.name}: leased ${lease.id}${lease.device === undefined ? '' : ` (${lease.device})`}`);
|
|
97
|
+
return lease;
|
|
98
|
+
}), `device provider "${provider.name}" could not lease a device`);
|
|
99
|
+
return leases.map((lease) => obj({ device: lease.device, deviceId: lease.deviceId, daemon: lease.daemon, client: lease.client, installedApp: lease.installedApp }));
|
|
100
|
+
}
|
|
101
|
+
async finish(info) {
|
|
102
|
+
const { provider } = this;
|
|
103
|
+
const leases = this.held.get(info.targetName);
|
|
104
|
+
this.held.delete(info.targetName);
|
|
105
|
+
if (leases === undefined || leases.length === 0)
|
|
106
|
+
return;
|
|
107
|
+
const context = { runId: info.runId, targetName: info.targetName, env: info.env, signal: info.signal, log: (line) => info.log(`${provider.name}: ${line}`) };
|
|
108
|
+
await allOrFirstFailure(leases.map((lease) => () => provider.release(lease, context)), `device provider "${provider.name}" could not release a device`);
|
|
109
|
+
info.log(`${provider.name}: released ${leases.length} device(s)`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,GAAG,EAAiD,MAAM,YAAY,CAAC;AACjH,OAAO,EAAE,aAAa,EAAmF,MAAM,eAAe,CAAC;AAC/H,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAgGpC,iEAAiE;AACjE,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,SAAS,GAAG,MAAwD,CAAC;IAC3E,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACvE,MAAM,IAAI,kBAAkB,CAAC,gBAAgB,EAAE,sDAAsD,CAAC,CAAC;IACzG,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,CAAC,SAAS,EAAE,SAAS,CAAU,EAAE,CAAC;QACrD,IAAI,OAAO,SAAS,CAAC,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC;YAC5C,MAAM,IAAI,kBAAkB,CAAC,gBAAgB,EAAE,4BAA4B,SAAS,CAAC,IAAI,oBAAoB,MAAM,IAAI,CAAC,CAAC;QAC3H,CAAC;IACH,CAAC;IACD,OAAO,MAAwB,CAAC;AAClC,CAAC;AAED,6HAA6H;AAC7H,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,OAAQ,KAA0B,CAAC,EAAE,KAAK,QAAQ,CAAC;AAC3G,CAAC;AAED,0FAA0F;AAC1F,SAAS,aAAa,CAAC,KAA8B;IACnD,OAAO,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;AAC5F,CAAC;AAED,MAAM,cAAc,GAAG,gHAAgH,CAAC;AAExI,oGAAoG;AACpG,KAAK,UAAU,iBAAiB,CAAI,KAAoC,EAAE,QAAgB;IACxF,qGAAqG;IACrG,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;IACtE,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,WAAW,CAAC,gBAAgB,EAAE,GAAG,QAAQ,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAChI,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC5F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IAKL,QAAQ;IACR,OAAO;IAL1B,0HAA0H;IACzG,IAAI,GAAG,IAAI,GAAG,EAAyB,CAAC;IAEzD,YACmB,QAAwB,EACxB,OAA4D;wBAD5D,QAAQ;uBACR,OAAO;IACvB,CAAC;IAEJ,KAAK,CAAC,IAAI,CAAC,IAAuB;QAChC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;QAC1B,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACtH,MAAM,IAAI,GAAkB,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,KAAK,IAAI,QAAQ,mBAAmB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9E,MAAM,MAAM,GAAG,MAAM,iBAAiB,CACpC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE;YACzD,MAAM,KAAK,GAAY,MAAM,QAAQ,CAAC,OAAO,CAAC;gBAC5C,QAAQ;gBACR,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,IAAI;gBACJ,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,GAAG;gBACH,OAAO;gBACP,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,KAAK,IAAI,GAAG,CAAC,OAAO,IAAI,CAAC,KAAK,MAAM,IAAI,EAAE,CAAC;aACpF,CAAC,CAAC;YACH,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;YAC5D,qEAAqE;YACrE,iEAAiE;YACjE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjB,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;YAC3D,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC9D,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;YAC/E,CAAC;YACD,2FAA2F;YAC3F,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;gBACnE,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAC;YAC1F,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,YAAY,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;YAC1G,OAAO,KAAK,CAAC;QACf,CAAC,CAAC,EACF,oBAAoB,QAAQ,CAAC,IAAI,4BAA4B,CAC9D,CAAC;QACF,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IACtK,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAsB;QACjC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC9C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACxD,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;QACrK,MAAM,iBAAiB,CACrB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,EAC7D,oBAAoB,QAAQ,CAAC,IAAI,8BAA8B,CAChE,CAAC;QACF,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,cAAc,MAAM,CAAC,MAAM,YAAY,CAAC,CAAC;IACpE,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-device selector support for the `selector` locator expression:
|
|
3
|
+
* `screen.locator('id=save role=button')` and the structural hint on every
|
|
4
|
+
* observed node. The grammar is agent-device's own (`parseSelectorChain`), so a
|
|
5
|
+
* selector that works on the agent-device CLI works here unchanged; matching
|
|
6
|
+
* runs against the projected snapshot, one immediate pass, every match returned.
|
|
7
|
+
*/
|
|
8
|
+
import { type ProjectedNode } from './nodes.ts';
|
|
9
|
+
export type CompiledSelector = (entries: readonly ProjectedNode[]) => ProjectedNode[];
|
|
10
|
+
/**
|
|
11
|
+
* Compiles one selector string. Alternatives (agent-device's fallback chain)
|
|
12
|
+
* are tried in order and the first that matches anything wins, mirroring how
|
|
13
|
+
* agent-device itself resolves a chain; a term's text compares exactly after
|
|
14
|
+
* whitespace collapsing, like every other text rule in the runner.
|
|
15
|
+
*/
|
|
16
|
+
export declare function compileSelector(raw: string): CompiledSelector;
|
|
17
|
+
//# sourceMappingURL=selector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector.d.ts","sourceRoot":"","sources":["../src/selector.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAQ/D,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,KAAK,aAAa,EAAE,CAAC;AAEtF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAiB7D"}
|
package/dist/selector.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-device selector support for the `selector` locator expression:
|
|
3
|
+
* `screen.locator('id=save role=button')` and the structural hint on every
|
|
4
|
+
* observed node. The grammar is agent-device's own (`parseSelectorChain`), so a
|
|
5
|
+
* selector that works on the agent-device CLI works here unchanged; matching
|
|
6
|
+
* runs against the projected snapshot, one immediate pass, every match returned.
|
|
7
|
+
*/
|
|
8
|
+
import { parseSelectorChain } from 'agent-device/selectors';
|
|
9
|
+
import { EngineError } from 'e2e/engine';
|
|
10
|
+
import { normalizeKind } from './nodes.js';
|
|
11
|
+
import { message } from './errors.js';
|
|
12
|
+
/**
|
|
13
|
+
* Compiles one selector string. Alternatives (agent-device's fallback chain)
|
|
14
|
+
* are tried in order and the first that matches anything wins, mirroring how
|
|
15
|
+
* agent-device itself resolves a chain; a term's text compares exactly after
|
|
16
|
+
* whitespace collapsing, like every other text rule in the runner.
|
|
17
|
+
*/
|
|
18
|
+
export function compileSelector(raw) {
|
|
19
|
+
let chain;
|
|
20
|
+
try {
|
|
21
|
+
chain = parseSelectorChain(raw);
|
|
22
|
+
}
|
|
23
|
+
catch (cause) {
|
|
24
|
+
throw new EngineError('ENGINE_FAILURE', `invalid agent-device selector ${JSON.stringify(raw)}: ${message(cause)}`, {
|
|
25
|
+
retryable: false,
|
|
26
|
+
cause,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return (entries) => {
|
|
30
|
+
for (const alternative of chain.selectors) {
|
|
31
|
+
const matches = entries.filter((entry) => alternative.terms.every((term) => matchesTerm(entry, term)));
|
|
32
|
+
if (matches.length > 0)
|
|
33
|
+
return matches;
|
|
34
|
+
}
|
|
35
|
+
return [];
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function normalize(text) {
|
|
39
|
+
return text.replaceAll(/\s+/g, ' ').trim();
|
|
40
|
+
}
|
|
41
|
+
function textEquals(actual, expected) {
|
|
42
|
+
return actual !== undefined && normalize(actual) === normalize(String(expected));
|
|
43
|
+
}
|
|
44
|
+
function flag(value) {
|
|
45
|
+
return value === true || String(value).toLowerCase() === 'true';
|
|
46
|
+
}
|
|
47
|
+
function matchesTerm(entry, term) {
|
|
48
|
+
const states = entry.node.states ?? {};
|
|
49
|
+
switch (term.key) {
|
|
50
|
+
case 'id':
|
|
51
|
+
return textEquals(entry.raw.identifier, term.value);
|
|
52
|
+
case 'role': {
|
|
53
|
+
const wanted = normalizeKind(String(term.value));
|
|
54
|
+
return entry.kind === wanted || entry.node.role === wanted;
|
|
55
|
+
}
|
|
56
|
+
case 'text':
|
|
57
|
+
return textEquals(entry.raw.label, term.value) || textEquals(entry.raw.value, term.value);
|
|
58
|
+
case 'label':
|
|
59
|
+
return textEquals(entry.raw.label, term.value);
|
|
60
|
+
case 'value':
|
|
61
|
+
return textEquals(entry.raw.value, term.value);
|
|
62
|
+
case 'appname':
|
|
63
|
+
return textEquals(entry.raw.appName, term.value);
|
|
64
|
+
case 'windowtitle':
|
|
65
|
+
return textEquals(entry.raw.windowTitle, term.value);
|
|
66
|
+
case 'visible':
|
|
67
|
+
return (states.hidden !== true) === flag(term.value);
|
|
68
|
+
case 'hidden':
|
|
69
|
+
return (states.hidden === true) === flag(term.value);
|
|
70
|
+
case 'editable':
|
|
71
|
+
return (entry.node.role === 'textbox') === flag(term.value);
|
|
72
|
+
case 'selected':
|
|
73
|
+
return (states.selected === true) === flag(term.value);
|
|
74
|
+
case 'focused':
|
|
75
|
+
return (states.focused === true) === flag(term.value);
|
|
76
|
+
case 'enabled':
|
|
77
|
+
return (states.disabled !== true) === flag(term.value);
|
|
78
|
+
case 'hittable':
|
|
79
|
+
return (entry.raw.hittable !== false) === flag(term.value);
|
|
80
|
+
default:
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector.js","sourceRoot":"","sources":["../src/selector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Shared helpers for the agent-device engine: error constructors, filenames, PNG headers, gestures, the screen location and size. */
|
|
2
|
+
import { EngineError, type Momentum, type ScrollDirection, type ViewportSize } from 'e2e/engine';
|
|
3
|
+
export interface Point {
|
|
4
|
+
readonly x: number;
|
|
5
|
+
readonly y: number;
|
|
6
|
+
}
|
|
7
|
+
export interface Rect {
|
|
8
|
+
readonly x: number;
|
|
9
|
+
readonly y: number;
|
|
10
|
+
readonly width: number;
|
|
11
|
+
readonly height: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function cancelled(text: string): EngineError;
|
|
14
|
+
export declare function invalidState(text: string): EngineError;
|
|
15
|
+
export declare function notActionable(text: string): EngineError;
|
|
16
|
+
export declare function unsupported(text: string): EngineError;
|
|
17
|
+
/** Constrains a caller-supplied artifact label to a safe filename. */
|
|
18
|
+
export declare function sanitizeFilename(name: string): string;
|
|
19
|
+
/** Reads the dimensions out of a PNG's IHDR chunk; undefined for anything else. */
|
|
20
|
+
export declare function readPngSize(data: Uint8Array): {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
} | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* The finger gesture that scrolls one rect's content in `direction`. Scrolling
|
|
26
|
+
* down reveals what is below, so the finger travels up; the travel is a share
|
|
27
|
+
* of the rect's extent scaled by momentum, and never leaves the rect. The
|
|
28
|
+
* root scroll sends no share (agent-device's default, see `perform`); these
|
|
29
|
+
* are for a gesture inside one control, and deterministic tests step through
|
|
30
|
+
* a list with `slow`, a quarter of the box, shorter than any row, so every
|
|
31
|
+
* row lands fully on screen at some stop. At three quarters the mobile
|
|
32
|
+
* benchmark's scenario list dragged rows past.
|
|
33
|
+
*/
|
|
34
|
+
export declare function swipeWithin(rect: Rect, direction: ScrollDirection, momentum: Momentum | undefined): {
|
|
35
|
+
from: Point;
|
|
36
|
+
to: Point;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* The location a device surface reports in a snapshot: the foreground app
|
|
40
|
+
* and the screen shown, `<app> / <screen title>`. A simulator has no address
|
|
41
|
+
* bar, so this is an opaque address the harness shows to the model and the
|
|
42
|
+
* report and anchors trace replay on; it changes exactly when the app or its
|
|
43
|
+
* screen changes. It is deliberately not a URL: no scheme, no colon, so the
|
|
44
|
+
* harness never mistakes it for one and applies an origin policy to it.
|
|
45
|
+
* Undefined when neither the app nor a title is known.
|
|
46
|
+
*/
|
|
47
|
+
export declare function screenLocation(app: string | undefined, title: string | undefined): string | undefined;
|
|
48
|
+
/** The screenshot fields a viewport probe reads off agent-device's response. */
|
|
49
|
+
export interface RawScreenshotResult {
|
|
50
|
+
readonly path?: string;
|
|
51
|
+
readonly width?: number;
|
|
52
|
+
readonly height?: number;
|
|
53
|
+
readonly logicalWidth?: number;
|
|
54
|
+
readonly logicalHeight?: number;
|
|
55
|
+
readonly pixelDensity?: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The device's logical screen size as its screenshot reports it, in points:
|
|
59
|
+
* the logical dimensions when given, else the pixel dimensions over the
|
|
60
|
+
* density. Undefined when the response carries neither.
|
|
61
|
+
*/
|
|
62
|
+
export declare function logicalScreenSize(result: RawScreenshotResult): ViewportSize | undefined;
|
|
63
|
+
//# sourceMappingURL=support.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"support.d.ts","sourceRoot":"","sources":["../src/support.ts"],"names":[],"mappings":"AAAA,sIAAsI;AAEtI,OAAO,EAAE,WAAW,EAAE,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAEjG,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAEnD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAEtD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAEvD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAErD;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAID,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAO3F;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAC7B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,KAAK,CAAA;CAAE,CAe5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAKrG;AAED,gFAAgF;AAChF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,mBAAmB,GAC1B,YAAY,GAAG,SAAS,CAS1B"}
|
package/dist/support.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** Shared helpers for the agent-device engine: error constructors, filenames, PNG headers, gestures, the screen location and size. */
|
|
2
|
+
import { EngineError } from 'e2e/engine';
|
|
3
|
+
export function cancelled(text) {
|
|
4
|
+
return new EngineError('CANCELLED', text, { retryable: false });
|
|
5
|
+
}
|
|
6
|
+
export function invalidState(text) {
|
|
7
|
+
return new EngineError('INVALID_STATE', text, { retryable: false });
|
|
8
|
+
}
|
|
9
|
+
export function notActionable(text) {
|
|
10
|
+
return new EngineError('NOT_ACTIONABLE', text, { retryable: false });
|
|
11
|
+
}
|
|
12
|
+
export function unsupported(text) {
|
|
13
|
+
return new EngineError('UNSUPPORTED_CAPABILITY', text, { retryable: false });
|
|
14
|
+
}
|
|
15
|
+
/** Constrains a caller-supplied artifact label to a safe filename. */
|
|
16
|
+
export function sanitizeFilename(name) {
|
|
17
|
+
return name.replaceAll(/[^A-Za-z0-9._-]/g, '_').slice(0, 64) || 'artifact';
|
|
18
|
+
}
|
|
19
|
+
const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
20
|
+
/** Reads the dimensions out of a PNG's IHDR chunk; undefined for anything else. */
|
|
21
|
+
export function readPngSize(data) {
|
|
22
|
+
if (data.byteLength < 24)
|
|
23
|
+
return undefined;
|
|
24
|
+
for (const [offset, byte] of PNG_SIGNATURE.entries()) {
|
|
25
|
+
if (data[offset] !== byte)
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
29
|
+
return { width: view.getUint32(16), height: view.getUint32(20) };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The finger gesture that scrolls one rect's content in `direction`. Scrolling
|
|
33
|
+
* down reveals what is below, so the finger travels up; the travel is a share
|
|
34
|
+
* of the rect's extent scaled by momentum, and never leaves the rect. The
|
|
35
|
+
* root scroll sends no share (agent-device's default, see `perform`); these
|
|
36
|
+
* are for a gesture inside one control, and deterministic tests step through
|
|
37
|
+
* a list with `slow`, a quarter of the box, shorter than any row, so every
|
|
38
|
+
* row lands fully on screen at some stop. At three quarters the mobile
|
|
39
|
+
* benchmark's scenario list dragged rows past.
|
|
40
|
+
*/
|
|
41
|
+
export function swipeWithin(rect, direction, momentum) {
|
|
42
|
+
const ratio = momentum === 'fast' ? 0.8 : momentum === 'slow' ? 0.25 : 0.5;
|
|
43
|
+
const centre = { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };
|
|
44
|
+
const dx = (rect.width * ratio) / 2;
|
|
45
|
+
const dy = (rect.height * ratio) / 2;
|
|
46
|
+
switch (direction) {
|
|
47
|
+
case 'down':
|
|
48
|
+
return { from: { x: centre.x, y: centre.y + dy }, to: { x: centre.x, y: centre.y - dy } };
|
|
49
|
+
case 'up':
|
|
50
|
+
return { from: { x: centre.x, y: centre.y - dy }, to: { x: centre.x, y: centre.y + dy } };
|
|
51
|
+
case 'right':
|
|
52
|
+
return { from: { x: centre.x + dx, y: centre.y }, to: { x: centre.x - dx, y: centre.y } };
|
|
53
|
+
case 'left':
|
|
54
|
+
return { from: { x: centre.x - dx, y: centre.y }, to: { x: centre.x + dx, y: centre.y } };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The location a device surface reports in a snapshot: the foreground app
|
|
59
|
+
* and the screen shown, `<app> / <screen title>`. A simulator has no address
|
|
60
|
+
* bar, so this is an opaque address the harness shows to the model and the
|
|
61
|
+
* report and anchors trace replay on; it changes exactly when the app or its
|
|
62
|
+
* screen changes. It is deliberately not a URL: no scheme, no colon, so the
|
|
63
|
+
* harness never mistakes it for one and applies an origin policy to it.
|
|
64
|
+
* Undefined when neither the app nor a title is known.
|
|
65
|
+
*/
|
|
66
|
+
export function screenLocation(app, title) {
|
|
67
|
+
const parts = [app, title]
|
|
68
|
+
.map((part) => (part === undefined ? '' : part.replaceAll(/\s+/g, ' ').trim()))
|
|
69
|
+
.filter((part) => part !== '');
|
|
70
|
+
return parts.length === 0 ? undefined : parts.join(' / ');
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The device's logical screen size as its screenshot reports it, in points:
|
|
74
|
+
* the logical dimensions when given, else the pixel dimensions over the
|
|
75
|
+
* density. Undefined when the response carries neither.
|
|
76
|
+
*/
|
|
77
|
+
export function logicalScreenSize(result) {
|
|
78
|
+
const logical = result.logicalWidth !== undefined && result.logicalHeight !== undefined
|
|
79
|
+
? { width: result.logicalWidth, height: result.logicalHeight }
|
|
80
|
+
: result.width !== undefined && result.height !== undefined && result.pixelDensity !== undefined && result.pixelDensity > 0
|
|
81
|
+
? { width: result.width / result.pixelDensity, height: result.height / result.pixelDensity }
|
|
82
|
+
: undefined;
|
|
83
|
+
if (logical === undefined || logical.width <= 0 || logical.height <= 0)
|
|
84
|
+
return undefined;
|
|
85
|
+
return logical;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=support.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"support.js","sourceRoot":"","sources":["../src/support.ts"],"names":[],"mappings":"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"}
|