agent-device 0.21.8 → 0.21.9
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/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.8.apk → agent-device-android-ime-helper-0.21.9.apk} +0 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.9.apk.sha256 +1 -0
- package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.8.manifest.json → agent-device-android-ime-helper-0.21.9.manifest.json} +4 -4
- package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.8.apk → agent-device-android-snapshot-helper-0.21.9.apk} +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.9.apk.sha256 +1 -0
- package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.8.manifest.json → agent-device-android-snapshot-helper-0.21.9.manifest.json} +6 -6
- package/apple/macos-helper/Sources/AgentDeviceMacOSDeviceHub/DeviceHubWindowInventory.swift +65 -0
- package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/DeviceHubPose.swift +45 -140
- package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/DeviceHubSidebar.swift +57 -0
- package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/DeviceHubWindowDiscovery.swift +108 -0
- package/apple/macos-helper/Tests/AgentDeviceMacOSDeviceHubTests/DeviceHubWindowInventoryTests.swift +65 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +1 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAppScreenCapture.swift +175 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +10 -2
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +42 -15
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +8 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +45 -22
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +39 -734
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Keyboard.swift +20 -4
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +63 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +13 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +25 -10
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +5 -562
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotAcquisition.swift +566 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +5 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedInteraction.swift +594 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerXCTestEventBridge.h +35 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerXCTestEventBridge.m +160 -0
- package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/RecordingExportSupport.swift +3 -0
- package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-frames.swift +203 -0
- package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotCoordinateSpace.swift +5 -0
- package/dist/src/absence-observation-errors.js +1 -1
- package/dist/src/adapter.js +1 -1
- package/dist/src/agent-browser-lifecycle.js +1 -1
- package/dist/src/agent-browser-process-record.js +1 -1
- package/dist/src/agent-device-client.js +1 -1
- package/dist/src/alert-runtime.js +1 -1
- package/dist/src/android-adb.js +1 -1
- package/dist/src/android-input-ownership.js +1 -1
- package/dist/src/android.js +1 -1
- package/dist/src/app-launch.js +1 -1
- package/dist/src/app-log-runtime2.js +3 -3
- package/dist/src/app-resolution.js +2 -2
- package/dist/src/apple-runner-platform.js +1 -1
- package/dist/src/application-lifecycle-interaction.js +1 -1
- package/dist/src/artifact-paths.js +1 -0
- package/dist/src/back-mode.js +1 -1
- package/dist/src/cli-config.js +1 -1
- package/dist/src/cli-help.js +8 -7
- package/dist/src/cli.js +4 -4
- package/dist/src/client-types.d.ts +23 -5
- package/dist/src/command-tools.js +3 -3
- package/dist/src/connection-runtime.js +1 -1
- package/dist/src/device-claim-inspection.js +1 -1
- package/dist/src/device-claim-rule.js +2 -2
- package/dist/src/device-input-state.d.ts +1 -1
- package/dist/src/device-selection-resolver.js +1 -1
- package/dist/src/device.js +1 -1
- package/dist/src/device2.js +1 -1
- package/dist/src/dispatch-resolve.js +1 -1
- package/dist/src/doctor-facade.js +1 -1
- package/dist/src/durable-resource.js +1 -1
- package/dist/src/find.js +1 -1
- package/dist/src/finders.js +1 -1
- package/dist/src/fold-runtime.js +1 -0
- package/dist/src/gesture-admission.js +1 -1
- package/dist/src/interaction-positionals.js +1 -1
- package/dist/src/interaction-ref-policy.js +1 -1
- package/dist/src/interaction.js +1 -1
- package/dist/src/interaction2.js +1 -1
- package/dist/src/interactor-operation-catalog.js +1 -1
- package/dist/src/interactor.js +4 -4
- package/dist/src/interactors.js +1 -1
- package/dist/src/internal/bin.js +1 -1
- package/dist/src/internal/daemon.js +2 -2
- package/dist/src/inventory2.js +1 -1
- package/dist/src/ios-snapshot-acquisition.js +1 -1
- package/dist/src/ios-snapshot-runtime.js +1 -1
- package/dist/src/lease.js +1 -1
- package/dist/src/local-interactor-operation-set.js +1 -1
- package/dist/src/location-coordinates.js +1 -1
- package/dist/src/managed-device-scope.js +1 -1
- package/dist/src/mechanics.js +17 -17
- package/dist/src/mobile-snapshot-semantics.js +1 -1
- package/dist/src/mp4-duration.js +1 -0
- package/dist/src/multitouch-support.js +1 -1
- package/dist/src/owner-identity.js +1 -1
- package/dist/src/parameterized-recorded-fill.js +1 -1
- package/dist/src/perf-facade.js +2 -2
- package/dist/src/perf-runtime-plan.js +1 -1
- package/dist/src/platform-request-scope.js +1 -1
- package/dist/src/platform-runtime-managed-owner.js +1 -1
- package/dist/src/platform-runtime-open-target.js +1 -1
- package/dist/src/platform-runtime-operation-host.js +2 -2
- package/dist/src/platform-runtime-runtime-hints.js +1 -1
- package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
- package/dist/src/platform-runtime-unavailable.js +1 -1
- package/dist/src/platform-runtime.js +1 -1
- package/dist/src/platform-runtime2.js +1 -1
- package/dist/src/png-crop-bytes.js +1 -1
- package/dist/src/png-encode.js +1 -0
- package/dist/src/png-size.js +1 -0
- package/dist/src/provider-webdriver.js +1 -1
- package/dist/src/quality-warnings.js +1 -1
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-runtime.js +1 -1
- package/dist/src/recording-export-quality.js +1 -1
- package/dist/src/recording-scope.js +1 -0
- package/dist/src/recording.js +3 -0
- package/dist/src/registry.js +35 -35
- package/dist/src/replay.js +2 -2
- package/dist/src/request-admission.js +1 -1
- package/dist/src/request-runtime-binding.js +1 -1
- package/dist/src/result-serialization.js +1 -1
- package/dist/src/runner-cache-metadata.js +2 -2
- package/dist/src/runner-client.js +1 -1
- package/dist/src/runner-contract.js +1 -1
- package/dist/src/runner-dev-tools-security.js +1 -1
- package/dist/src/runner-device-readiness.js +1 -1
- package/dist/src/runner-disposal.js +1 -1
- package/dist/src/runner-operations-facade.js +1 -1
- package/dist/src/runner-presentation.js +1 -1
- package/dist/src/runner.js +1 -1
- package/dist/src/runtime.js +2 -2
- package/dist/src/runtime10.js +2 -1
- package/dist/src/runtime2.js +4 -4
- package/dist/src/runtime4.js +5 -5
- package/dist/src/runtime5.js +1 -1
- package/dist/src/runtime6.js +1 -1
- package/dist/src/runtime7.js +1 -1
- package/dist/src/runtime8.js +1 -1
- package/dist/src/runtime9.js +1 -1
- package/dist/src/screen-recording-admission-ledger.js +1 -1
- package/dist/src/screen-recording-resource-recovery.js +1 -1
- package/dist/src/screen-recording-transport.js +1 -1
- package/dist/src/screenshot-density.js +1 -1
- package/dist/src/screenshot-overlay-draw.js +1 -0
- package/dist/src/screenshot-runtime.js +1 -1
- package/dist/src/script.js +1 -1
- package/dist/src/scroll-gesture.js +1 -1
- package/dist/src/sdk-batch.js +2 -2
- package/dist/src/sdk-contracts.d.ts +7 -5
- package/dist/src/sdk-device.js +1 -1
- package/dist/src/sdk-finders.js +1 -1
- package/dist/src/sdk-selectors.d.ts +44 -1
- package/dist/src/sdk-selectors.js +1 -1
- package/dist/src/selection.js +0 -0
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/selectors.js +1 -1
- package/dist/src/semantic-index.js +1 -1
- package/dist/src/session-routing.js +1 -1
- package/dist/src/session-snapshot.js +1 -1
- package/dist/src/session-store.js +3 -3
- package/dist/src/session2.js +3 -3
- package/dist/src/simctl.js +1 -1
- package/dist/src/simulator-boot.js +1 -1
- package/dist/src/snapshot-runtime-binding.js +1 -1
- package/dist/src/snapshot-runtime.js +1 -1
- package/dist/src/snapshot-state.js +1 -1
- package/dist/src/snapshot4.js +1 -1
- package/dist/src/src.js +1 -1
- package/dist/src/src7.js +1 -1
- package/dist/src/stop-sequence.js +1 -1
- package/dist/src/text-entry-focus.js +1 -1
- package/dist/src/tree.js +1 -1
- package/dist/src/type-text-runtime.js +1 -1
- package/dist/src/video.js +2 -2
- package/package.json +1 -1
- package/server.json +2 -2
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.8.apk.sha256 +0 -1
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.8.apk.sha256 +0 -1
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAppScreenCapture.swift
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
#if canImport(UIKit)
|
|
3
|
+
import UIKit
|
|
4
|
+
#endif
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
struct CapturedAppScreen {
|
|
10
|
+
let image: RunnerImage
|
|
11
|
+
let displayID: UInt
|
|
12
|
+
let pixelWidth: Int
|
|
13
|
+
let pixelHeight: Int
|
|
14
|
+
let pixelsPerPoint: Double
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
enum RunnerAppScreenCaptureFailure: String, Error {
|
|
21
|
+
case unresolvedWindow = "APP_SCREEN_WINDOW_UNRESOLVED"
|
|
22
|
+
case unresolvedScreen = "APP_SCREEN_UNRESOLVED"
|
|
23
|
+
case unrenderableImage = "APP_SCREEN_CAPTURE_UNRENDERABLE"
|
|
24
|
+
|
|
25
|
+
init(_ failure: RunnerApplicationScreenFailure) {
|
|
26
|
+
switch failure {
|
|
27
|
+
case .unresolvedWindow:
|
|
28
|
+
self = .unresolvedWindow
|
|
29
|
+
default:
|
|
30
|
+
self = .unresolvedScreen
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var message: String {
|
|
35
|
+
switch self {
|
|
36
|
+
case .unresolvedWindow:
|
|
37
|
+
return "Neither the target app nor the system surface resolved a window, so no display could be named."
|
|
38
|
+
case .unresolvedScreen:
|
|
39
|
+
return "The resolved app window did not report the display it is shown on."
|
|
40
|
+
case .unrenderableImage:
|
|
41
|
+
return "The resolved display handed back an image that could not be drawn upright."
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
var hint: String {
|
|
46
|
+
switch self {
|
|
47
|
+
case .unresolvedWindow:
|
|
48
|
+
return "The capture follows the display that owns a window, so this is a runtime that is answering no window for any process; runner.log names the window frame and the query answer it refused."
|
|
49
|
+
case .unresolvedScreen:
|
|
50
|
+
return "Retry the capture. If it persists, the XCTest runtime is not reporting the window's display; runner.log names the window frame it resolved."
|
|
51
|
+
case .unrenderableImage:
|
|
52
|
+
return "Retry the capture. The display was resolved, so this is the capture itself failing, not a panel question."
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#if canImport(UIKit) && os(iOS)
|
|
58
|
+
extension RunnerTests {
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
func captureResolvedAppScreen(
|
|
71
|
+
app: XCUIApplication
|
|
72
|
+
) -> Result<CapturedAppScreen, RunnerAppScreenCaptureFailure> {
|
|
73
|
+
var outcome: Result<CapturedAppScreen, RunnerAppScreenCaptureFailure> = .failure(
|
|
74
|
+
.unresolvedScreen
|
|
75
|
+
)
|
|
76
|
+
let capture = {
|
|
77
|
+
outcome = Self.resolveCapturedAppScreen(app: app)
|
|
78
|
+
}
|
|
79
|
+
if Thread.isMainThread {
|
|
80
|
+
capture()
|
|
81
|
+
} else {
|
|
82
|
+
DispatchQueue.main.sync(execute: capture)
|
|
83
|
+
}
|
|
84
|
+
return outcome
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
func captureObservedScreen(app: XCUIApplication) -> Result<
|
|
98
|
+
CapturedAppScreen, RunnerAppScreenCaptureFailure
|
|
99
|
+
> {
|
|
100
|
+
selectObservedScreenCapture(
|
|
101
|
+
resolving: { captureResolvedAppScreen(app: app) },
|
|
102
|
+
fallingBack: { captureResolvedAppScreen(app: springboard) }
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
func selectObservedScreenCapture(
|
|
109
|
+
resolving: () -> Result<CapturedAppScreen, RunnerAppScreenCaptureFailure>,
|
|
110
|
+
fallingBack: () -> Result<CapturedAppScreen, RunnerAppScreenCaptureFailure>
|
|
111
|
+
) -> Result<CapturedAppScreen, RunnerAppScreenCaptureFailure> {
|
|
112
|
+
let outcome = resolving()
|
|
113
|
+
if case .failure(.unresolvedWindow) = outcome {
|
|
114
|
+
return fallingBack()
|
|
115
|
+
}
|
|
116
|
+
return outcome
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private static func resolveCapturedAppScreen(
|
|
120
|
+
app: XCUIApplication
|
|
121
|
+
) -> Result<CapturedAppScreen, RunnerAppScreenCaptureFailure> {
|
|
122
|
+
var screen: AnyObject?
|
|
123
|
+
var displayID: UInt = 0
|
|
124
|
+
var resolution: RunnerApplicationScreenFailure = .unresolvedScreen
|
|
125
|
+
guard RunnerResolveApplicationScreen(app, &screen, &displayID, &resolution),
|
|
126
|
+
let captureScreen = screen as? XCUIScreen
|
|
127
|
+
else {
|
|
128
|
+
return .failure(RunnerAppScreenCaptureFailure(resolution))
|
|
129
|
+
}
|
|
130
|
+
let image = captureScreen.screenshot().image
|
|
131
|
+
guard let upright = runnerUprightCaptureImage(image) else {
|
|
132
|
+
return .failure(.unrenderableImage)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
guard upright.scale.isFinite, upright.scale > 0 else {
|
|
138
|
+
return .failure(.unrenderableImage)
|
|
139
|
+
}
|
|
140
|
+
guard let cgImage = runnerCGImage(from: upright) else {
|
|
141
|
+
return .failure(.unrenderableImage)
|
|
142
|
+
}
|
|
143
|
+
return .success(
|
|
144
|
+
CapturedAppScreen(
|
|
145
|
+
image: upright,
|
|
146
|
+
displayID: displayID,
|
|
147
|
+
pixelWidth: cgImage.width,
|
|
148
|
+
pixelHeight: cgImage.height,
|
|
149
|
+
pixelsPerPoint: Double(upright.scale)
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
private static func runnerUprightCaptureImage(_ image: UIImage) -> UIImage? {
|
|
161
|
+
if image.imageOrientation == .up {
|
|
162
|
+
return image
|
|
163
|
+
}
|
|
164
|
+
guard image.size.width > 0, image.size.height > 0, image.scale.isFinite, image.scale > 0 else {
|
|
165
|
+
return nil
|
|
166
|
+
}
|
|
167
|
+
let format = UIGraphicsImageRendererFormat()
|
|
168
|
+
format.scale = image.scale
|
|
169
|
+
let renderer = UIGraphicsImageRenderer(size: image.size, format: format)
|
|
170
|
+
return renderer.image { _ in
|
|
171
|
+
image.draw(in: CGRect(origin: .zero, size: image.size))
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
#endif
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h
CHANGED
|
@@ -8,7 +8,11 @@ FOUNDATION_EXPORT double RunnerControlledScrollProgress(double t);
|
|
|
8
8
|
|
|
9
9
|
@interface RunnerSynthesizedGesture : NSObject
|
|
10
10
|
|
|
11
|
+
// `resolvedWindow` is the app window the caller already resolved for the gesture's reference
|
|
12
|
+
// frame. The record's display ID is read from that same window so geometry and routing can never
|
|
13
|
+
// name different windows; pass nil to fall back to the application's first window.
|
|
11
14
|
+ (NSString * _Nullable)synthesizeSwipeWithApplication:(id)application
|
|
15
|
+
resolvedWindow:(id _Nullable)resolvedWindow
|
|
12
16
|
x:(double)x
|
|
13
17
|
y:(double)y
|
|
14
18
|
x2:(double)x2
|
|
@@ -16,26 +20,30 @@ FOUNDATION_EXPORT double RunnerControlledScrollProgress(double t);
|
|
|
16
20
|
durationMs:(double)durationMs;
|
|
17
21
|
|
|
18
22
|
+ (NSString * _Nullable)synthesizeContinuousDragWithApplication:(id)application
|
|
23
|
+
resolvedWindow:(id _Nullable)resolvedWindow
|
|
19
24
|
x:(double)x
|
|
20
25
|
y:(double)y
|
|
21
26
|
x2:(double)x2
|
|
22
|
-
|
|
27
|
+
y2:(double)y2
|
|
23
28
|
durationMs:(double)durationMs;
|
|
24
29
|
|
|
25
30
|
+ (NSString * _Nullable)synthesizeControlledScrollWithApplication:(id)application
|
|
31
|
+
resolvedWindow:(id _Nullable)resolvedWindow
|
|
26
32
|
x:(double)x
|
|
27
33
|
y:(double)y
|
|
28
34
|
x2:(double)x2
|
|
29
|
-
|
|
35
|
+
y2:(double)y2
|
|
30
36
|
durationMs:(double)durationMs;
|
|
31
37
|
|
|
32
38
|
+ (NSString * _Nullable)synthesizeTapWithApplication:(id)application
|
|
39
|
+
resolvedWindow:(id _Nullable)resolvedWindow
|
|
33
40
|
x:(double)x
|
|
34
41
|
y:(double)y;
|
|
35
42
|
|
|
36
43
|
// Each pointer is an ordered array of { x, y, offsetMs } samples. The first sample
|
|
37
44
|
// starts contact; subsequent samples move it; all pointers lift at their final offset.
|
|
38
45
|
+ (NSString * _Nullable)synthesizeGestureWithApplication:(id)application
|
|
46
|
+
resolvedWindow:(id _Nullable)resolvedWindow
|
|
39
47
|
pointerSamples:(NSArray<NSArray<NSDictionary<NSString *, NSNumber *> *> *> *)pointerSamples;
|
|
40
48
|
|
|
41
49
|
// UIInterfaceOrientation of the app (1 portrait, 2 upsideDown, 3 landscapeRight,
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
static NSString *const RunnerGestureSynthesisSurface = @"event";
|
|
8
8
|
|
|
9
|
-
typedef id (*RunnerMsgSendInitRecord)(id, SEL, NSString *, NSInteger);
|
|
9
|
+
typedef id (*RunnerMsgSendInitRecord)(id, SEL, NSString *, NSUInteger, NSInteger);
|
|
10
10
|
typedef id (*RunnerMsgSendInitPath)(id, SEL, CGPoint, NSTimeInterval);
|
|
11
11
|
typedef void (*RunnerMsgSendPathMove)(id, SEL, CGPoint, NSTimeInterval);
|
|
12
12
|
typedef void (*RunnerMsgSendPathOffset)(id, SEL, NSTimeInterval);
|
|
13
13
|
|
|
14
|
-
// Gesture-specific extension of the shared bridge: the
|
|
15
|
-
// `initWithName:interfaceOrientation:` record initializer and the touch-path
|
|
14
|
+
// Gesture-specific extension of the shared bridge: the display-aware
|
|
15
|
+
// `initWithName:displayID:interfaceOrientation:` record initializer and the touch-path
|
|
16
16
|
// factory/mutator selectors, none of which text-entry synthesis needs.
|
|
17
17
|
typedef struct {
|
|
18
18
|
RunnerXCTestEventBridge core;
|
|
@@ -37,6 +37,7 @@ static NSString * _Nullable RunnerResolveGestureEventBridge(
|
|
|
37
37
|
);
|
|
38
38
|
static NSString * _Nullable RunnerCreateEventRecord(
|
|
39
39
|
id application,
|
|
40
|
+
id _Nullable resolvedWindow,
|
|
40
41
|
NSString *recordName,
|
|
41
42
|
RunnerGestureEventBridge *bridge,
|
|
42
43
|
id *record
|
|
@@ -74,13 +75,14 @@ static id RunnerSampledDragPointerPath(
|
|
|
74
75
|
static double RunnerSmoothstepProgress(double t);
|
|
75
76
|
static NSString * _Nullable RunnerTrySynthesizeDrag(
|
|
76
77
|
id application,
|
|
78
|
+
id _Nullable resolvedWindow,
|
|
77
79
|
CGPoint start,
|
|
78
80
|
CGPoint end,
|
|
79
81
|
double durationMs,
|
|
80
82
|
NSString *recordName,
|
|
81
83
|
RunnerDragPointerPathFactory pathFactory
|
|
82
84
|
);
|
|
83
|
-
static NSString * _Nullable RunnerTrySynthesizeTap(id application, CGPoint point);
|
|
85
|
+
static NSString * _Nullable RunnerTrySynthesizeTap(id application, id _Nullable resolvedWindow, CGPoint point);
|
|
84
86
|
// XCTest's proven swipe profile reaches the endpoint in 100 ms, then holds for the planned
|
|
85
87
|
// fling duration. Fast movement is what lets UIKit distinguish a fling from a timed pan.
|
|
86
88
|
static const NSTimeInterval RunnerSwipeMovementDurationSeconds = 0.1;
|
|
@@ -94,14 +96,16 @@ static id RunnerTapPointerPath(
|
|
|
94
96
|
@implementation RunnerSynthesizedGesture
|
|
95
97
|
|
|
96
98
|
+ (NSString * _Nullable)synthesizeSwipeWithApplication:(id)application
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
resolvedWindow:(id _Nullable)resolvedWindow
|
|
100
|
+
x:(double)x
|
|
101
|
+
y:(double)y
|
|
102
|
+
x2:(double)x2
|
|
103
|
+
y2:(double)y2
|
|
101
104
|
durationMs:(double)durationMs {
|
|
102
105
|
@try {
|
|
103
106
|
return RunnerTrySynthesizeDrag(
|
|
104
107
|
application,
|
|
108
|
+
resolvedWindow,
|
|
105
109
|
CGPointMake(x, y),
|
|
106
110
|
CGPointMake(x2, y2),
|
|
107
111
|
durationMs,
|
|
@@ -114,6 +118,7 @@ static id RunnerTapPointerPath(
|
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
+ (NSString * _Nullable)synthesizeContinuousDragWithApplication:(id)application
|
|
121
|
+
resolvedWindow:(id _Nullable)resolvedWindow
|
|
117
122
|
x:(double)x
|
|
118
123
|
y:(double)y
|
|
119
124
|
x2:(double)x2
|
|
@@ -122,6 +127,7 @@ static id RunnerTapPointerPath(
|
|
|
122
127
|
@try {
|
|
123
128
|
return RunnerTrySynthesizeDrag(
|
|
124
129
|
application,
|
|
130
|
+
resolvedWindow,
|
|
125
131
|
CGPointMake(x, y),
|
|
126
132
|
CGPointMake(x2, y2),
|
|
127
133
|
durationMs,
|
|
@@ -134,14 +140,16 @@ static id RunnerTapPointerPath(
|
|
|
134
140
|
}
|
|
135
141
|
|
|
136
142
|
+ (NSString * _Nullable)synthesizeControlledScrollWithApplication:(id)application
|
|
143
|
+
resolvedWindow:(id _Nullable)resolvedWindow
|
|
137
144
|
x:(double)x
|
|
138
145
|
y:(double)y
|
|
139
146
|
x2:(double)x2
|
|
140
|
-
|
|
141
|
-
|
|
147
|
+
y2:(double)y2
|
|
148
|
+
durationMs:(double)durationMs {
|
|
142
149
|
@try {
|
|
143
150
|
return RunnerTrySynthesizeDrag(
|
|
144
151
|
application,
|
|
152
|
+
resolvedWindow,
|
|
145
153
|
CGPointMake(x, y),
|
|
146
154
|
CGPointMake(x2, y2),
|
|
147
155
|
durationMs,
|
|
@@ -154,22 +162,25 @@ static id RunnerTapPointerPath(
|
|
|
154
162
|
}
|
|
155
163
|
|
|
156
164
|
+ (NSString * _Nullable)synthesizeTapWithApplication:(id)application
|
|
165
|
+
resolvedWindow:(id _Nullable)resolvedWindow
|
|
157
166
|
x:(double)x
|
|
158
167
|
y:(double)y {
|
|
159
168
|
@try {
|
|
160
|
-
return RunnerTrySynthesizeTap(application, CGPointMake(x, y));
|
|
169
|
+
return RunnerTrySynthesizeTap(application, resolvedWindow, CGPointMake(x, y));
|
|
161
170
|
} @catch (NSException *exception) {
|
|
162
171
|
return RunnerFormatXCTestException(exception, @"private XCTest event synthesis failed");
|
|
163
172
|
}
|
|
164
173
|
}
|
|
165
174
|
|
|
166
175
|
+ (NSString * _Nullable)synthesizeGestureWithApplication:(id)application
|
|
176
|
+
resolvedWindow:(id _Nullable)resolvedWindow
|
|
167
177
|
pointerSamples:(NSArray<NSArray<NSDictionary<NSString *, NSNumber *> *> *> *)pointerSamples {
|
|
168
178
|
@try {
|
|
169
179
|
RunnerGestureEventBridge bridge;
|
|
170
180
|
id record = nil;
|
|
171
181
|
NSString *error = RunnerCreateEventRecord(
|
|
172
182
|
application,
|
|
183
|
+
resolvedWindow,
|
|
173
184
|
@"agent-device-gesture-plan",
|
|
174
185
|
&bridge,
|
|
175
186
|
&record
|
|
@@ -224,6 +235,7 @@ static id RunnerTapPointerPath(
|
|
|
224
235
|
|
|
225
236
|
static NSString * _Nullable RunnerTrySynthesizeDrag(
|
|
226
237
|
id application,
|
|
238
|
+
id _Nullable resolvedWindow,
|
|
227
239
|
CGPoint start,
|
|
228
240
|
CGPoint end,
|
|
229
241
|
double durationMs,
|
|
@@ -232,7 +244,7 @@ static NSString * _Nullable RunnerTrySynthesizeDrag(
|
|
|
232
244
|
) {
|
|
233
245
|
RunnerGestureEventBridge bridge;
|
|
234
246
|
id record = nil;
|
|
235
|
-
NSString *error = RunnerCreateEventRecord(application, recordName, &bridge, &record);
|
|
247
|
+
NSString *error = RunnerCreateEventRecord(application, resolvedWindow, recordName, &bridge, &record);
|
|
236
248
|
if (error != nil) return error;
|
|
237
249
|
|
|
238
250
|
id path = pathFactory(&bridge, start, end, durationMs);
|
|
@@ -244,11 +256,12 @@ static NSString * _Nullable RunnerTrySynthesizeDrag(
|
|
|
244
256
|
return RunnerSynthesizeEventRecord(&bridge, record);
|
|
245
257
|
}
|
|
246
258
|
|
|
247
|
-
static NSString * _Nullable RunnerTrySynthesizeTap(id application, CGPoint point) {
|
|
259
|
+
static NSString * _Nullable RunnerTrySynthesizeTap(id application, id _Nullable resolvedWindow, CGPoint point) {
|
|
248
260
|
RunnerGestureEventBridge bridge;
|
|
249
261
|
id record = nil;
|
|
250
262
|
NSString *error = RunnerCreateEventRecord(
|
|
251
263
|
application,
|
|
264
|
+
resolvedWindow,
|
|
252
265
|
@"agent-device-tap",
|
|
253
266
|
&bridge,
|
|
254
267
|
&record
|
|
@@ -271,14 +284,14 @@ static NSString * _Nullable RunnerResolveGestureEventBridge(
|
|
|
271
284
|
NSString *missing = RunnerResolveXCTestEventBridge(application, RunnerGestureSynthesisSurface, &core);
|
|
272
285
|
if (missing != nil) return missing;
|
|
273
286
|
|
|
274
|
-
SEL initRecordSelector = NSSelectorFromString(@"initWithName:interfaceOrientation:");
|
|
287
|
+
SEL initRecordSelector = NSSelectorFromString(@"initWithName:displayID:interfaceOrientation:");
|
|
275
288
|
SEL interfaceOrientationSelector = NSSelectorFromString(@"interfaceOrientation");
|
|
276
289
|
SEL initPathSelector = NSSelectorFromString(@"initForTouchAtPoint:offset:");
|
|
277
290
|
SEL moveSelector = NSSelectorFromString(@"moveToPoint:atOffset:");
|
|
278
291
|
SEL liftSelector = NSSelectorFromString(@"liftUpAtOffset:");
|
|
279
292
|
|
|
280
293
|
missing = RunnerRequireSelector(
|
|
281
|
-
core.recordClass, initRecordSelector, @"initWithName:interfaceOrientation:", RunnerGestureSynthesisSurface
|
|
294
|
+
core.recordClass, initRecordSelector, @"initWithName:displayID:interfaceOrientation:", RunnerGestureSynthesisSurface
|
|
282
295
|
);
|
|
283
296
|
if (missing != nil) return missing;
|
|
284
297
|
missing = RunnerRequireSelector(
|
|
@@ -311,6 +324,7 @@ static NSString * _Nullable RunnerResolveGestureEventBridge(
|
|
|
311
324
|
|
|
312
325
|
static NSString * _Nullable RunnerCreateEventRecord(
|
|
313
326
|
id application,
|
|
327
|
+
id _Nullable resolvedWindow,
|
|
314
328
|
NSString *recordName,
|
|
315
329
|
RunnerGestureEventBridge *bridge,
|
|
316
330
|
id *record
|
|
@@ -318,6 +332,12 @@ static NSString * _Nullable RunnerCreateEventRecord(
|
|
|
318
332
|
NSString *missing = RunnerResolveGestureEventBridge(application, bridge);
|
|
319
333
|
if (missing != nil) return missing;
|
|
320
334
|
|
|
335
|
+
NSUInteger displayID = 0;
|
|
336
|
+
if (resolvedWindow == nil) {
|
|
337
|
+
return @"private XCTest event synthesis unavailable: no resolved application window";
|
|
338
|
+
}
|
|
339
|
+
NSString *displayError = RunnerResolveWindowDisplayID(resolvedWindow, &displayID);
|
|
340
|
+
if (displayError != nil) return displayError;
|
|
321
341
|
NSInteger interfaceOrientation =
|
|
322
342
|
((RunnerMsgSendInteger)objc_msgSend)(application, bridge->interfaceOrientationSelector);
|
|
323
343
|
NSInteger targetProcessID =
|
|
@@ -330,11 +350,18 @@ static NSString * _Nullable RunnerCreateEventRecord(
|
|
|
330
350
|
[bridge->core.recordClass alloc],
|
|
331
351
|
bridge->initRecordSelector,
|
|
332
352
|
recordName,
|
|
353
|
+
displayID,
|
|
333
354
|
interfaceOrientation
|
|
334
355
|
);
|
|
335
356
|
if (eventRecord == nil) {
|
|
336
357
|
return @"private XCTest event synthesis failed: could not create event record";
|
|
337
358
|
}
|
|
359
|
+
NSLog(
|
|
360
|
+
@"AGENT_DEVICE_RUNNER_SYNTHESIZED_RECORD name=%@ displayID=%lu interfaceOrientation=%ld",
|
|
361
|
+
recordName,
|
|
362
|
+
(unsigned long)displayID,
|
|
363
|
+
(long)interfaceOrientation
|
|
364
|
+
);
|
|
338
365
|
((RunnerMsgSendSetInteger)objc_msgSend)(
|
|
339
366
|
eventRecord,
|
|
340
367
|
bridge->core.setTargetProcessIDSelector,
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift
CHANGED
|
@@ -68,6 +68,14 @@ extension RunnerTests {
|
|
|
68
68
|
guard waitUntilAlertButtonHittable(button, deadline: deadline) else {
|
|
69
69
|
return alertVerificationResponse(.timedOut, action: action, activated: false)
|
|
70
70
|
}
|
|
71
|
+
let buttonFrame = button.frame
|
|
72
|
+
NSLog(
|
|
73
|
+
"AGENT_DEVICE_RUNNER_ALERT_ACTIVATION action=%@ label=%@ frame=(%.1f,%.1f,%.1f,%.1f) point=(%.1f,%.1f)",
|
|
74
|
+
action,
|
|
75
|
+
button.label,
|
|
76
|
+
buttonFrame.origin.x, buttonFrame.origin.y, buttonFrame.size.width, buttonFrame.size.height,
|
|
77
|
+
buttonFrame.midX, buttonFrame.midY
|
|
78
|
+
)
|
|
71
79
|
|
|
72
80
|
|
|
73
81
|
|
|
@@ -24,6 +24,7 @@ import AgentDeviceSnapshotPresentation
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
extension RunnerTests {
|
|
@@ -1465,7 +1466,7 @@ extension RunnerTests {
|
|
|
1465
1466
|
fps: command.fps.map { Int32($0) }
|
|
1466
1467
|
)
|
|
1467
1468
|
try recorder.start { [weak self] in
|
|
1468
|
-
return self?.captureRunnerFrame()
|
|
1469
|
+
return self?.captureRunnerFrame(app: activeApp)
|
|
1469
1470
|
}
|
|
1470
1471
|
activeRecording = recorder
|
|
1471
1472
|
return Response(ok: true, data: DataPayload(message: "recording started"))
|
|
@@ -2054,7 +2055,6 @@ extension RunnerTests {
|
|
|
2054
2055
|
case .snapshot:
|
|
2055
2056
|
return try executeSnapshotPrepared(command: command, activeApp: activeApp)
|
|
2056
2057
|
case .screenshot:
|
|
2057
|
-
let screenshot: XCUIScreenshot
|
|
2058
2058
|
#if os(macOS)
|
|
2059
2059
|
|
|
2060
2060
|
if let bundleId = command.appBundleId, !bundleId.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
@@ -2064,6 +2064,7 @@ extension RunnerTests {
|
|
|
2064
2064
|
|
|
2065
2065
|
sleepFor(0.5)
|
|
2066
2066
|
}
|
|
2067
|
+
let screenshot: XCUIScreenshot
|
|
2067
2068
|
if command.fullscreen == true {
|
|
2068
2069
|
screenshot = XCUIScreen.main.screenshot()
|
|
2069
2070
|
} else if let bundleId = command.appBundleId, !bundleId.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
@@ -2071,30 +2072,52 @@ extension RunnerTests {
|
|
|
2071
2072
|
} else {
|
|
2072
2073
|
screenshot = XCUIScreen.main.screenshot()
|
|
2073
2074
|
}
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2075
|
+
return screenshotResponse(
|
|
2076
|
+
image: screenshot.image,
|
|
2077
|
+
inlineScreenshot: command.inlineScreenshot == true
|
|
2078
|
+
)
|
|
2079
|
+
#elseif os(iOS)
|
|
2080
|
+
|
|
2081
|
+
|
|
2082
|
+
|
|
2083
|
+
|
|
2084
|
+
|
|
2085
|
+
|
|
2086
|
+
|
|
2087
|
+
|
|
2088
|
+
|
|
2089
|
+
switch captureObservedScreen(app: resolveAppWithoutActivation(command: command)) {
|
|
2090
|
+
case .failure(let failure):
|
|
2081
2091
|
return Response(
|
|
2082
|
-
ok:
|
|
2083
|
-
|
|
2092
|
+
ok: false,
|
|
2093
|
+
error: ErrorPayload(code: failure.rawValue, message: failure.message, hint: failure.hint)
|
|
2094
|
+
)
|
|
2095
|
+
case .success(let captured):
|
|
2096
|
+
|
|
2097
|
+
|
|
2098
|
+
NSLog(
|
|
2099
|
+
"AGENT_DEVICE_RUNNER_SCREEN_CAPTURE display=%lu pixels=%ldx%ld pixelsPerPoint=%g",
|
|
2100
|
+
captured.displayID,
|
|
2101
|
+
captured.pixelWidth,
|
|
2102
|
+
captured.pixelHeight,
|
|
2103
|
+
captured.pixelsPerPoint
|
|
2104
|
+
)
|
|
2105
|
+
return screenshotResponse(
|
|
2106
|
+
image: captured.image,
|
|
2107
|
+
inlineScreenshot: command.inlineScreenshot == true,
|
|
2108
|
+
metadata: ScreenshotMetadataPayload(
|
|
2109
|
+
displayID: captured.displayID,
|
|
2110
|
+
pixelWidth: captured.pixelWidth,
|
|
2111
|
+
pixelHeight: captured.pixelHeight,
|
|
2112
|
+
pixelsPerPoint: captured.pixelsPerPoint
|
|
2113
|
+
)
|
|
2084
2114
|
)
|
|
2085
2115
|
}
|
|
2086
|
-
let fileName = "screenshot-\(Int(Date().timeIntervalSince1970 * 1000)).png"
|
|
2087
|
-
let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent(fileName)
|
|
2088
|
-
do {
|
|
2089
|
-
try pngData.write(to: URL(fileURLWithPath: filePath))
|
|
2090
|
-
} catch {
|
|
2091
|
-
return Response(ok: false, error: ErrorPayload(message: "Failed to write screenshot: \(error.localizedDescription)"))
|
|
2092
|
-
}
|
|
2093
|
-
#if os(macOS)
|
|
2094
|
-
return Response(ok: true, data: DataPayload(message: filePath))
|
|
2095
2116
|
#else
|
|
2096
|
-
|
|
2097
|
-
|
|
2117
|
+
return screenshotResponse(
|
|
2118
|
+
image: XCUIScreen.main.screenshot().image,
|
|
2119
|
+
inlineScreenshot: command.inlineScreenshot == true
|
|
2120
|
+
)
|
|
2098
2121
|
#endif
|
|
2099
2122
|
case .back, .backInApp:
|
|
2100
2123
|
if tapInAppBackControl(app: activeApp) {
|