@e2e-dev/mobile 0.10.0 → 0.11.0-nightly-20261009094406
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/dist/.build.json +1 -1
- package/dist/surface.d.ts +8 -0
- package/dist/surface.js +41 -7
- package/package.json +3 -3
package/dist/.build.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.
|
|
1
|
+
{"version":"0.11.0-nightly-20261009094406"}
|
package/dist/surface.d.ts
CHANGED
|
@@ -68,6 +68,8 @@ export declare class AgentDeviceSurface {
|
|
|
68
68
|
private readonly createClient;
|
|
69
69
|
private client;
|
|
70
70
|
private attempt;
|
|
71
|
+
/** The attempt's `EngineAttemptContext.screen`, which locate captures feed; undefined between attempts. */
|
|
72
|
+
private screenSink;
|
|
71
73
|
/** The device this worker drives, the pool's binding for its slot; undefined leaves the choice to agent-device. */
|
|
72
74
|
private device;
|
|
73
75
|
private generation;
|
|
@@ -293,6 +295,12 @@ export declare class AgentDeviceSurface {
|
|
|
293
295
|
observe(operation: OperationContext, options?: EngineObserveOptions): Promise<EngineSnapshot>;
|
|
294
296
|
/** Retains only action bindings for matches, independently of the observation generation. */
|
|
295
297
|
locate(expression: LocatorExpression, operation: OperationContext): Promise<readonly SemanticNode[]>;
|
|
298
|
+
/**
|
|
299
|
+
* Hands the harness the capture a locate matched against, for the trace:
|
|
300
|
+
* the screen the step acted on, at no cost of another snapshot. A screen
|
|
301
|
+
* whose viewport is not known yet is skipped rather than probed.
|
|
302
|
+
*/
|
|
303
|
+
private traceScreen;
|
|
296
304
|
private resolveRef;
|
|
297
305
|
private actionTarget;
|
|
298
306
|
/**
|
package/dist/surface.js
CHANGED
|
@@ -165,11 +165,22 @@ function settleOptions(settle) {
|
|
|
165
165
|
}
|
|
166
166
|
return { settle: true, settleQuietMs: quietMs };
|
|
167
167
|
}
|
|
168
|
+
/** Calls one of the harness's trace hooks; a hook that throws, or one an older harness lacks, never fails the device's work. */
|
|
169
|
+
function tellHarness(call) {
|
|
170
|
+
try {
|
|
171
|
+
call();
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// The trace is a reader of the attempt, never a reason it fails.
|
|
175
|
+
}
|
|
176
|
+
}
|
|
168
177
|
export class AgentDeviceSurface {
|
|
169
178
|
options;
|
|
170
179
|
createClient;
|
|
171
180
|
client;
|
|
172
181
|
attempt;
|
|
182
|
+
/** The attempt's `EngineAttemptContext.screen`, which locate captures feed; undefined between attempts. */
|
|
183
|
+
screenSink;
|
|
173
184
|
/** The device this worker drives, the pool's binding for its slot; undefined leaves the choice to agent-device. */
|
|
174
185
|
device;
|
|
175
186
|
generation = new Map();
|
|
@@ -346,11 +357,15 @@ export class AgentDeviceSurface {
|
|
|
346
357
|
this.attempt = { attemptId: context.attemptId, artifactsDir: context.artifactsDir, screenshots: 0, video: undefined };
|
|
347
358
|
this.generation = new Map();
|
|
348
359
|
this.located.clear();
|
|
360
|
+
this.screenSink = context.screen;
|
|
361
|
+
const device = deviceLabel(this.device);
|
|
362
|
+
tellHarness(() => context.environment?.({ platform: this.options.platform, ...(device === undefined ? {} : { device }) }));
|
|
349
363
|
}
|
|
350
364
|
async endAttempt(context) {
|
|
351
365
|
const attempt = this.attempt;
|
|
352
366
|
const dangling = attempt?.video;
|
|
353
367
|
this.attempt = undefined;
|
|
368
|
+
this.screenSink = undefined;
|
|
354
369
|
this.generation = new Map();
|
|
355
370
|
this.located.clear();
|
|
356
371
|
// The harness stops the video before it ends the attempt; a recording still
|
|
@@ -737,7 +752,7 @@ export class AgentDeviceSurface {
|
|
|
737
752
|
this.generation = new Map(projected.index.map((entry) => [entry.id, this.bind(entry, projected.index)]));
|
|
738
753
|
const viewport = await this.viewportFor(projected, operation.signal);
|
|
739
754
|
const location = screenLocation(raw.appBundleId ?? raw.appName ?? this.appIdentity, screenTitle(projected));
|
|
740
|
-
const capture = options?.pixels === true ? await this.capturePixels(operation, projected, viewport) : undefined;
|
|
755
|
+
const capture = options?.pixels === true ? await this.capturePixels(operation, projected, viewport, { normalizeStatusBar: options.comparable === true }) : undefined;
|
|
741
756
|
return {
|
|
742
757
|
root: screenRoot(projected.roots, viewport),
|
|
743
758
|
viewport,
|
|
@@ -750,6 +765,7 @@ export class AgentDeviceSurface {
|
|
|
750
765
|
async locate(expression, operation) {
|
|
751
766
|
const raw = await this.snapshotOrEmpty(operation, false);
|
|
752
767
|
const projected = this.project(raw);
|
|
768
|
+
this.traceScreen(raw, projected);
|
|
753
769
|
const matches = resolveExpression(expression, projected.index);
|
|
754
770
|
for (const entry of matches)
|
|
755
771
|
this.located.set(entry.id, this.bind(entry, projected.index));
|
|
@@ -760,6 +776,24 @@ export class AgentDeviceSurface {
|
|
|
760
776
|
}
|
|
761
777
|
return matches.map((entry) => entry.node);
|
|
762
778
|
}
|
|
779
|
+
/**
|
|
780
|
+
* Hands the harness the capture a locate matched against, for the trace:
|
|
781
|
+
* the screen the step acted on, at no cost of another snapshot. A screen
|
|
782
|
+
* whose viewport is not known yet is skipped rather than probed.
|
|
783
|
+
*/
|
|
784
|
+
traceScreen(raw, projected) {
|
|
785
|
+
const sink = this.screenSink;
|
|
786
|
+
const viewport = projected.viewport ?? this.knownViewport;
|
|
787
|
+
if (sink === undefined || viewport === undefined)
|
|
788
|
+
return;
|
|
789
|
+
const location = screenLocation(raw.appBundleId ?? raw.appName ?? this.appIdentity, screenTitle(projected));
|
|
790
|
+
tellHarness(() => sink({
|
|
791
|
+
root: screenRoot(projected.roots, viewport),
|
|
792
|
+
viewport,
|
|
793
|
+
...(isTruncated(raw) ? { truncated: true } : {}),
|
|
794
|
+
...(location === undefined ? {} : { location }),
|
|
795
|
+
}));
|
|
796
|
+
}
|
|
763
797
|
resolveRef(ref) {
|
|
764
798
|
const entry = this.located.get(ref.id) ?? this.generation.get(ref.id);
|
|
765
799
|
if (entry === undefined) {
|
|
@@ -1190,20 +1224,20 @@ export class AgentDeviceSurface {
|
|
|
1190
1224
|
return relative;
|
|
1191
1225
|
}
|
|
1192
1226
|
/** Raw device pixels; cleanup follows the capture even when its caller abandons it. */
|
|
1193
|
-
rawScreenshot(signal) {
|
|
1194
|
-
return this.captureScreenshot(signal, async (shot, file) => new Uint8Array(readFileSync(shot.path ?? file)));
|
|
1227
|
+
rawScreenshot(signal, capture = {}) {
|
|
1228
|
+
return this.captureScreenshot(signal, async (shot, file) => new Uint8Array(readFileSync(shot.path ?? file)), capture);
|
|
1195
1229
|
}
|
|
1196
1230
|
/**
|
|
1197
1231
|
* One screenshot into a temp directory that is removed once `read` has
|
|
1198
1232
|
* taken what it needs from the response or the file, even when the caller
|
|
1199
1233
|
* abandons the capture.
|
|
1200
1234
|
*/
|
|
1201
|
-
captureScreenshot(signal, read) {
|
|
1235
|
+
captureScreenshot(signal, read, capture = {}) {
|
|
1202
1236
|
return this.command('screenshot', async (client) => {
|
|
1203
1237
|
const directory = mkdtempSync(path.join(tmpdir(), 'e2e-agent-device-'));
|
|
1204
1238
|
const file = path.join(directory, 'screenshot.png');
|
|
1205
1239
|
try {
|
|
1206
|
-
return await read(await client.capture.screenshot({ path: file }), file);
|
|
1240
|
+
return await read(await client.capture.screenshot({ path: file, ...(capture.normalizeStatusBar === true ? { normalizeStatusBar: true } : {}) }), file);
|
|
1207
1241
|
}
|
|
1208
1242
|
finally {
|
|
1209
1243
|
rmSync(directory, { recursive: true, force: true });
|
|
@@ -1238,10 +1272,10 @@ export class AgentDeviceSurface {
|
|
|
1238
1272
|
* Viewport pixels for an observation. Best-effort: a screenshot that cannot
|
|
1239
1273
|
* be produced or redacted costs the observation its image, not the step.
|
|
1240
1274
|
*/
|
|
1241
|
-
async capturePixels(operation, projected, viewport) {
|
|
1275
|
+
async capturePixels(operation, projected, viewport, capture) {
|
|
1242
1276
|
let raw;
|
|
1243
1277
|
try {
|
|
1244
|
-
raw = await this.rawScreenshot(operation.signal);
|
|
1278
|
+
raw = await this.rawScreenshot(operation.signal, capture);
|
|
1245
1279
|
}
|
|
1246
1280
|
catch {
|
|
1247
1281
|
return undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@e2e-dev/mobile",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0-nightly-20261009094406",
|
|
4
4
|
"description": "Mobile engine (iOS and Android) for e2e agentic end-to-end testing, built on agent-device",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"e2e",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"ai": "^7.0.0",
|
|
60
|
-
"e2e": "
|
|
60
|
+
"e2e": "0.19.0-nightly-20261009094406"
|
|
61
61
|
},
|
|
62
62
|
"peerDependenciesMeta": {
|
|
63
63
|
"ai": {
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"@types/node": "26.6.2",
|
|
69
69
|
"@types/pngjs": "6.0.5",
|
|
70
70
|
"ai": "7.0.107",
|
|
71
|
-
"e2e": "0.
|
|
71
|
+
"e2e": "0.19.0-nightly-20261009094406",
|
|
72
72
|
"typescript": "7.0.2",
|
|
73
73
|
"vitest": "5.0.1"
|
|
74
74
|
},
|