execbro 2.8.2 → 2.8.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/build/__tests__/unit/errorCategorization.test.js +19 -3
- package/build/__tests__/unit/errorCategorization.test.js.map +1 -1
- package/build/__tests__/unit/inputTarget.test.js +62 -1
- package/build/__tests__/unit/inputTarget.test.js.map +1 -1
- package/build/__tests__/unit/metroGateReconnect.test.d.ts +2 -0
- package/build/__tests__/unit/metroGateReconnect.test.d.ts.map +1 -0
- package/build/__tests__/unit/metroGateReconnect.test.js +98 -0
- package/build/__tests__/unit/metroGateReconnect.test.js.map +1 -0
- package/build/__tests__/unit/pressablesLongPress.test.d.ts +2 -0
- package/build/__tests__/unit/pressablesLongPress.test.d.ts.map +1 -0
- package/build/__tests__/unit/pressablesLongPress.test.js +91 -0
- package/build/__tests__/unit/pressablesLongPress.test.js.map +1 -0
- package/build/__tests__/unit/socketLifecycle.test.d.ts +2 -0
- package/build/__tests__/unit/socketLifecycle.test.d.ts.map +1 -0
- package/build/__tests__/unit/socketLifecycle.test.js +66 -0
- package/build/__tests__/unit/socketLifecycle.test.js.map +1 -0
- package/build/__tests__/unit/swipeProbeWithoutRn.test.d.ts +2 -0
- package/build/__tests__/unit/swipeProbeWithoutRn.test.d.ts.map +1 -0
- package/build/__tests__/unit/swipeProbeWithoutRn.test.js +44 -0
- package/build/__tests__/unit/swipeProbeWithoutRn.test.js.map +1 -0
- package/build/__tests__/unit/tapAndroidSerial.test.d.ts +2 -0
- package/build/__tests__/unit/tapAndroidSerial.test.d.ts.map +1 -0
- package/build/__tests__/unit/tapAndroidSerial.test.js +179 -0
- package/build/__tests__/unit/tapAndroidSerial.test.js.map +1 -0
- package/build/__tests__/unit/tapDuration.test.d.ts +2 -0
- package/build/__tests__/unit/tapDuration.test.d.ts.map +1 -0
- package/build/__tests__/unit/tapDuration.test.js +168 -0
- package/build/__tests__/unit/tapDuration.test.js.map +1 -0
- package/build/__tests__/unit/tapLongPressReport.test.d.ts +2 -0
- package/build/__tests__/unit/tapLongPressReport.test.d.ts.map +1 -0
- package/build/__tests__/unit/tapLongPressReport.test.js +167 -0
- package/build/__tests__/unit/tapLongPressReport.test.js.map +1 -0
- package/build/__tests__/unit/textEntry.test.js +32 -0
- package/build/__tests__/unit/textEntry.test.js.map +1 -1
- package/build/core/android.d.ts +1 -1
- package/build/core/android.d.ts.map +1 -1
- package/build/core/android.js +4 -1
- package/build/core/android.js.map +1 -1
- package/build/core/connection.d.ts.map +1 -1
- package/build/core/connection.js +14 -5
- package/build/core/connection.js.map +1 -1
- package/build/core/connectionState.d.ts +7 -0
- package/build/core/connectionState.d.ts.map +1 -1
- package/build/core/connectionState.js +11 -0
- package/build/core/connectionState.js.map +1 -1
- package/build/core/index.d.ts +1 -1
- package/build/core/index.d.ts.map +1 -1
- package/build/core/index.js +1 -1
- package/build/core/index.js.map +1 -1
- package/build/core/inputTarget.d.ts.map +1 -1
- package/build/core/inputTarget.js +31 -8
- package/build/core/inputTarget.js.map +1 -1
- package/build/core/nativeOnlyHints.d.ts +7 -1
- package/build/core/nativeOnlyHints.d.ts.map +1 -1
- package/build/core/nativeOnlyHints.js +32 -4
- package/build/core/nativeOnlyHints.js.map +1 -1
- package/build/core/pressables.d.ts +1 -0
- package/build/core/pressables.d.ts.map +1 -1
- package/build/core/pressables.js +32 -8
- package/build/core/pressables.js.map +1 -1
- package/build/core/register.d.ts.map +1 -1
- package/build/core/register.js +17 -2
- package/build/core/register.js.map +1 -1
- package/build/core/state.d.ts +1 -0
- package/build/core/state.d.ts.map +1 -1
- package/build/core/state.js +4 -0
- package/build/core/state.js.map +1 -1
- package/build/core/swipeDiagnosis.d.ts +1 -0
- package/build/core/swipeDiagnosis.d.ts.map +1 -1
- package/build/core/swipeDiagnosis.js +18 -5
- package/build/core/swipeDiagnosis.js.map +1 -1
- package/build/core/telemetry.d.ts +1 -1
- package/build/core/telemetry.d.ts.map +1 -1
- package/build/core/telemetry.js +5 -2
- package/build/core/telemetry.js.map +1 -1
- package/build/core/textEntry.d.ts.map +1 -1
- package/build/core/textEntry.js +7 -1
- package/build/core/textEntry.js.map +1 -1
- package/build/pro/tap.d.ts +12 -0
- package/build/pro/tap.d.ts.map +1 -1
- package/build/pro/tap.js +102 -34
- package/build/pro/tap.js.map +1 -1
- package/build/pro/verifyAction.d.ts +3 -1
- package/build/pro/verifyAction.d.ts.map +1 -1
- package/build/pro/verifyAction.js +7 -7
- package/build/pro/verifyAction.js.map +1 -1
- package/build/tools/componentTools.js +4 -4
- package/build/tools/componentTools.js.map +1 -1
- package/build/tools/interactionTools.d.ts.map +1 -1
- package/build/tools/interactionTools.js +20 -5
- package/build/tools/interactionTools.js.map +1 -1
- package/package.json +1 -1
- package/tools.json +1 -0
package/build/pro/tap.js
CHANGED
|
@@ -87,6 +87,24 @@ export async function analyzeBurstFrames(frames, options) {
|
|
|
87
87
|
kind
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
|
+
export function buildLongPressReport(args) {
|
|
91
|
+
if (args.durationMs === undefined)
|
|
92
|
+
return undefined;
|
|
93
|
+
if (args.hasLongPress === undefined) {
|
|
94
|
+
return { durationMs: args.durationMs, handlerFound: null };
|
|
95
|
+
}
|
|
96
|
+
if (args.hasLongPress) {
|
|
97
|
+
return { durationMs: args.durationMs, handlerFound: true };
|
|
98
|
+
}
|
|
99
|
+
const what = args.element ? `<${args.element} />` : "the element";
|
|
100
|
+
return {
|
|
101
|
+
durationMs: args.durationMs,
|
|
102
|
+
handlerFound: false,
|
|
103
|
+
warning: `Held for ${args.durationMs}ms, but ${what} has no onLongPress handler — ` +
|
|
104
|
+
`React Native will have fired its onPress on release instead. The gesture was ` +
|
|
105
|
+
`delivered; if you expected a long-press action, it is not wired to this element.`
|
|
106
|
+
};
|
|
107
|
+
}
|
|
90
108
|
export function buildQuery(options) {
|
|
91
109
|
const query = {};
|
|
92
110
|
if (options.text !== undefined)
|
|
@@ -416,7 +434,7 @@ export function findClosestOcrText(ocrResult, query) {
|
|
|
416
434
|
}
|
|
417
435
|
return best;
|
|
418
436
|
}
|
|
419
|
-
async function tryFiberStrategy(query, index, maxTraversalDepth, sink, device) {
|
|
437
|
+
async function tryFiberStrategy(query, index, maxTraversalDepth, sink, device, longPress = false) {
|
|
420
438
|
if (sink) {
|
|
421
439
|
sink.fiber.ran = true;
|
|
422
440
|
sink.fiber.metroConnected = connectedApps.size > 0;
|
|
@@ -433,7 +451,7 @@ async function tryFiberStrategy(query, index, maxTraversalDepth, sink, device) {
|
|
|
433
451
|
}
|
|
434
452
|
let lastResult = null;
|
|
435
453
|
for (const depth of depthAttempts) {
|
|
436
|
-
const result = await tryFiberAtDepth(query, index, depth, device);
|
|
454
|
+
const result = await tryFiberAtDepth(query, index, depth, device, longPress);
|
|
437
455
|
if (sink && result.matches?.length) {
|
|
438
456
|
sink.fiber.pressables = result.matches.slice(0, 50).map(m => ({
|
|
439
457
|
label: m.text || undefined,
|
|
@@ -456,7 +474,7 @@ async function tryFiberStrategy(query, index, maxTraversalDepth, sink, device) {
|
|
|
456
474
|
sink.fiber.durationMs = Date.now() - startedAt;
|
|
457
475
|
}
|
|
458
476
|
}
|
|
459
|
-
async function tryFiberAtDepth(query, index, maxTraversalDepth, device) {
|
|
477
|
+
async function tryFiberAtDepth(query, index, maxTraversalDepth, device, longPress = false) {
|
|
460
478
|
try {
|
|
461
479
|
const result = await pressElement({
|
|
462
480
|
text: query.text,
|
|
@@ -464,7 +482,8 @@ async function tryFiberAtDepth(query, index, maxTraversalDepth, device) {
|
|
|
464
482
|
component: query.component,
|
|
465
483
|
index,
|
|
466
484
|
maxTraversalDepth,
|
|
467
|
-
device
|
|
485
|
+
device,
|
|
486
|
+
longPress
|
|
468
487
|
});
|
|
469
488
|
if (!result.success) {
|
|
470
489
|
return { success: false, reason: result.error || "pressElement failed" };
|
|
@@ -508,7 +527,8 @@ async function tryFiberAtDepth(query, index, maxTraversalDepth, device) {
|
|
|
508
527
|
x: parsed.nativeTapTarget.x,
|
|
509
528
|
y: parsed.nativeTapTarget.y,
|
|
510
529
|
unit: parsed.nativeTapTarget.unit || "points"
|
|
511
|
-
}
|
|
530
|
+
},
|
|
531
|
+
hasLongPress: !!parsed.hasLongPress
|
|
512
532
|
};
|
|
513
533
|
}
|
|
514
534
|
return {
|
|
@@ -528,7 +548,7 @@ async function tryFiberAtDepth(query, index, maxTraversalDepth, device) {
|
|
|
528
548
|
};
|
|
529
549
|
}
|
|
530
550
|
}
|
|
531
|
-
async function tryAccessibilityStrategy(query, index, platform, udid, sink, signal) {
|
|
551
|
+
async function tryAccessibilityStrategy(query, index, platform, udid, sink, signal, deviceId, duration) {
|
|
532
552
|
if (sink)
|
|
533
553
|
sink.accessibility.ran = true;
|
|
534
554
|
const startedAt = Date.now();
|
|
@@ -616,7 +636,7 @@ async function tryAccessibilityStrategy(query, index, platform, udid, sink, sign
|
|
|
616
636
|
reason: `Index ${index} out of bounds (${iosEffectiveMatches.length} matches)`
|
|
617
637
|
};
|
|
618
638
|
}
|
|
619
|
-
await iosTap(match.center.x, match.center.y, { udid });
|
|
639
|
+
await iosTap(match.center.x, match.center.y, { udid, duration });
|
|
620
640
|
return {
|
|
621
641
|
success: true,
|
|
622
642
|
reason: "Tapped via iOS accessibility",
|
|
@@ -635,13 +655,13 @@ async function tryAccessibilityStrategy(query, index, platform, udid, sink, sign
|
|
|
635
655
|
searchOptions.textContains = query.text;
|
|
636
656
|
}
|
|
637
657
|
const { androidGetUITree } = await import("../core/android.js");
|
|
638
|
-
const androidTree = await androidGetUITree(
|
|
639
|
-
let result = await androidFindElement(searchOptions,
|
|
658
|
+
const androidTree = await androidGetUITree(deviceId, signal);
|
|
659
|
+
let result = await androidFindElement(searchOptions, deviceId, signal, androidTree);
|
|
640
660
|
if (hasTestID && !hasText && (!result.success || !result.allMatches || result.allMatches.length === 0)) {
|
|
641
661
|
result = await androidFindElement({
|
|
642
662
|
contentDescContains: query.testID,
|
|
643
663
|
index
|
|
644
|
-
},
|
|
664
|
+
}, deviceId, signal, androidTree);
|
|
645
665
|
}
|
|
646
666
|
if (sink && result.allMatches?.length) {
|
|
647
667
|
sink.accessibility.elements = result.allMatches.slice(0, 50).map(el => ({
|
|
@@ -683,7 +703,13 @@ async function tryAccessibilityStrategy(query, index, platform, udid, sink, sign
|
|
|
683
703
|
reason: `Index ${index} out of bounds (${androidEffectiveMatches.length} matches)`
|
|
684
704
|
};
|
|
685
705
|
}
|
|
686
|
-
await androidTap(match.center.x, match.center.y);
|
|
706
|
+
const a11yTap = await androidTap(match.center.x, match.center.y, deviceId, duration);
|
|
707
|
+
if (!a11yTap.success) {
|
|
708
|
+
return {
|
|
709
|
+
success: false,
|
|
710
|
+
reason: `Accessibility found "${match.text ?? match.contentDesc ?? match.resourceId}" but tap failed: ${a11yTap.error}`
|
|
711
|
+
};
|
|
712
|
+
}
|
|
687
713
|
return {
|
|
688
714
|
success: true,
|
|
689
715
|
reason: "Tapped via Android accessibility",
|
|
@@ -705,7 +731,7 @@ async function tryAccessibilityStrategy(query, index, platform, udid, sink, sign
|
|
|
705
731
|
sink.accessibility.durationMs = Date.now() - startedAt;
|
|
706
732
|
}
|
|
707
733
|
}
|
|
708
|
-
async function tryOcrStrategy(query, platform, udid, sink, signal) {
|
|
734
|
+
async function tryOcrStrategy(query, platform, udid, sink, signal, deviceId, duration) {
|
|
709
735
|
if (sink)
|
|
710
736
|
sink.ocr.ran = true;
|
|
711
737
|
const ocrStartedAt = Date.now();
|
|
@@ -729,7 +755,7 @@ async function tryOcrStrategy(query, platform, udid, sink, signal) {
|
|
|
729
755
|
}
|
|
730
756
|
else {
|
|
731
757
|
const { androidScreenshot } = await import("../core/android.js");
|
|
732
|
-
const screenshot = await androidScreenshot(undefined,
|
|
758
|
+
const screenshot = await androidScreenshot(undefined, deviceId, signal);
|
|
733
759
|
if (!screenshot.success || !screenshot.data) {
|
|
734
760
|
return {
|
|
735
761
|
success: false,
|
|
@@ -776,7 +802,7 @@ async function tryOcrStrategy(query, platform, udid, sink, signal) {
|
|
|
776
802
|
if (platform === "ios") {
|
|
777
803
|
const { getDevicePixelRatio } = await import("../core/ios.js");
|
|
778
804
|
const dpr = await getDevicePixelRatio(udid);
|
|
779
|
-
const tapResult = await iosTap(Math.round((match.tapCenter.x * scaleFactor) / dpr), Math.round((match.tapCenter.y * scaleFactor) / dpr), { udid });
|
|
805
|
+
const tapResult = await iosTap(Math.round((match.tapCenter.x * scaleFactor) / dpr), Math.round((match.tapCenter.y * scaleFactor) / dpr), { udid, duration });
|
|
780
806
|
if (!tapResult.success) {
|
|
781
807
|
return {
|
|
782
808
|
success: false,
|
|
@@ -785,7 +811,13 @@ async function tryOcrStrategy(query, platform, udid, sink, signal) {
|
|
|
785
811
|
}
|
|
786
812
|
}
|
|
787
813
|
else {
|
|
788
|
-
await androidTap(Math.round(match.tapCenter.x * scaleFactor), Math.round(match.tapCenter.y * scaleFactor));
|
|
814
|
+
const ocrTap = await androidTap(Math.round(match.tapCenter.x * scaleFactor), Math.round(match.tapCenter.y * scaleFactor), deviceId, duration);
|
|
815
|
+
if (!ocrTap.success) {
|
|
816
|
+
return {
|
|
817
|
+
success: false,
|
|
818
|
+
reason: `OCR found "${match.text}" but tap failed: ${ocrTap.error}`
|
|
819
|
+
};
|
|
820
|
+
}
|
|
789
821
|
}
|
|
790
822
|
return {
|
|
791
823
|
success: true,
|
|
@@ -809,7 +841,7 @@ async function tryOcrStrategy(query, platform, udid, sink, signal) {
|
|
|
809
841
|
sink.ocr.durationMs = Date.now() - ocrStartedAt;
|
|
810
842
|
}
|
|
811
843
|
}
|
|
812
|
-
async function tryCoordinateStrategy(pixelX, pixelY, platform, lastScreenshot, udid) {
|
|
844
|
+
async function tryCoordinateStrategy(pixelX, pixelY, platform, lastScreenshot, udid, deviceId, duration) {
|
|
813
845
|
try {
|
|
814
846
|
if (platform === "ios") {
|
|
815
847
|
const scaleFactor = lastScreenshot?.scaleFactor ?? 1;
|
|
@@ -819,7 +851,7 @@ async function tryCoordinateStrategy(pixelX, pixelY, platform, lastScreenshot, u
|
|
|
819
851
|
: undefined;
|
|
820
852
|
const devicePixelRatio = await getDevicePixelRatio(udid, dprHint);
|
|
821
853
|
const converted = convertScreenshotToTapCoords(pixelX, pixelY, "ios", devicePixelRatio, scaleFactor);
|
|
822
|
-
const tapResult = await iosTap(converted.x, converted.y, { udid });
|
|
854
|
+
const tapResult = await iosTap(converted.x, converted.y, { udid, duration });
|
|
823
855
|
if (!tapResult.success) {
|
|
824
856
|
return {
|
|
825
857
|
success: false,
|
|
@@ -835,7 +867,13 @@ async function tryCoordinateStrategy(pixelX, pixelY, platform, lastScreenshot, u
|
|
|
835
867
|
else {
|
|
836
868
|
const scaleFactor = lastScreenshot?.scaleFactor ?? 1;
|
|
837
869
|
const converted = convertScreenshotToTapCoords(pixelX, pixelY, "android", 1, scaleFactor);
|
|
838
|
-
await androidTap(converted.x, converted.y);
|
|
870
|
+
const coordTap = await androidTap(converted.x, converted.y, deviceId, duration);
|
|
871
|
+
if (!coordTap.success) {
|
|
872
|
+
return {
|
|
873
|
+
success: false,
|
|
874
|
+
reason: `Coordinate tap failed: ${coordTap.error}`
|
|
875
|
+
};
|
|
876
|
+
}
|
|
839
877
|
return {
|
|
840
878
|
success: true,
|
|
841
879
|
reason: "Tapped at coordinates (Android)",
|
|
@@ -1145,12 +1183,13 @@ export async function tap(options) {
|
|
|
1145
1183
|
}
|
|
1146
1184
|
const platform = nativeResolved.target.platform;
|
|
1147
1185
|
const nativeUdid = nativeResolved.target.iosUdid;
|
|
1186
|
+
const nativeSerial = nativeResolved.target.androidSerial;
|
|
1148
1187
|
const nativeShouldScreenshot = options.screenshot !== false;
|
|
1149
1188
|
const nativeShouldVerify = options.verify !== false;
|
|
1150
1189
|
let nativeBeforeBuffer = null;
|
|
1151
1190
|
let nativeScreenshotMeta;
|
|
1152
1191
|
if (nativeShouldVerify) {
|
|
1153
|
-
const before = await captureScreenshot(platform, nativeUdid);
|
|
1192
|
+
const before = await captureScreenshot(platform, nativeUdid, nativeSerial);
|
|
1154
1193
|
nativeBeforeBuffer = before?.buffer || null;
|
|
1155
1194
|
if (before) {
|
|
1156
1195
|
nativeScreenshotMeta = {
|
|
@@ -1161,7 +1200,7 @@ export async function tap(options) {
|
|
|
1161
1200
|
}
|
|
1162
1201
|
}
|
|
1163
1202
|
if (!nativeScreenshotMeta) {
|
|
1164
|
-
const ref = await captureScreenshot(platform, nativeUdid);
|
|
1203
|
+
const ref = await captureScreenshot(platform, nativeUdid, nativeSerial);
|
|
1165
1204
|
if (ref) {
|
|
1166
1205
|
nativeScreenshotMeta = {
|
|
1167
1206
|
originalWidth: ref.width,
|
|
@@ -1175,7 +1214,7 @@ export async function tap(options) {
|
|
|
1175
1214
|
}
|
|
1176
1215
|
let result;
|
|
1177
1216
|
try {
|
|
1178
|
-
result = await withTimeout(tryCoordinateStrategy(query.x, query.y, platform, nativeScreenshotMeta, nativeUdid), remainingMs(), "native-coordinate");
|
|
1217
|
+
result = await withTimeout(tryCoordinateStrategy(query.x, query.y, platform, nativeScreenshotMeta, nativeUdid, nativeSerial, options.duration), remainingMs(), "native-coordinate");
|
|
1179
1218
|
}
|
|
1180
1219
|
catch (err) {
|
|
1181
1220
|
return formatTapFailure({
|
|
@@ -1203,6 +1242,7 @@ export async function tap(options) {
|
|
|
1203
1242
|
platform,
|
|
1204
1243
|
beforeBuffer: nativeBeforeBuffer,
|
|
1205
1244
|
udid: nativeUdid,
|
|
1245
|
+
deviceId: nativeSerial,
|
|
1206
1246
|
beforeScaleFactor: nativeScreenshotMeta?.scaleFactor,
|
|
1207
1247
|
markerPx: nativeMarker
|
|
1208
1248
|
}));
|
|
@@ -1216,11 +1256,12 @@ export async function tap(options) {
|
|
|
1216
1256
|
shouldScreenshot: nativeShouldScreenshot,
|
|
1217
1257
|
beforeBuffer: nativeBeforeBuffer,
|
|
1218
1258
|
udid: nativeUdid,
|
|
1259
|
+
deviceId: nativeSerial,
|
|
1219
1260
|
beforeScaleFactor: nativeScreenshotMeta?.scaleFactor,
|
|
1220
1261
|
markerPx: nativeMarker
|
|
1221
1262
|
}));
|
|
1222
1263
|
}
|
|
1223
|
-
|
|
1264
|
+
const nativeSuccess = formatTapSuccess({
|
|
1224
1265
|
method: "native-coordinate",
|
|
1225
1266
|
query,
|
|
1226
1267
|
pressed: result.pressed,
|
|
@@ -1229,6 +1270,10 @@ export async function tap(options) {
|
|
|
1229
1270
|
screenshot,
|
|
1230
1271
|
verification
|
|
1231
1272
|
});
|
|
1273
|
+
const nativeLongPress = buildLongPressReport({ durationMs: options.duration });
|
|
1274
|
+
if (nativeLongPress)
|
|
1275
|
+
nativeSuccess.longPress = nativeLongPress;
|
|
1276
|
+
return nativeSuccess;
|
|
1232
1277
|
}
|
|
1233
1278
|
return formatTapFailure({
|
|
1234
1279
|
query,
|
|
@@ -1247,6 +1292,7 @@ export async function tap(options) {
|
|
|
1247
1292
|
const deviceNote = resolved.note;
|
|
1248
1293
|
const platform = resolved.target.platform;
|
|
1249
1294
|
let targetUdid = resolved.target.iosUdid;
|
|
1295
|
+
const targetSerial = resolved.target.androidSerial;
|
|
1250
1296
|
const allApps = Array.from(connectedApps.values());
|
|
1251
1297
|
let hasMetro = allApps.length > 0;
|
|
1252
1298
|
let app;
|
|
@@ -1384,7 +1430,7 @@ export async function tap(options) {
|
|
|
1384
1430
|
let beforeScaleFactor;
|
|
1385
1431
|
let beforeDims;
|
|
1386
1432
|
{
|
|
1387
|
-
const before = await captureScreenshot(platform, targetUdid);
|
|
1433
|
+
const before = await captureScreenshot(platform, targetUdid, targetSerial);
|
|
1388
1434
|
beforeBuffer = before?.buffer || null;
|
|
1389
1435
|
beforeScaleFactor = before?.scaleFactor;
|
|
1390
1436
|
if (before && before.width > 0 && before.height > 0) {
|
|
@@ -1407,13 +1453,13 @@ export async function tap(options) {
|
|
|
1407
1453
|
try {
|
|
1408
1454
|
switch (strat) {
|
|
1409
1455
|
case "fiber":
|
|
1410
|
-
result = await withTimeout(tryFiberStrategy(query, index, maxTraversalDepth, evidence, deviceName), budget, `fiber`);
|
|
1456
|
+
result = await withTimeout(tryFiberStrategy(query, index, maxTraversalDepth, evidence, deviceName, options.duration !== undefined), budget, `fiber`);
|
|
1411
1457
|
break;
|
|
1412
1458
|
case "accessibility":
|
|
1413
|
-
result = await withCancelableTimeout((signal) => tryAccessibilityStrategy(query, index, platform, targetUdid, evidence, signal), budget, `accessibility`);
|
|
1459
|
+
result = await withCancelableTimeout((signal) => tryAccessibilityStrategy(query, index, platform, targetUdid, evidence, signal, targetSerial, options.duration), budget, `accessibility`);
|
|
1414
1460
|
break;
|
|
1415
1461
|
case "ocr":
|
|
1416
|
-
result = await withCancelableTimeout((signal) => tryOcrStrategy(query, platform, targetUdid, evidence, signal), budget, `ocr`);
|
|
1462
|
+
result = await withCancelableTimeout((signal) => tryOcrStrategy(query, platform, targetUdid, evidence, signal, targetSerial, options.duration), budget, `ocr`);
|
|
1417
1463
|
break;
|
|
1418
1464
|
case "coordinate":
|
|
1419
1465
|
result = await withTimeout(tryCoordinateStrategy(query.x, query.y, platform, beforeScaleFactor != null
|
|
@@ -1422,7 +1468,7 @@ export async function tap(options) {
|
|
|
1422
1468
|
originalHeight: beforeDims?.height ?? 0,
|
|
1423
1469
|
scaleFactor: beforeScaleFactor
|
|
1424
1470
|
}
|
|
1425
|
-
: app?.lastScreenshot, targetUdid), budget, `coordinate`);
|
|
1471
|
+
: app?.lastScreenshot, targetUdid, targetSerial, options.duration), budget, `coordinate`);
|
|
1426
1472
|
break;
|
|
1427
1473
|
default:
|
|
1428
1474
|
result = { success: false, reason: `Unknown strategy: ${strat}` };
|
|
@@ -1461,6 +1507,7 @@ export async function tap(options) {
|
|
|
1461
1507
|
platform,
|
|
1462
1508
|
beforeBuffer,
|
|
1463
1509
|
udid: targetUdid,
|
|
1510
|
+
deviceId: targetSerial,
|
|
1464
1511
|
beforeScaleFactor,
|
|
1465
1512
|
markerPx: strategyMarker
|
|
1466
1513
|
}));
|
|
@@ -1474,6 +1521,7 @@ export async function tap(options) {
|
|
|
1474
1521
|
shouldScreenshot: true,
|
|
1475
1522
|
beforeBuffer,
|
|
1476
1523
|
udid: targetUdid,
|
|
1524
|
+
deviceId: targetSerial,
|
|
1477
1525
|
beforeScaleFactor,
|
|
1478
1526
|
markerPx: strategyMarker
|
|
1479
1527
|
}));
|
|
@@ -1501,6 +1549,13 @@ export async function tap(options) {
|
|
|
1501
1549
|
screenshot,
|
|
1502
1550
|
verification
|
|
1503
1551
|
});
|
|
1552
|
+
const strategyLongPress = buildLongPressReport({
|
|
1553
|
+
durationMs: options.duration,
|
|
1554
|
+
hasLongPress: result.hasLongPress,
|
|
1555
|
+
element: result.pressed
|
|
1556
|
+
});
|
|
1557
|
+
if (strategyLongPress)
|
|
1558
|
+
successResult.longPress = strategyLongPress;
|
|
1504
1559
|
if (shadowWarning) {
|
|
1505
1560
|
successResult.warning = successResult.warning
|
|
1506
1561
|
? `${successResult.warning}\n${shadowWarning}`
|
|
@@ -1558,16 +1613,19 @@ export async function tap(options) {
|
|
|
1558
1613
|
if (platform === "ios") {
|
|
1559
1614
|
const safeAreaTop = await getIOSSafeAreaTop(targetUdid);
|
|
1560
1615
|
const tapY = (safeAreaTop > 0 && coords.y < safeAreaTop) ? coords.y + safeAreaTop : coords.y;
|
|
1561
|
-
await iosTap(coords.x, tapY, { udid: targetUdid });
|
|
1616
|
+
await iosTap(coords.x, tapY, { udid: targetUdid, duration: options.duration });
|
|
1562
1617
|
coords.y = tapY;
|
|
1563
1618
|
}
|
|
1564
1619
|
else {
|
|
1565
1620
|
const { androidGetDensity } = await import("../core/android.js");
|
|
1566
|
-
const densityResult = await androidGetDensity();
|
|
1621
|
+
const densityResult = await androidGetDensity(targetSerial);
|
|
1567
1622
|
const densityScale = (densityResult.density || 420) / 160;
|
|
1568
1623
|
const pxX = Math.round(coords.x * densityScale);
|
|
1569
1624
|
const pxY = Math.round(coords.y * densityScale);
|
|
1570
|
-
await androidTap(pxX, pxY);
|
|
1625
|
+
const fiberTap = await androidTap(pxX, pxY, targetSerial, options.duration);
|
|
1626
|
+
if (!fiberTap.success) {
|
|
1627
|
+
throw new Error(fiberTap.error || "adb tap failed");
|
|
1628
|
+
}
|
|
1571
1629
|
coords.x = pxX;
|
|
1572
1630
|
coords.y = pxY;
|
|
1573
1631
|
coords.unit = "pixels";
|
|
@@ -1588,7 +1646,7 @@ export async function tap(options) {
|
|
|
1588
1646
|
else {
|
|
1589
1647
|
try {
|
|
1590
1648
|
const { androidGetDensity } = await import("../core/android.js");
|
|
1591
|
-
const d = await androidGetDensity();
|
|
1649
|
+
const d = await androidGetDensity(targetSerial);
|
|
1592
1650
|
fnDensity = (d.density || 420) / 160;
|
|
1593
1651
|
}
|
|
1594
1652
|
catch {
|
|
@@ -1608,6 +1666,7 @@ export async function tap(options) {
|
|
|
1608
1666
|
platform,
|
|
1609
1667
|
beforeBuffer,
|
|
1610
1668
|
udid: targetUdid,
|
|
1669
|
+
deviceId: targetSerial,
|
|
1611
1670
|
beforeScaleFactor,
|
|
1612
1671
|
markerPx: fiberMarker
|
|
1613
1672
|
}));
|
|
@@ -1619,6 +1678,7 @@ export async function tap(options) {
|
|
|
1619
1678
|
shouldScreenshot: true,
|
|
1620
1679
|
beforeBuffer,
|
|
1621
1680
|
udid: targetUdid,
|
|
1681
|
+
deviceId: targetSerial,
|
|
1622
1682
|
beforeScaleFactor,
|
|
1623
1683
|
markerPx: fiberMarker
|
|
1624
1684
|
}));
|
|
@@ -1632,7 +1692,7 @@ export async function tap(options) {
|
|
|
1632
1692
|
scaleFactor: screenshot.scaleFactor
|
|
1633
1693
|
};
|
|
1634
1694
|
}
|
|
1635
|
-
|
|
1695
|
+
const fiberSuccess = formatTapSuccess({
|
|
1636
1696
|
method: "fiber+native",
|
|
1637
1697
|
query,
|
|
1638
1698
|
pressed: result.pressed,
|
|
@@ -1646,13 +1706,21 @@ export async function tap(options) {
|
|
|
1646
1706
|
screenshot,
|
|
1647
1707
|
verification
|
|
1648
1708
|
});
|
|
1709
|
+
const fiberLongPress = buildLongPressReport({
|
|
1710
|
+
durationMs: options.duration,
|
|
1711
|
+
hasLongPress: result.hasLongPress,
|
|
1712
|
+
element: result.pressed
|
|
1713
|
+
});
|
|
1714
|
+
if (fiberLongPress)
|
|
1715
|
+
fiberSuccess.longPress = fiberLongPress;
|
|
1716
|
+
return fiberSuccess;
|
|
1649
1717
|
}
|
|
1650
1718
|
catch {
|
|
1651
1719
|
}
|
|
1652
1720
|
}
|
|
1653
1721
|
if (result.matches && result.ambiguous) {
|
|
1654
1722
|
const { screenshot: matchScreenshot } = shouldScreenshot
|
|
1655
|
-
? await verifyAndCapture({ platform, shouldVerify: false, shouldScreenshot: true, beforeBuffer: null, udid: targetUdid })
|
|
1723
|
+
? await verifyAndCapture({ platform, shouldVerify: false, shouldScreenshot: true, beforeBuffer: null, udid: targetUdid, deviceId: targetSerial })
|
|
1656
1724
|
: { screenshot: undefined };
|
|
1657
1725
|
if (matchScreenshot && app) {
|
|
1658
1726
|
app.lastScreenshot = {
|
|
@@ -1685,7 +1753,7 @@ export async function tap(options) {
|
|
|
1685
1753
|
suggestion = `All strategies timed out — the element's presence is UNKNOWN. Retry the tap (transient slowness is common on dense screens), or try a different strategy explicitly (e.g. strategy='fiber' if accessibility timed out). ` + suggestion;
|
|
1686
1754
|
}
|
|
1687
1755
|
const { screenshot: failScreenshot } = shouldScreenshot
|
|
1688
|
-
? await verifyAndCapture({ platform, shouldVerify: false, shouldScreenshot: true, beforeBuffer: null, udid: targetUdid })
|
|
1756
|
+
? await verifyAndCapture({ platform, shouldVerify: false, shouldScreenshot: true, beforeBuffer: null, udid: targetUdid, deviceId: targetSerial })
|
|
1689
1757
|
: { screenshot: undefined };
|
|
1690
1758
|
if (failScreenshot && app) {
|
|
1691
1759
|
app.lastScreenshot = {
|