agent-device 0.17.2 → 0.17.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/README.md +2 -2
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.17.2.apk → agent-device-android-multitouch-helper-0.17.4.apk} +0 -0
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.17.4.apk.sha256 +1 -0
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.17.2.manifest.json → agent-device-android-multitouch-helper-0.17.4.manifest.json} +4 -4
- package/android-snapshot-helper/README.md +3 -0
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.17.2.apk → agent-device-android-snapshot-helper-0.17.4.apk} +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.17.4.apk.sha256 +1 -0
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.17.2.manifest.json → agent-device-android-snapshot-helper-0.17.4.manifest.json} +6 -6
- package/dist/src/123.js +1 -1
- package/dist/src/1231.js +1 -1
- package/dist/src/1352.js +1 -1
- package/dist/src/221.js +2 -2
- package/dist/src/2403.js +3 -3
- package/dist/src/2415.js +37 -29
- package/dist/src/4012.js +4 -0
- package/dist/src/5611.js +1 -0
- package/dist/src/5827.js +1 -0
- package/dist/src/6088.js +1 -0
- package/dist/src/6133.js +1 -0
- package/dist/src/7847.js +1 -1
- package/dist/src/9471.js +1 -1
- package/dist/src/9542.js +6 -3
- package/dist/src/9616.js +2 -1
- package/dist/src/9818.js +1 -1
- package/dist/src/android-snapshot-helper.d.ts +0 -1
- package/dist/src/android.js +5 -5
- package/dist/src/apple.js +1 -1
- package/dist/src/apps.js +10 -10
- package/dist/src/args.js +53 -14
- package/dist/src/batch.js +1 -1
- package/dist/src/cli-output.js +30 -0
- package/dist/src/cli.js +15 -16
- package/dist/src/command-surface.js +1 -1
- package/dist/src/contracts.d.ts +52 -0
- package/dist/src/contracts.js +1 -1
- package/dist/src/find.js +1 -1
- package/dist/src/generic.js +5 -5
- package/dist/src/index.d.ts +127 -8
- package/dist/src/index.js +1 -1
- package/dist/src/input-actions.js +1 -1
- package/dist/src/interaction.js +1 -1
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-trace-recording.js +3 -3
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/selectors.d.ts +14 -0
- package/dist/src/selectors.js +1 -1
- package/dist/src/server.js +2 -2
- package/dist/src/session.js +8 -10
- package/dist/src/snapshot.js +2 -2
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +1 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +14 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +297 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +327 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +122 -173
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +153 -5
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +171 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +0 -10
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +64 -18
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +200 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +450 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +82 -178
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +425 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +77 -6
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +10 -1
- package/ios-runner/README.md +5 -5
- package/ios-runner/RUNNER_PROTOCOL.md +0 -1
- package/package.json +3 -3
- package/server.json +2 -2
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.17.2.apk.sha256 +0 -1
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.17.2.apk.sha256 +0 -1
- package/dist/src/1534.js +0 -1
- package/dist/src/1620.js +0 -1
- package/dist/src/1644.js +0 -2
- package/dist/src/2284.js +0 -1
- package/dist/src/2474.js +0 -1
- package/dist/src/2672.js +0 -1
- package/dist/src/3393.js +0 -1
- package/dist/src/5898.js +0 -1
- package/dist/src/695.js +0 -1
- package/dist/src/7455.js +0 -1
- package/dist/src/8173.js +0 -27
- package/dist/src/8407.js +0 -1
- package/dist/src/8699.js +0 -1
- package/dist/src/9238.js +0 -4
- package/dist/src/9673.js +0 -1
- package/dist/src/9974.js +0 -1
package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift
CHANGED
|
@@ -13,11 +13,11 @@ extension RunnerTests {
|
|
|
13
13
|
return (gestureStartUptimeMs, currentUptimeMs())
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
func synthesizedSwipeFallbackHoldDuration(durationMs: Double) -> TimeInterval {
|
|
17
17
|
min(max((durationMs / 5.0) / 1000.0, 0.016), 0.120)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
func coordinateDragHoldDuration() -> TimeInterval {
|
|
21
21
|
0.050
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -55,27 +55,6 @@ extension RunnerTests {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
private func performDragSeries(
|
|
59
|
-
count: Int,
|
|
60
|
-
pauseMs: Double,
|
|
61
|
-
pattern: String,
|
|
62
|
-
points: DragPoints,
|
|
63
|
-
_ drag: (_ x: Double, _ y: Double, _ x2: Double, _ y2: Double) -> RunnerInteractionOutcome
|
|
64
|
-
) -> RunnerInteractionOutcome {
|
|
65
|
-
var outcome = RunnerInteractionOutcome.performed
|
|
66
|
-
runSeries(count: count, pauseMs: pauseMs) { idx in
|
|
67
|
-
guard case .performed = outcome else {
|
|
68
|
-
return
|
|
69
|
-
}
|
|
70
|
-
let reverse = pattern == "ping-pong" && (idx % 2 == 1)
|
|
71
|
-
let startX = reverse ? points.x2 : points.x
|
|
72
|
-
let startY = reverse ? points.y2 : points.y
|
|
73
|
-
let endX = reverse ? points.x : points.x2
|
|
74
|
-
let endY = reverse ? points.y : points.y2
|
|
75
|
-
outcome = drag(startX, startY, endX, endY)
|
|
76
|
-
}
|
|
77
|
-
return outcome
|
|
78
|
-
}
|
|
79
58
|
|
|
80
59
|
/// Runs a gesture action with uniform timing capture. Touch gestures pass `idleTimeout: true`
|
|
81
60
|
/// (the default) to run inside the scroll idle-timeout + quiescence-skip wrapper; synthesis
|
|
@@ -84,7 +63,7 @@ extension RunnerTests {
|
|
|
84
63
|
///
|
|
85
64
|
/// NOTE: a new SYNTHESIS gesture must pass `idleTimeout: false` — the default `true` would wrap
|
|
86
65
|
/// it in the scroll idle-timeout/quiescence-skip path and change its runtime behavior.
|
|
87
|
-
|
|
66
|
+
func performGesture(
|
|
88
67
|
_ app: XCUIApplication,
|
|
89
68
|
idleTimeout: Bool = true,
|
|
90
69
|
_ action: () -> RunnerInteractionOutcome
|
|
@@ -244,6 +223,8 @@ extension RunnerTests {
|
|
|
244
223
|
}
|
|
245
224
|
|
|
246
225
|
func executeUptime() -> Response {
|
|
226
|
+
// Placeholder value: the transport layer (jsonResponse) overwrites currentUptimeMs with a
|
|
227
|
+
// fresher send-time stamp on every ok response; kept so direct callers still get a value.
|
|
247
228
|
Response(
|
|
248
229
|
ok: true,
|
|
249
230
|
data: DataPayload(currentUptimeMs: currentUptimeMs())
|
|
@@ -580,42 +561,6 @@ extension RunnerTests {
|
|
|
580
561
|
} catch {
|
|
581
562
|
return Response(ok: false, error: ErrorPayload(message: error.localizedDescription))
|
|
582
563
|
}
|
|
583
|
-
case .tapSeries:
|
|
584
|
-
guard let x = command.x, let y = command.y else {
|
|
585
|
-
return Response(ok: false, error: ErrorPayload(message: "tapSeries requires x and y"))
|
|
586
|
-
}
|
|
587
|
-
let count = max(Int(command.count ?? 1), 1)
|
|
588
|
-
let intervalMs = max(command.intervalMs ?? 0, 0)
|
|
589
|
-
let doubleTap = command.doubleTap ?? false
|
|
590
|
-
let touchFrame = resolvedTouchVisualizationFrame(app: activeApp, x: x, y: y)
|
|
591
|
-
if doubleTap {
|
|
592
|
-
let (timing, outcome) = performGesture(activeApp) {
|
|
593
|
-
var outcome = RunnerInteractionOutcome.performed
|
|
594
|
-
runSeries(count: count, pauseMs: intervalMs) { _ in
|
|
595
|
-
if case .performed = outcome {
|
|
596
|
-
outcome = doubleTapAt(app: activeApp, x: x, y: y)
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
return outcome
|
|
600
|
-
}
|
|
601
|
-
if let response = unsupportedResponse(for: outcome) {
|
|
602
|
-
return response
|
|
603
|
-
}
|
|
604
|
-
return gestureResponse(message: "tap series", timing: timing, frame: .touch(touchFrame))
|
|
605
|
-
}
|
|
606
|
-
let (timing, outcome) = performGesture(activeApp) {
|
|
607
|
-
var outcome = RunnerInteractionOutcome.performed
|
|
608
|
-
runSeries(count: count, pauseMs: intervalMs) { _ in
|
|
609
|
-
if case .performed = outcome {
|
|
610
|
-
outcome = tapAt(app: activeApp, x: x, y: y)
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
return outcome
|
|
614
|
-
}
|
|
615
|
-
if let response = unsupportedResponse(for: outcome) {
|
|
616
|
-
return response
|
|
617
|
-
}
|
|
618
|
-
return gestureResponse(message: "tap series", timing: timing, frame: .touch(touchFrame))
|
|
619
564
|
case .longPress:
|
|
620
565
|
guard let x = command.x, let y = command.y else {
|
|
621
566
|
return Response(ok: false, error: ErrorPayload(message: "longPress requires x and y"))
|
|
@@ -633,114 +578,63 @@ extension RunnerTests {
|
|
|
633
578
|
guard let x = command.x, let y = command.y, let x2 = command.x2, let y2 = command.y2 else {
|
|
634
579
|
return Response(ok: false, error: ErrorPayload(message: "drag requires x, y, x2, and y2"))
|
|
635
580
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
581
|
+
return executeDragGesture(
|
|
582
|
+
activeApp: activeApp,
|
|
583
|
+
x: x,
|
|
584
|
+
y: y,
|
|
585
|
+
x2: x2,
|
|
586
|
+
y2: y2,
|
|
587
|
+
durationMs: command.durationMs,
|
|
588
|
+
synthesized: command.synthesized == true,
|
|
589
|
+
message: "dragged"
|
|
643
590
|
)
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
591
|
+
case .scroll:
|
|
592
|
+
// Fused frame-resolve + drag scroll for non-tvOS. Resolves the interaction frame via
|
|
593
|
+
// resolvedTouchReferenceFrame, computes drag endpoints with the Swift port of
|
|
594
|
+
// buildScrollGesturePlan, then runs the same non-synthesized drag path scroll's drag used.
|
|
595
|
+
guard let direction = command.direction,
|
|
596
|
+
direction == "up" || direction == "down" || direction == "left" || direction == "right"
|
|
597
|
+
else {
|
|
598
|
+
return Response(
|
|
599
|
+
ok: false,
|
|
600
|
+
error: ErrorPayload(
|
|
601
|
+
code: "INVALID_ARGS",
|
|
602
|
+
message: "scroll requires direction up|down|left|right"
|
|
655
603
|
)
|
|
656
|
-
}
|
|
657
|
-
if case .performed = outcome {
|
|
658
|
-
return gestureResponse(message: "dragged", timing: timing, frame: .drag(dragFrame))
|
|
659
|
-
}
|
|
660
|
-
fallback = gestureFallback(strategy: "xctest-coordinate-drag", from: outcome)
|
|
661
|
-
}
|
|
662
|
-
let holdDuration = command.synthesized == true
|
|
663
|
-
? synthesizedSwipeFallbackHoldDuration(durationMs: command.durationMs ?? 250)
|
|
664
|
-
: coordinateDragHoldDuration()
|
|
665
|
-
let (timing, outcome) = performGesture(activeApp) {
|
|
666
|
-
dragAt(
|
|
667
|
-
app: activeApp,
|
|
668
|
-
x: dragPoints.x,
|
|
669
|
-
y: dragPoints.y,
|
|
670
|
-
x2: dragPoints.x2,
|
|
671
|
-
y2: dragPoints.y2,
|
|
672
|
-
holdDuration: holdDuration
|
|
673
604
|
)
|
|
674
605
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
frame: .drag(dragFrame),
|
|
682
|
-
fallback: fallback
|
|
683
|
-
)
|
|
684
|
-
case .dragSeries:
|
|
685
|
-
guard let x = command.x, let y = command.y, let x2 = command.x2, let y2 = command.y2 else {
|
|
686
|
-
return Response(ok: false, error: ErrorPayload(message: "dragSeries requires x, y, x2, and y2"))
|
|
687
|
-
}
|
|
688
|
-
let count = max(Int(command.count ?? 1), 1)
|
|
689
|
-
let pauseMs = max(command.pauseMs ?? 0, 0)
|
|
690
|
-
let pattern = command.pattern ?? "one-way"
|
|
691
|
-
if pattern != "one-way" && pattern != "ping-pong" {
|
|
692
|
-
return Response(ok: false, error: ErrorPayload(message: "dragSeries pattern must be one-way or ping-pong"))
|
|
693
|
-
}
|
|
694
|
-
let dragPoints = keyboardAvoidingDragPoints(app: activeApp, x: x, y: y, x2: x2, y2: y2)
|
|
695
|
-
var fallback: GestureFallback?
|
|
696
|
-
if command.synthesized == true {
|
|
697
|
-
let durationMs = min(max(command.durationMs ?? 250, 16), 10000)
|
|
698
|
-
let (timing, outcome) = performGesture(activeApp, idleTimeout: false) {
|
|
699
|
-
performDragSeries(
|
|
700
|
-
count: count,
|
|
701
|
-
pauseMs: pauseMs,
|
|
702
|
-
pattern: pattern,
|
|
703
|
-
points: dragPoints
|
|
704
|
-
) { startX, startY, endX, endY in
|
|
705
|
-
synthesizedDragAt(
|
|
706
|
-
app: activeApp,
|
|
707
|
-
x: startX,
|
|
708
|
-
y: startY,
|
|
709
|
-
x2: endX,
|
|
710
|
-
y2: endY,
|
|
711
|
-
durationMs: durationMs
|
|
712
|
-
)
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
if case .performed = outcome {
|
|
716
|
-
return gestureResponse(message: "drag series", timing: timing)
|
|
717
|
-
}
|
|
718
|
-
fallback = gestureFallback(strategy: "xctest-coordinate-drag-series", from: outcome)
|
|
606
|
+
let frame = resolvedTouchReferenceFrame(app: activeApp, appFrame: activeApp.frame)
|
|
607
|
+
guard frame.width > 0, frame.height > 0 else {
|
|
608
|
+
return Response(
|
|
609
|
+
ok: false,
|
|
610
|
+
error: ErrorPayload(message: "scroll could not resolve a usable interaction frame")
|
|
611
|
+
)
|
|
719
612
|
}
|
|
720
|
-
let
|
|
721
|
-
|
|
722
|
-
:
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
x: startX,
|
|
733
|
-
y: startY,
|
|
734
|
-
x2: endX,
|
|
735
|
-
y2: endY,
|
|
736
|
-
holdDuration: holdDuration
|
|
613
|
+
guard let plan = runnerScrollGesturePlan(
|
|
614
|
+
direction: direction,
|
|
615
|
+
amount: command.amount,
|
|
616
|
+
pixels: command.pixels,
|
|
617
|
+
referenceWidth: frame.width,
|
|
618
|
+
referenceHeight: frame.height
|
|
619
|
+
) else {
|
|
620
|
+
return Response(
|
|
621
|
+
ok: false,
|
|
622
|
+
error: ErrorPayload(
|
|
623
|
+
code: "INVALID_ARGS",
|
|
624
|
+
message: "scroll could not compute a gesture plan"
|
|
737
625
|
)
|
|
738
|
-
|
|
739
|
-
}
|
|
740
|
-
if let response = unsupportedResponse(for: outcome) {
|
|
741
|
-
return response
|
|
626
|
+
)
|
|
742
627
|
}
|
|
743
|
-
return
|
|
628
|
+
return executeDragGesture(
|
|
629
|
+
activeApp: activeApp,
|
|
630
|
+
x: frame.minX + plan.x1,
|
|
631
|
+
y: frame.minY + plan.y1,
|
|
632
|
+
x2: frame.minX + plan.x2,
|
|
633
|
+
y2: frame.minY + plan.y2,
|
|
634
|
+
durationMs: nil,
|
|
635
|
+
synthesized: false,
|
|
636
|
+
message: "scrolled"
|
|
637
|
+
)
|
|
744
638
|
case .remotePress:
|
|
745
639
|
guard let button = tvRemoteButton(from: command.remoteButton) else {
|
|
746
640
|
return Response(ok: false, error: ErrorPayload(message: "remotePress requires remoteButton"))
|
|
@@ -759,17 +653,6 @@ extension RunnerTests {
|
|
|
759
653
|
response = executeTypeCommand(activeApp: activeApp, command: command)
|
|
760
654
|
}
|
|
761
655
|
return response ?? Response(ok: false, error: ErrorPayload(message: "type produced no response"))
|
|
762
|
-
case .interactionFrame:
|
|
763
|
-
let frame = resolvedTouchReferenceFrame(app: activeApp, appFrame: activeApp.frame)
|
|
764
|
-
return Response(
|
|
765
|
-
ok: true,
|
|
766
|
-
data: DataPayload(
|
|
767
|
-
x: frame.minX,
|
|
768
|
-
y: frame.minY,
|
|
769
|
-
referenceWidth: frame.width,
|
|
770
|
-
referenceHeight: frame.height
|
|
771
|
-
)
|
|
772
|
-
)
|
|
773
656
|
case .swipe:
|
|
774
657
|
guard let direction = command.direction else {
|
|
775
658
|
return Response(ok: false, error: ErrorPayload(message: "swipe requires direction"))
|
|
@@ -808,7 +691,6 @@ extension RunnerTests {
|
|
|
808
691
|
case .snapshot:
|
|
809
692
|
let options = SnapshotOptions(
|
|
810
693
|
interactiveOnly: command.interactiveOnly ?? false,
|
|
811
|
-
compact: command.compact ?? false,
|
|
812
694
|
depth: command.depth,
|
|
813
695
|
scope: command.scope,
|
|
814
696
|
raw: command.raw ?? false
|
|
@@ -960,6 +842,8 @@ extension RunnerTests {
|
|
|
960
842
|
return response
|
|
961
843
|
}
|
|
962
844
|
return gestureResponse(message: "pinched", timing: timing)
|
|
845
|
+
case .sequence:
|
|
846
|
+
return executeSequence(command: command, activeApp: activeApp)
|
|
963
847
|
case .rotateGesture:
|
|
964
848
|
guard let degrees = command.degrees, degrees.isFinite else {
|
|
965
849
|
return Response(ok: false, error: ErrorPayload(message: "rotateGesture requires degrees"))
|
|
@@ -1023,6 +907,71 @@ extension RunnerTests {
|
|
|
1023
907
|
}
|
|
1024
908
|
}
|
|
1025
909
|
|
|
910
|
+
/// Shared drag execution for `.drag` and the fused `.scroll`. Mirrors the original `.drag` body
|
|
911
|
+
/// exactly: keyboardAvoidingDragPoints -> resolvedDragVisualizationFrame -> synthesized branch
|
|
912
|
+
/// (16-10000ms clamp) or non-synthesized dragAt with coordinateDragHoldDuration ->
|
|
913
|
+
/// gestureResponse(.drag). `.scroll` always passes synthesized: false, pinning the same
|
|
914
|
+
/// non-synthesized drag path scroll's drag used today.
|
|
915
|
+
private func executeDragGesture(
|
|
916
|
+
activeApp: XCUIApplication,
|
|
917
|
+
x: Double,
|
|
918
|
+
y: Double,
|
|
919
|
+
x2: Double,
|
|
920
|
+
y2: Double,
|
|
921
|
+
durationMs: Double?,
|
|
922
|
+
synthesized: Bool,
|
|
923
|
+
message: String
|
|
924
|
+
) -> Response {
|
|
925
|
+
let dragPoints = keyboardAvoidingDragPoints(app: activeApp, x: x, y: y, x2: x2, y2: y2)
|
|
926
|
+
let dragFrame = resolvedDragVisualizationFrame(
|
|
927
|
+
app: activeApp,
|
|
928
|
+
x: dragPoints.x,
|
|
929
|
+
y: dragPoints.y,
|
|
930
|
+
x2: dragPoints.x2,
|
|
931
|
+
y2: dragPoints.y2
|
|
932
|
+
)
|
|
933
|
+
var fallback: GestureFallback?
|
|
934
|
+
if synthesized {
|
|
935
|
+
let durationMs = min(max(durationMs ?? 250, 16), 10000)
|
|
936
|
+
let (timing, outcome) = performGesture(activeApp, idleTimeout: false) {
|
|
937
|
+
synthesizedDragAt(
|
|
938
|
+
app: activeApp,
|
|
939
|
+
x: dragPoints.x,
|
|
940
|
+
y: dragPoints.y,
|
|
941
|
+
x2: dragPoints.x2,
|
|
942
|
+
y2: dragPoints.y2,
|
|
943
|
+
durationMs: durationMs
|
|
944
|
+
)
|
|
945
|
+
}
|
|
946
|
+
if case .performed = outcome {
|
|
947
|
+
return gestureResponse(message: message, timing: timing, frame: .drag(dragFrame))
|
|
948
|
+
}
|
|
949
|
+
fallback = gestureFallback(strategy: "xctest-coordinate-drag", from: outcome)
|
|
950
|
+
}
|
|
951
|
+
let holdDuration = synthesized
|
|
952
|
+
? synthesizedSwipeFallbackHoldDuration(durationMs: durationMs ?? 250)
|
|
953
|
+
: coordinateDragHoldDuration()
|
|
954
|
+
let (timing, outcome) = performGesture(activeApp) {
|
|
955
|
+
dragAt(
|
|
956
|
+
app: activeApp,
|
|
957
|
+
x: dragPoints.x,
|
|
958
|
+
y: dragPoints.y,
|
|
959
|
+
x2: dragPoints.x2,
|
|
960
|
+
y2: dragPoints.y2,
|
|
961
|
+
holdDuration: holdDuration
|
|
962
|
+
)
|
|
963
|
+
}
|
|
964
|
+
if let response = unsupportedResponse(for: outcome) {
|
|
965
|
+
return response
|
|
966
|
+
}
|
|
967
|
+
return gestureResponse(
|
|
968
|
+
message: message,
|
|
969
|
+
timing: timing,
|
|
970
|
+
frame: .drag(dragFrame),
|
|
971
|
+
fallback: fallback
|
|
972
|
+
)
|
|
973
|
+
}
|
|
974
|
+
|
|
1026
975
|
private func currentXCTestFailureCount() -> Int {
|
|
1027
976
|
return testRun?.failureCount ?? 0
|
|
1028
977
|
}
|
package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift
CHANGED
|
@@ -143,11 +143,11 @@ final class RunnerCommandJournal {
|
|
|
143
143
|
switch command.command {
|
|
144
144
|
case .snapshot, .screenshot:
|
|
145
145
|
return false
|
|
146
|
-
case .tap, .mouseClick, .
|
|
147
|
-
.remotePress, .type, .swipe, .findText, .querySelector, .readText, .back,
|
|
148
|
-
.backSystem, .home, .rotate, .appSwitcher, .keyboardDismiss, .keyboardReturn,
|
|
149
|
-
.
|
|
150
|
-
.shutdown:
|
|
146
|
+
case .tap, .mouseClick, .longPress, .drag,
|
|
147
|
+
.remotePress, .type, .swipe, .scroll, .findText, .querySelector, .readText, .back,
|
|
148
|
+
.backInApp, .backSystem, .home, .rotate, .appSwitcher, .keyboardDismiss, .keyboardReturn,
|
|
149
|
+
.alert, .pinch, .sequence, .rotateGesture, .transformGesture, .recordStart, .recordStop,
|
|
150
|
+
.status, .uptime, .shutdown:
|
|
151
151
|
return true
|
|
152
152
|
}
|
|
153
153
|
}
|
|
@@ -165,6 +165,46 @@ extension RunnerTests {
|
|
|
165
165
|
XCTAssertEqual(status.lifecycleState, RunnerCommandLifecycleState.notAccepted.rawValue)
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
func testStampingCurrentUptimePreservesPayload() {
|
|
169
|
+
let stamped = Response(ok: true, data: DataPayload(message: "recording started"))
|
|
170
|
+
.stampingCurrentUptimeMs(123.5)
|
|
171
|
+
|
|
172
|
+
XCTAssertEqual(stamped.ok, true)
|
|
173
|
+
XCTAssertEqual(stamped.data?.message, "recording started")
|
|
174
|
+
XCTAssertEqual(stamped.data?.currentUptimeMs, 123.5)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
func testStampingCurrentUptimeCreatesPayloadWhenNil() {
|
|
178
|
+
let stamped = Response(ok: true).stampingCurrentUptimeMs(456.0)
|
|
179
|
+
|
|
180
|
+
XCTAssertEqual(stamped.ok, true)
|
|
181
|
+
XCTAssertEqual(stamped.data?.currentUptimeMs, 456.0)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
func testStampingCurrentUptimeSkipsErrorResponses() {
|
|
185
|
+
let response = Response(ok: false, error: ErrorPayload(message: "boom"))
|
|
186
|
+
let stamped = response.stampingCurrentUptimeMs(789.0)
|
|
187
|
+
|
|
188
|
+
XCTAssertEqual(stamped.ok, false)
|
|
189
|
+
XCTAssertNil(stamped.data)
|
|
190
|
+
XCTAssertEqual(stamped.error?.message, "boom")
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
func testJournalStoredResponseStaysUnstamped() throws {
|
|
194
|
+
let journal = RunnerCommandJournal()
|
|
195
|
+
let recordStart = runnerJournalCommand("recordStart", id: "record-start-anchor")
|
|
196
|
+
|
|
197
|
+
journal.accept(command: recordStart)
|
|
198
|
+
journal.finish(
|
|
199
|
+
command: recordStart,
|
|
200
|
+
response: Response(ok: true, data: DataPayload(message: "recording started"))
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
let status = journal.status(commandId: "record-start-anchor")
|
|
204
|
+
let responseJson = try XCTUnwrap(status.lifecycleResponseJson)
|
|
205
|
+
XCTAssertFalse(responseJson.contains("currentUptimeMs"))
|
|
206
|
+
}
|
|
207
|
+
|
|
168
208
|
func testCommandJournalRetentionPolicy() throws {
|
|
169
209
|
let journal = RunnerCommandJournal()
|
|
170
210
|
|
|
@@ -219,6 +259,38 @@ extension RunnerTests {
|
|
|
219
259
|
XCTAssertEqual(screenshotStatus.lifecycleResponseOk, true)
|
|
220
260
|
XCTAssertNil(screenshotStatus.lifecycleResponseJson)
|
|
221
261
|
|
|
262
|
+
let scroll = runnerJournalCommand("scroll", id: "scroll-drag")
|
|
263
|
+
journal.accept(command: scroll)
|
|
264
|
+
journal.finish(
|
|
265
|
+
command: scroll,
|
|
266
|
+
response: Response(
|
|
267
|
+
ok: true,
|
|
268
|
+
data: DataPayload(
|
|
269
|
+
message: "scrolled",
|
|
270
|
+
gestureStartUptimeMs: 1,
|
|
271
|
+
gestureEndUptimeMs: 2,
|
|
272
|
+
x: 155,
|
|
273
|
+
y: 420,
|
|
274
|
+
x2: 155,
|
|
275
|
+
y2: 301,
|
|
276
|
+
referenceWidth: 300,
|
|
277
|
+
referenceHeight: 600
|
|
278
|
+
)
|
|
279
|
+
)
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
let scrollStatus = journal.status(commandId: "scroll-drag")
|
|
283
|
+
XCTAssertEqual(scrollStatus.lifecycleState, RunnerCommandLifecycleState.completed.rawValue)
|
|
284
|
+
XCTAssertEqual(scrollStatus.lifecycleResponseOk, true)
|
|
285
|
+
XCTAssertNotNil(scrollStatus.lifecycleResponseJson)
|
|
286
|
+
let scrollResponse = try decodeRunnerJournalResponse(scrollStatus.lifecycleResponseJson)
|
|
287
|
+
XCTAssertEqual(scrollResponse.data?.x, 155)
|
|
288
|
+
XCTAssertEqual(scrollResponse.data?.y, 420)
|
|
289
|
+
XCTAssertEqual(scrollResponse.data?.x2, 155)
|
|
290
|
+
XCTAssertEqual(scrollResponse.data?.y2, 301)
|
|
291
|
+
XCTAssertEqual(scrollResponse.data?.referenceWidth, 300)
|
|
292
|
+
XCTAssertEqual(scrollResponse.data?.referenceHeight, 600)
|
|
293
|
+
|
|
222
294
|
let largeRead = runnerJournalCommand("readText", id: "large-read")
|
|
223
295
|
journal.accept(command: largeRead)
|
|
224
296
|
journal.finish(
|
|
@@ -262,6 +334,82 @@ extension RunnerTests {
|
|
|
262
334
|
XCTAssertEqual(status.lifecycleErrorHint, hint)
|
|
263
335
|
}
|
|
264
336
|
|
|
337
|
+
func testCommandJournalRetainsCompletedSequenceResults() throws {
|
|
338
|
+
let journal = RunnerCommandJournal()
|
|
339
|
+
let sequence = runnerJournalCommand("sequence", id: "sequence-completed")
|
|
340
|
+
let results = (0..<20).map { _ in
|
|
341
|
+
SequenceStepResult(
|
|
342
|
+
ok: true,
|
|
343
|
+
kind: "tap",
|
|
344
|
+
errorCode: nil,
|
|
345
|
+
errorMessage: nil,
|
|
346
|
+
gestureStartUptimeMs: 100,
|
|
347
|
+
gestureEndUptimeMs: 120
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
journal.accept(command: sequence)
|
|
352
|
+
journal.finish(
|
|
353
|
+
command: sequence,
|
|
354
|
+
response: Response(
|
|
355
|
+
ok: true,
|
|
356
|
+
data: DataPayload(
|
|
357
|
+
message: "sequence",
|
|
358
|
+
completedSteps: 20,
|
|
359
|
+
failedStepIndex: nil,
|
|
360
|
+
sequenceResults: results
|
|
361
|
+
)
|
|
362
|
+
)
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
let status = journal.status(commandId: "sequence-completed")
|
|
366
|
+
XCTAssertEqual(status.lifecycleState, RunnerCommandLifecycleState.completed.rawValue)
|
|
367
|
+
XCTAssertEqual(status.lifecycleResponseOk, true)
|
|
368
|
+
let json = try XCTUnwrap(status.lifecycleResponseJson)
|
|
369
|
+
// Worst-case 20-step response must stay under the 16KB journal retention cap.
|
|
370
|
+
XCTAssertLessThan(json.utf8.count, 16 * 1024)
|
|
371
|
+
let decoded = try decodeRunnerJournalResponse(status.lifecycleResponseJson)
|
|
372
|
+
XCTAssertEqual(decoded.data?.completedSteps, 20)
|
|
373
|
+
XCTAssertEqual(decoded.data?.sequenceResults?.count, 20)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
func testCommandJournalRetainsFailedSequenceResults() throws {
|
|
377
|
+
let journal = RunnerCommandJournal()
|
|
378
|
+
let sequence = runnerJournalCommand("sequence", id: "sequence-failed")
|
|
379
|
+
let longError = String(repeating: "z", count: 200)
|
|
380
|
+
let results: [SequenceStepResult] = [
|
|
381
|
+
SequenceStepResult(ok: true, kind: "tap", errorCode: nil, errorMessage: nil,
|
|
382
|
+
gestureStartUptimeMs: 100, gestureEndUptimeMs: 120),
|
|
383
|
+
SequenceStepResult(ok: true, kind: "tap", errorCode: nil, errorMessage: nil,
|
|
384
|
+
gestureStartUptimeMs: 130, gestureEndUptimeMs: 150),
|
|
385
|
+
SequenceStepResult(ok: false, kind: "drag", errorCode: "UNSUPPORTED_OPERATION",
|
|
386
|
+
errorMessage: longError, gestureStartUptimeMs: 160, gestureEndUptimeMs: 180),
|
|
387
|
+
]
|
|
388
|
+
|
|
389
|
+
journal.accept(command: sequence)
|
|
390
|
+
journal.finish(
|
|
391
|
+
command: sequence,
|
|
392
|
+
response: Response(
|
|
393
|
+
ok: true,
|
|
394
|
+
data: DataPayload(
|
|
395
|
+
message: "sequence",
|
|
396
|
+
completedSteps: 2,
|
|
397
|
+
failedStepIndex: 2,
|
|
398
|
+
sequenceResults: results
|
|
399
|
+
)
|
|
400
|
+
)
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
let status = journal.status(commandId: "sequence-failed")
|
|
404
|
+
XCTAssertEqual(status.lifecycleState, RunnerCommandLifecycleState.completed.rawValue)
|
|
405
|
+
let decoded = try decodeRunnerJournalResponse(status.lifecycleResponseJson)
|
|
406
|
+
XCTAssertEqual(decoded.data?.completedSteps, 2)
|
|
407
|
+
XCTAssertEqual(decoded.data?.failedStepIndex, 2)
|
|
408
|
+
XCTAssertEqual(decoded.data?.sequenceResults?.count, 3)
|
|
409
|
+
XCTAssertEqual(decoded.data?.sequenceResults?[2].ok, false)
|
|
410
|
+
XCTAssertEqual(decoded.data?.sequenceResults?[2].errorCode, "UNSUPPORTED_OPERATION")
|
|
411
|
+
}
|
|
412
|
+
|
|
265
413
|
private func runnerJournalCommand(_ command: String, id: String) -> Command {
|
|
266
414
|
let json = #"{"command":"\#(command)","commandId":"\#(id)"}"#
|
|
267
415
|
return try! JSONDecoder().decode(Command.self, from: Data(json.utf8))
|