@vostride/agent-qa-android 0.2.3 → 0.2.4
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/index.cjs +30 -119
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -92
- package/dist/index.d.ts +7 -92
- package/dist/index.js +30 -119
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -45,7 +45,7 @@ var import_agent_qa_core2 = require("@vostride/agent-qa-core");
|
|
|
45
45
|
// src/session.ts
|
|
46
46
|
var import_webdriverio = require("webdriverio");
|
|
47
47
|
var import_agent_qa_core = require("@vostride/agent-qa-core");
|
|
48
|
-
async function createAndroidSession(config
|
|
48
|
+
async function createAndroidSession(config) {
|
|
49
49
|
const match = config.device?.match ?? {};
|
|
50
50
|
const appiumUrl = process.env.AGENT_QA_APPIUM_URL ?? config.appiumUrl ?? "http://localhost:4723";
|
|
51
51
|
const url = new URL(appiumUrl);
|
|
@@ -88,17 +88,6 @@ async function createAndroidSession(config, farmSession) {
|
|
|
88
88
|
delete capabilities[key];
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
if (farmSession) {
|
|
92
|
-
Object.assign(capabilities, farmSession.capabilities);
|
|
93
|
-
return (0, import_webdriverio.remote)({
|
|
94
|
-
protocol: "https",
|
|
95
|
-
hostname: farmSession.hostname,
|
|
96
|
-
port: farmSession.port,
|
|
97
|
-
path: farmSession.path,
|
|
98
|
-
logLevel: config.verbose ? "info" : "silent",
|
|
99
|
-
capabilities
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
91
|
return (0, import_webdriverio.remote)({
|
|
103
92
|
hostname: url.hostname,
|
|
104
93
|
port: parseInt(url.port || "4723", 10),
|
|
@@ -278,8 +267,7 @@ return (function() {
|
|
|
278
267
|
return node ? (node.textContent || '').trim() : '';
|
|
279
268
|
}
|
|
280
269
|
|
|
281
|
-
//
|
|
282
|
-
// becomes a name. A wrapping label's text drops the field's own text (a select's options).
|
|
270
|
+
// A field is named by its label, not its value; a wrapping label drops the field's own text.
|
|
283
271
|
function labelText(el, label) {
|
|
284
272
|
var text = label.textContent || '';
|
|
285
273
|
var own = el.textContent || '';
|
|
@@ -303,8 +291,7 @@ return (function() {
|
|
|
303
291
|
return '';
|
|
304
292
|
}
|
|
305
293
|
|
|
306
|
-
//
|
|
307
|
-
// away, matching the mobile/web walkers' Pitfall #7 handling).
|
|
294
|
+
// The nearest included ancestor: structural and skipped nodes collapse, as on mobile and web.
|
|
308
295
|
function nearestAncestorIndex(el) {
|
|
309
296
|
var p = el.parentElement;
|
|
310
297
|
while (p) {
|
|
@@ -324,8 +311,7 @@ return (function() {
|
|
|
324
311
|
if (style.display === 'none' || style.visibility === 'hidden') continue;
|
|
325
312
|
|
|
326
313
|
var tag = el.tagName;
|
|
327
|
-
//
|
|
328
|
-
// its descendants are ordinary nodes.
|
|
314
|
+
// Only the outermost content-editable element is an editor; its descendants are ordinary nodes.
|
|
329
315
|
var host = el.isContentEditable === true
|
|
330
316
|
&& !(el.parentElement && el.parentElement.isContentEditable === true);
|
|
331
317
|
var ariaRole = el.getAttribute('role');
|
|
@@ -363,7 +349,7 @@ return (function() {
|
|
|
363
349
|
height: Math.round(rect.height)
|
|
364
350
|
};
|
|
365
351
|
|
|
366
|
-
// Real DOM state signals only
|
|
352
|
+
// Real DOM state signals only; the builder derives every default.
|
|
367
353
|
var state = {};
|
|
368
354
|
var disabled = el.disabled === true
|
|
369
355
|
|| el.hasAttribute('disabled')
|
|
@@ -389,7 +375,7 @@ return (function() {
|
|
|
389
375
|
|
|
390
376
|
var parentIndex = nearestAncestorIndex(el);
|
|
391
377
|
|
|
392
|
-
//
|
|
378
|
+
// What a field or an editor holds is its value, never its name; a password has no value here.
|
|
393
379
|
var observedValue = undefined;
|
|
394
380
|
var textEntry = tag === 'TEXTAREA' || (tag === 'INPUT' && !VALUELESS_TYPES[String(el.type).toLowerCase()]);
|
|
395
381
|
if (textEntry && el.value) {
|
|
@@ -548,14 +534,13 @@ var AndroidPlatformAdapter = class {
|
|
|
548
534
|
captureConsole = true;
|
|
549
535
|
appPackage = null;
|
|
550
536
|
appPid = null;
|
|
551
|
-
//
|
|
552
|
-
// enumerated (or enumeration failed and may be retried). Reset in cleanup().
|
|
537
|
+
// Enumerated once per session; `null` is not yet enumerated or retryable; reset in `cleanup()`.
|
|
553
538
|
appRegistry = null;
|
|
554
539
|
async setup(config) {
|
|
555
540
|
this.config = config;
|
|
556
541
|
this.captureConsole = config.logCapture?.console !== false;
|
|
557
542
|
this.appPackage = this.config.appPackage ?? config.device?.match?.appPackage ?? null;
|
|
558
|
-
this.driver = await createAndroidSession(this.config
|
|
543
|
+
this.driver = await createAndroidSession(this.config);
|
|
559
544
|
const appPackage = this.appPackage;
|
|
560
545
|
if (appPackage && !this.config.browserName && !this.config.device?.match?.browserName) {
|
|
561
546
|
let state;
|
|
@@ -624,7 +609,7 @@ var AndroidPlatformAdapter = class {
|
|
|
624
609
|
// Once true, we know a WebView/browser exists and should retry rather than
|
|
625
610
|
// falling back to native (which returns an opaque WebView and can crash UiAutomator2).
|
|
626
611
|
hasSeenWebContext = false;
|
|
627
|
-
async observe(
|
|
612
|
+
async observe() {
|
|
628
613
|
if (!this.driver) {
|
|
629
614
|
throw new Error("AndroidPlatformAdapter not initialized \u2014 call setup() first");
|
|
630
615
|
}
|
|
@@ -699,7 +684,7 @@ var AndroidPlatformAdapter = class {
|
|
|
699
684
|
const dims = await getImageDimensions(buf);
|
|
700
685
|
const { width } = await this.driver.getWindowSize();
|
|
701
686
|
if (dims && Math.abs(dims.width - width) > 1) {
|
|
702
|
-
console.warn(`[android-adapter] screenshot width ${dims.width} != window width ${width} (coord-space divergence, handled by scaleFactor
|
|
687
|
+
console.warn(`[android-adapter] screenshot width ${dims.width} != window width ${width} (coord-space divergence, handled by scaleFactor)`);
|
|
703
688
|
}
|
|
704
689
|
} catch {
|
|
705
690
|
}
|
|
@@ -731,13 +716,7 @@ var AndroidPlatformAdapter = class {
|
|
|
731
716
|
if (!level) return newLogs;
|
|
732
717
|
return newLogs.filter((e) => e.level === level);
|
|
733
718
|
}
|
|
734
|
-
|
|
735
|
-
* 428 D-11: the labelled 1s snapshot preview, over `captureScreenshot()` — `takeScreenshot()`
|
|
736
|
-
* and nothing else. Never `screenshot()`: that one switches the Appium context, and a tick
|
|
737
|
-
* landing between a step's context switch and its command would run the command in the wrong
|
|
738
|
-
* context. Declared before the driver exists (a capture then answers `undefined`), so the
|
|
739
|
-
* composition's selection never falls through to `screenshot()`.
|
|
740
|
-
*/
|
|
719
|
+
// `takeScreenshot()` only: `screenshot()` switches the Appium context mid-step.
|
|
741
720
|
get liveStream() {
|
|
742
721
|
this.liveStreamCapability ??= (0, import_agent_qa_runner.createSnapshotLiveStream)({ capture: () => this.captureScreenshot() });
|
|
743
722
|
return this.liveStreamCapability;
|
|
@@ -913,9 +892,6 @@ var AndroidPlatformAdapter = class {
|
|
|
913
892
|
return void 0;
|
|
914
893
|
}
|
|
915
894
|
}
|
|
916
|
-
// ---------------------------------------------------------------------------
|
|
917
|
-
// Compound behaviors (EXEC-01/02/03)
|
|
918
|
-
// ---------------------------------------------------------------------------
|
|
919
895
|
/** Read the live text of a focused element handle; undefined when nothing is readable. */
|
|
920
896
|
async readElementText(el) {
|
|
921
897
|
try {
|
|
@@ -932,15 +908,12 @@ var AndroidPlatformAdapter = class {
|
|
|
932
908
|
return false;
|
|
933
909
|
}
|
|
934
910
|
}
|
|
935
|
-
/** An empty field's getText() returns its hint
|
|
911
|
+
/** An empty field's getText() returns its hint, so a showing hint reads ''. */
|
|
936
912
|
async readFocusedValue(el) {
|
|
937
913
|
if (await this.focusedMatches(FOCUSED_HINT_XPATH)) return "";
|
|
938
914
|
return await this.readElementText(el);
|
|
939
915
|
}
|
|
940
|
-
|
|
941
|
-
* The `mobile: shell` route of keyevent and openQuickSettings (437 D-18). A refusal by secure
|
|
942
|
-
* Appium is recorded as unavailable and any other driver error as failed; neither is a pass.
|
|
943
|
-
*/
|
|
916
|
+
// A secure-Appium refusal is recorded as unavailable and any other driver error as failed.
|
|
944
917
|
async runShell(verb, args) {
|
|
945
918
|
try {
|
|
946
919
|
await this.driver.execute("mobile: shell", args);
|
|
@@ -963,7 +936,7 @@ var AndroidPlatformAdapter = class {
|
|
|
963
936
|
return { success: false, error: `${verb} failed: ${detail}`, metadata: { shellAction } };
|
|
964
937
|
}
|
|
965
938
|
}
|
|
966
|
-
/** The one submit mechanism on every Android path
|
|
939
|
+
/** The one submit mechanism on every Android path; pressed once, never retried. */
|
|
967
940
|
async pressEnter() {
|
|
968
941
|
try {
|
|
969
942
|
await this.driver.execute("mobile: pressKey", { keycode: ENTER_KEYCODE });
|
|
@@ -972,7 +945,7 @@ var AndroidPlatformAdapter = class {
|
|
|
972
945
|
return { sent: false, outcome: "failed", detail: firstErrorLine(err) };
|
|
973
946
|
}
|
|
974
947
|
}
|
|
975
|
-
|
|
948
|
+
// Line breaks into a single-line field are refused before typing.
|
|
976
949
|
refuseLineBreaks(action, box) {
|
|
977
950
|
const fillRefusal = {
|
|
978
951
|
verb: "fill",
|
|
@@ -986,14 +959,7 @@ var AndroidPlatformAdapter = class {
|
|
|
986
959
|
metadata: { fillRefusal, ...action.submit ? { fillSubmit: { ...INPUT_FAILED } } : {} }
|
|
987
960
|
};
|
|
988
961
|
}
|
|
989
|
-
|
|
990
|
-
* Native fill from the focused handle (437 D-08..D-10, D-12, D-13, D-15, D-17). Delivers once
|
|
991
|
-
* (addValue for append, setValue for replace), reads back from the same handle and records the
|
|
992
|
-
* shared verdict. An unknown append prior or an unreadable read-back is unverified, which never
|
|
993
|
-
* stops a requested Enter; a mismatch or a multi-line field holds it. A driver error during
|
|
994
|
-
* delivery ends the fill: nothing is typed again and no Enter follows. A secure field yields
|
|
995
|
-
* length sentinels only.
|
|
996
|
-
*/
|
|
962
|
+
// Delivers once and reads back; an unverified result still presses Enter, a mismatch holds it.
|
|
997
963
|
async fillNativeCompound(el, action, box) {
|
|
998
964
|
const isAppend = action.clearMode === "append";
|
|
999
965
|
if (LINE_BREAK_RE.test(action.text) && await this.focusedMatches(FOCUSED_SINGLE_LINE_XPATH)) {
|
|
@@ -1029,11 +995,7 @@ var AndroidPlatformAdapter = class {
|
|
|
1029
995
|
return void 0;
|
|
1030
996
|
}
|
|
1031
997
|
}
|
|
1032
|
-
|
|
1033
|
-
* 437 D-11 (WR-02): where the focused node is relative to the ref, judged by its read bounds.
|
|
1034
|
-
* An unreadable or unparseable value is no evidence, so the native path stays (D-04, D-15).
|
|
1035
|
-
* 444.1 D-07: 'elsewhere' needs positive evidence, the bounds of another observed field.
|
|
1036
|
-
*/
|
|
998
|
+
// `elsewhere` needs positive evidence: the read bounds of another observed field.
|
|
1037
999
|
async focusIsElsewhere(handle, ref, bounds) {
|
|
1038
1000
|
let raw;
|
|
1039
1001
|
try {
|
|
@@ -1050,18 +1012,12 @@ var AndroidPlatformAdapter = class {
|
|
|
1050
1012
|
}
|
|
1051
1013
|
return "moved";
|
|
1052
1014
|
}
|
|
1053
|
-
/** Ctrl+A then DEL as key input; clears only the view holding input focus
|
|
1015
|
+
/** Ctrl+A then DEL as key input; clears only the view holding input focus. */
|
|
1054
1016
|
async clearByKeys() {
|
|
1055
1017
|
await this.driver.execute("mobile: pressKey", CTRL_A);
|
|
1056
1018
|
await this.driver.execute("mobile: pressKey", { keycode: DEL_KEYCODE });
|
|
1057
1019
|
}
|
|
1058
|
-
|
|
1059
|
-
* The last-resort keyboard path (437 D-14): the tap left no focused handle. Replace clears by
|
|
1060
|
-
* keys, the text goes through one `mobile: type` (Appium's Unicode IME, exact for every D-08
|
|
1061
|
-
* text), and the value is read back only when a focused handle can be found afterwards. An
|
|
1062
|
-
* append's prior value is unknown, so it is never better than unverified. The field's kind comes
|
|
1063
|
-
* from the observed input-type (D-15): only positive evidence refuses line breaks or holds Enter.
|
|
1064
|
-
*/
|
|
1020
|
+
// Last resort when the tap left no handle; an append's prior is unknown, so at best unverified.
|
|
1065
1021
|
async fillKeyboardPath(action, box, refData) {
|
|
1066
1022
|
const isAppend = action.clearMode === "append";
|
|
1067
1023
|
const kind = fieldKindOf(refData);
|
|
@@ -1089,11 +1045,7 @@ var AndroidPlatformAdapter = class {
|
|
|
1089
1045
|
enter: () => this.pressEnter()
|
|
1090
1046
|
});
|
|
1091
1047
|
}
|
|
1092
|
-
|
|
1093
|
-
* Native and keyboard clearText (437 D-16): clearValue on the focused handle, or Ctrl+A then DEL
|
|
1094
|
-
* when the tap left none, then the hint-aware read-back judged against the empty string. The
|
|
1095
|
-
* caller has already checked that a focused handle is the referenced field (WR-02).
|
|
1096
|
-
*/
|
|
1048
|
+
// The caller has already checked that a focused handle is the referenced field.
|
|
1097
1049
|
async clearNativeOrKeyboard(box, handle) {
|
|
1098
1050
|
const path = handle ? "native" : "keyboard";
|
|
1099
1051
|
const before = handle ? await this.isSecure(handle) : false;
|
|
@@ -1153,11 +1105,7 @@ var AndroidPlatformAdapter = class {
|
|
|
1153
1105
|
metadata: { fillVerdict, ...action.submit ? { fillSubmit: { ...INPUT_FAILED } } : {} }
|
|
1154
1106
|
};
|
|
1155
1107
|
}
|
|
1156
|
-
|
|
1157
|
-
* The shared end of every Android fill (437 D-09, D-10, D-13, D-15, D-17): records the verdict
|
|
1158
|
-
* and, when a submit was requested, holds Enter on a mismatch or a multi-line field and otherwise
|
|
1159
|
-
* presses it once through `enter`.
|
|
1160
|
-
*/
|
|
1108
|
+
// Holds Enter on a mismatch or a multi-line field; otherwise presses it once.
|
|
1161
1109
|
async settleFill(action, box, settle) {
|
|
1162
1110
|
const { path, verdict, multiline, enter } = settle;
|
|
1163
1111
|
const fillVerdict = {
|
|
@@ -1193,7 +1141,7 @@ var AndroidPlatformAdapter = class {
|
|
|
1193
1141
|
}
|
|
1194
1142
|
return { success: true, ...box, ...notice, metadata };
|
|
1195
1143
|
}
|
|
1196
|
-
/** A real W3C touch tap in the WebView context at the ref's CSS-pixel centre
|
|
1144
|
+
/** A real W3C touch tap in the WebView context at the ref's CSS-pixel centre. */
|
|
1197
1145
|
async trustedWebTap(center) {
|
|
1198
1146
|
await safePerform(
|
|
1199
1147
|
this.driver.action("pointer", { parameters: { pointerType: "touch" } }).move({ x: Math.round(center.x), y: Math.round(center.y), origin: "viewport" }).down({ button: 0 }).up({ button: 0 })
|
|
@@ -1246,7 +1194,7 @@ var AndroidPlatformAdapter = class {
|
|
|
1246
1194
|
if (this.activeWebContext) await this.ensureContext(this.activeWebContext);
|
|
1247
1195
|
}
|
|
1248
1196
|
}
|
|
1249
|
-
|
|
1197
|
+
// A tap that leaves no editable field, or another field, focused writes nothing.
|
|
1250
1198
|
refuseFocus(verb, ref, focus, submit, box) {
|
|
1251
1199
|
const nothing = verb === "fill" ? "nothing was typed" : "nothing was cleared";
|
|
1252
1200
|
let fillRefusal;
|
|
@@ -1268,10 +1216,7 @@ var AndroidPlatformAdapter = class {
|
|
|
1268
1216
|
metadata: { fillRefusal, ...submit ? { fillSubmit: { ...INPUT_FAILED } } : {} }
|
|
1269
1217
|
};
|
|
1270
1218
|
}
|
|
1271
|
-
|
|
1272
|
-
* WebView clearText (437 D-11, D-16): the same tap and focus probe as the fill, W3C Ctrl+A and
|
|
1273
|
-
* Backspace, then a read-back judged against the empty string. Never success without that check.
|
|
1274
|
-
*/
|
|
1219
|
+
// Never success without the read-back judged against the empty string.
|
|
1275
1220
|
async clearWebViewField(ref, center, box) {
|
|
1276
1221
|
const driver = this.driver;
|
|
1277
1222
|
await this.trustedWebTap(center);
|
|
@@ -1287,11 +1232,7 @@ var AndroidPlatformAdapter = class {
|
|
|
1287
1232
|
}
|
|
1288
1233
|
return this.settleClear("webview", await this.readWebFieldValue(), focus.secure, box);
|
|
1289
1234
|
}
|
|
1290
|
-
|
|
1291
|
-
* WebView fill as trusted input (437 D-11): a W3C tap on the ref, a read-only focus probe,
|
|
1292
|
-
* W3C Ctrl+A and Backspace for replace, `mobile: type` from NATIVE_APP, a read-only read-back,
|
|
1293
|
-
* and one `mobile: pressKey 66` from NATIVE_APP. No script writes the field or fires an event.
|
|
1294
|
-
*/
|
|
1235
|
+
// Trusted input only: no page script writes the field or fires an event.
|
|
1295
1236
|
async fillWebViewCompound(action, box, center) {
|
|
1296
1237
|
const driver = this.driver;
|
|
1297
1238
|
const isAppend = action.clearMode === "append";
|
|
@@ -1328,15 +1269,7 @@ var AndroidPlatformAdapter = class {
|
|
|
1328
1269
|
enter: () => this.withNativeContext(() => this.pressEnter())
|
|
1329
1270
|
});
|
|
1330
1271
|
}
|
|
1331
|
-
|
|
1332
|
-
* EXEC-02 platform primitives injected into the shared runner scroll loops (D-02/D-03; 436-02
|
|
1333
|
-
* D-25). A native swipe is `mobile: scrollGesture` over the container (the ref's bounds, else the
|
|
1334
|
-
* screen), whose boolean is the driver's own boundary signal; in a WebView the touch swipe is
|
|
1335
|
-
* measured over every WebView scroller before and after (436-18), paired by identity (436-23).
|
|
1336
|
-
* nativeSearchScroll drives the UiAutomator
|
|
1337
|
-
* `UiScrollable.scrollIntoView` rung over an injection-escaped selector; readInterimTree is a
|
|
1338
|
-
* light page-source parse — NO refMap mint, NO screenshot, lastRefs untouched.
|
|
1339
|
-
*/
|
|
1272
|
+
// Native swipes trust `scrollGesture`'s boolean; WebView swipes compare every scroller by id.
|
|
1340
1273
|
async buildAndroidScrollPrimitives(scrollDirection, maxSwipes, factor = 1) {
|
|
1341
1274
|
const driver = this.driver;
|
|
1342
1275
|
const viewport = await driver.getWindowSize();
|
|
@@ -1395,13 +1328,7 @@ var AndroidPlatformAdapter = class {
|
|
|
1395
1328
|
}
|
|
1396
1329
|
};
|
|
1397
1330
|
}
|
|
1398
|
-
|
|
1399
|
-
* The WebView scrollers a touch swipe may have acted on (436-18 · D-25), read by the shared
|
|
1400
|
-
* WEBVIEW_SCROLLERS_SCRIPT. The touch point cannot be mapped into the WebView's CSS coordinates,
|
|
1401
|
-
* so the swipe is judged over all of them.
|
|
1402
|
-
* 2026-10-08 (436-23): a frame in view reads unknown; members carry an identity and are paired
|
|
1403
|
-
* by it; open shadow roots are walked; only on-screen element scrollers are members.
|
|
1404
|
-
*/
|
|
1331
|
+
// The touch point cannot map into CSS coordinates, so the swipe is judged over every scroller.
|
|
1405
1332
|
async readWebViewScrollers() {
|
|
1406
1333
|
try {
|
|
1407
1334
|
return (0, import_agent_qa_core2.parseWebViewScrollers)(await this.driver.execute(import_agent_qa_core2.WEBVIEW_SCROLLERS_SCRIPT));
|
|
@@ -1409,12 +1336,7 @@ var AndroidPlatformAdapter = class {
|
|
|
1409
1336
|
return void 0;
|
|
1410
1337
|
}
|
|
1411
1338
|
}
|
|
1412
|
-
|
|
1413
|
-
* EXEC-03 openApp (D-10/D-11/D-12). Resolve a human name to a package id (or validate an
|
|
1414
|
-
* explicit bundleId) over the session-cached registry, then launch via activateApp. On a
|
|
1415
|
-
* resolution MISS the registry is re-enumerated exactly once (catches mid-run installs) before
|
|
1416
|
-
* erroring with the resolver's candidate ids. Never a best-guess launch (T-359-04).
|
|
1417
|
-
*/
|
|
1339
|
+
// On a resolution miss the registry is re-enumerated once before erroring; never a best guess.
|
|
1418
1340
|
async openApp(action) {
|
|
1419
1341
|
const driver = this.driver;
|
|
1420
1342
|
let registry = await this.ensureAppRegistry();
|
|
@@ -1466,12 +1388,7 @@ var AndroidPlatformAdapter = class {
|
|
|
1466
1388
|
if (enumerated !== null) this.appRegistry = enumerated;
|
|
1467
1389
|
return enumerated;
|
|
1468
1390
|
}
|
|
1469
|
-
|
|
1470
|
-
* Enumerate installed apps through the driver only (ownership contract — no adb/child_process).
|
|
1471
|
-
* `mobile: listApps` maps package ids → AppRegistryEntry (labels only when the same call carries
|
|
1472
|
-
* them, D-10 — no per-package round-trips). On failure, fall back to `mobile: shell` pm-list ONLY
|
|
1473
|
-
* when the AGENT_QA_ANDROID_USE_MOBILE_SHELL gate is '1' (Pitfall 4). Null = enumeration unavailable.
|
|
1474
|
-
*/
|
|
1391
|
+
// Driver only, no adb; the `mobile: shell` fallback needs `AGENT_QA_ANDROID_USE_MOBILE_SHELL=1`.
|
|
1475
1392
|
async enumerateApps() {
|
|
1476
1393
|
const driver = this.driver;
|
|
1477
1394
|
try {
|
|
@@ -1907,8 +1824,6 @@ var AndroidPlatformAdapter = class {
|
|
|
1907
1824
|
success: false,
|
|
1908
1825
|
error: 'assertValue {source:"network"} (HTTP log inspection) is not available on the Android adapter. Use a web target for network assertions, or assert on visible UI text.'
|
|
1909
1826
|
};
|
|
1910
|
-
case "expr-result":
|
|
1911
|
-
return { success: true, data: void 0 };
|
|
1912
1827
|
}
|
|
1913
1828
|
}
|
|
1914
1829
|
case "drag": {
|
|
@@ -1962,11 +1877,7 @@ var AndroidPlatformAdapter = class {
|
|
|
1962
1877
|
}
|
|
1963
1878
|
return { success: true, coordinates: { x, y } };
|
|
1964
1879
|
}
|
|
1965
|
-
// Runner-
|
|
1966
|
-
// setVariable are intercepted at the runner dispatch seam and never reach the adapter. A
|
|
1967
|
-
// grouped §7.1 defensive case surfaces a dispatch-routing error if one arrives here;
|
|
1968
|
-
// assertNever remains the backstop for a genuinely unknown schema verb. (uniqueValue is
|
|
1969
|
-
// web-only and is not part of ActionsFor<'android'>.)
|
|
1880
|
+
// Runner-channel verbs never reach the adapter; one arriving here is a routing error.
|
|
1970
1881
|
case "reportValue":
|
|
1971
1882
|
case "assertAI":
|
|
1972
1883
|
case "extractAI":
|