agent-device 0.19.2 → 0.20.0
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 +61 -70
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.0.apk +0 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.0.apk.sha256 +1 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.0.manifest.json +10 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.0.apk +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.0.apk.sha256 +1 -0
- package/{android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.2.manifest.json → android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.0.manifest.json} +6 -6
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +2 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +5 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +12 -10
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +154 -185
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +113 -19
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+BlockingSystemModalResolution.swift +82 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +275 -113
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +2 -2
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +150 -104
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +56 -2
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+LifecycleCacheTests.swift +73 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +44 -14
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +2 -2
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +2 -106
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +126 -11
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +51 -8
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTraversalIdentityTests.swift +24 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +1 -1
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +43 -35
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +14 -0
- package/dist/src/agent-browser-tool.js +1 -1
- package/dist/src/agent-device-client.js +3 -0
- package/dist/src/android-adb.d.ts +1 -1
- package/dist/src/android-adb.js +1 -1
- package/dist/src/android-input-method-overlays.js +1 -0
- package/dist/src/android-input-ownership.js +1 -6
- package/dist/src/android-system-chrome.js +1 -0
- package/dist/src/android.js +1 -1
- package/dist/src/app-lifecycle.js +5 -5
- package/dist/src/app-log-request-scope.js +1 -0
- package/dist/src/app-resolution.js +1 -0
- package/dist/src/apple-multitouch-support.js +1 -0
- package/dist/src/apps.js +5 -5
- package/dist/src/audio-probe.js +1 -1
- package/dist/src/boot-diagnostics.js +2 -0
- package/dist/src/cli-config.js +1 -0
- package/dist/src/cli-help.js +64 -30
- package/dist/src/cli-runner.js +1 -1
- package/dist/src/cli.js +15 -11
- package/dist/src/command-schema.js +3 -3
- package/dist/src/config.js +1 -0
- package/dist/src/context.js +1 -0
- package/dist/src/contracts.d.ts +1 -1
- package/dist/src/contracts.js +1 -1
- package/dist/src/daemon-shutdown-report.js +1 -0
- package/dist/src/debug-symbols.js +1 -1
- package/dist/src/device-input-state.js +1 -1
- package/dist/src/devices2.js +1 -1
- package/dist/src/find.js +1 -1
- package/dist/src/finders.js +1 -1
- package/dist/src/generic.js +1 -1
- package/dist/src/helper.js +1 -1
- package/dist/src/index.d.ts +412 -289
- package/dist/src/index.js +1 -1
- package/dist/src/input-actions.js +4 -3
- package/dist/src/input-actions2.js +1 -1
- package/dist/src/install-artifact.js +1 -0
- package/dist/src/install-source.d.ts +5 -1
- package/dist/src/install-source.js +1 -1
- package/dist/src/install-source2.js +1 -1
- package/dist/src/interaction-snapshot.js +1 -1
- package/dist/src/interaction.d.ts +230 -0
- package/dist/src/interaction.js +1 -1
- package/dist/src/interactor.js +2 -2
- package/dist/src/internal/bin.js +1 -1
- package/dist/src/internal/daemon.js +43 -17
- package/dist/src/internal/png-worker.d.ts +22 -0
- package/dist/src/internal/png-worker.js +1 -1
- package/dist/src/launch-console.js +1 -0
- package/dist/src/lease-context.js +1 -0
- package/dist/src/lease.js +1 -1
- package/dist/src/linux.js +1 -1
- package/dist/src/logcat.js +5 -0
- package/dist/src/metro.d.ts +2 -2
- package/dist/src/notifications.js +1 -1
- package/dist/src/png-worker-contract.js +1 -1
- package/dist/src/process-lock.js +1 -0
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-trace.js +1 -1
- package/dist/src/register-builtins.js +1 -0
- package/dist/src/registry.js +24 -24
- package/dist/src/reporting.js +8 -7
- package/dist/src/runner-client.js +1 -1
- package/dist/src/runner-disposal.js +2 -0
- package/dist/src/runner-lease.js +1 -0
- package/dist/src/runner-sequence.js +1 -1
- package/dist/src/runtime-transport.js +1 -1
- package/dist/src/runtime.js +3 -3
- package/dist/src/runtime2.js +1 -0
- package/dist/src/sdk-android-adb.d.ts +119 -33
- package/dist/src/sdk-android-adb.js +4 -4
- package/dist/src/sdk-app-inventory.js +1 -1
- package/dist/src/sdk-batch-runner.d.ts +2 -1
- package/dist/src/sdk-batch-runner.js +2 -2
- package/dist/src/sdk-batch.js +2 -2
- package/dist/src/sdk-contracts.d.ts +105 -98
- package/dist/src/sdk-contracts.js +2 -2
- package/dist/src/sdk-device.js +1 -0
- package/dist/src/sdk-finders.d.ts +3 -3
- package/dist/src/sdk-finders.js +1 -1
- package/dist/src/sdk-io.d.ts +1 -1
- package/dist/src/sdk-io.js +1 -1
- package/dist/src/sdk-remote-config.d.ts +9 -1
- package/dist/src/sdk-remote-config.js +1 -1
- package/dist/src/sdk-selectors.js +1 -1
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/selector-vocabulary.js +1 -0
- package/dist/src/selectors.d.ts +12 -3
- package/dist/src/selectors.js +1 -1
- package/dist/src/server.js +3 -3
- package/dist/src/session-target-evidence.js +1 -0
- package/dist/src/session.js +8 -10
- package/dist/src/setting-state.js +1 -1
- package/dist/src/simulator.js +1 -1
- package/dist/src/snapshot-diagnostics.js +5 -2
- package/dist/src/snapshot.js +1 -1
- package/dist/src/snapshot2.js +1 -2
- package/dist/src/tool-provider.js +2 -2
- package/dist/src/tool-provider2.js +1 -1
- package/dist/src/tv-remote.js +13 -12
- package/dist/src/url.js +1 -1
- package/dist/src/web.js +1 -1
- package/package.json +33 -20
- package/server.json +2 -2
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.2.apk +0 -0
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.2.apk.sha256 +0 -1
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.2.manifest.json +0 -10
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.2.apk +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.2.apk.sha256 +0 -1
- package/dist/src/apple-runner-options.js +0 -1
- package/dist/src/cli-output.js +0 -1
- package/dist/src/client.js +0 -3
- package/dist/src/handler-utils.js +0 -1
- package/dist/src/output-path.js +0 -1
- package/dist/src/points.js +0 -4
- package/dist/src/provider.js +0 -1
- package/dist/src/providers.js +0 -1
- package/dist/src/record-trace-recording.js +0 -23
- package/dist/src/recording-provider.js +0 -1
- package/dist/src/selectors2.js +0 -1
- package/dist/src/simctl.js +0 -1
- package/dist/src/snapshot-helper.js +0 -5
- package/dist/src/ttl-memo.js +0 -1
- package/dist/src/xml.js +0 -2
- /package/{macos-helper → apple/macos-helper}/Package.swift +0 -0
- /package/{macos-helper → apple/macos-helper}/Sources/AgentDeviceMacOSHelper/AudioProbe.swift +0 -0
- /package/{macos-helper → apple/macos-helper}/Sources/AgentDeviceMacOSHelper/SnapshotTraversal.swift +0 -0
- /package/{macos-helper → apple/macos-helper}/Sources/AgentDeviceMacOSHelper/main.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcshareddata/xcschemes/AgentDeviceRunner.xcscheme +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests.entitlements +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.h +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.m +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Exceptions.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/RecordingScripts/recording-resize.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/RecordingScripts/recording-trim.swift +0 -0
- /package/{src/platforms/linux → linux}/atspi-dump.py +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#import "RunnerSynthesizedGesture.h"
|
|
2
2
|
|
|
3
3
|
#import <CoreGraphics/CoreGraphics.h>
|
|
4
|
-
#import <math.h>
|
|
5
4
|
#import <objc/message.h>
|
|
6
5
|
|
|
7
6
|
typedef NSInteger (*RunnerMsgSendInteger)(id, SEL);
|
|
@@ -27,6 +26,13 @@ typedef struct {
|
|
|
27
26
|
SEL liftSelector;
|
|
28
27
|
} RunnerXCTestEventBridge;
|
|
29
28
|
|
|
29
|
+
typedef id (*RunnerDragPointerPathFactory)(
|
|
30
|
+
const RunnerXCTestEventBridge *,
|
|
31
|
+
CGPoint,
|
|
32
|
+
CGPoint,
|
|
33
|
+
double
|
|
34
|
+
);
|
|
35
|
+
|
|
30
36
|
static NSString * _Nullable RunnerResolveXCTestEventBridge(
|
|
31
37
|
id application,
|
|
32
38
|
RunnerXCTestEventBridge *bridge
|
|
@@ -34,64 +40,51 @@ static NSString * _Nullable RunnerResolveXCTestEventBridge(
|
|
|
34
40
|
static NSString * _Nullable RunnerRequireClass(Class cls, NSString *className);
|
|
35
41
|
static NSString * _Nullable RunnerRequireSelector(Class cls, SEL selector, NSString *selectorName);
|
|
36
42
|
static NSString * _Nullable RunnerRequireApplicationSelector(id application, SEL selector, NSString *selectorName);
|
|
37
|
-
static id
|
|
43
|
+
static id RunnerSwipePointerPath(
|
|
38
44
|
const RunnerXCTestEventBridge *bridge,
|
|
39
45
|
CGPoint start,
|
|
40
|
-
|
|
41
|
-
double
|
|
42
|
-
double dx,
|
|
43
|
-
double dy,
|
|
44
|
-
double scale,
|
|
45
|
-
double degrees,
|
|
46
|
-
double radius,
|
|
47
|
-
double durationMs,
|
|
48
|
-
double side
|
|
46
|
+
CGPoint end,
|
|
47
|
+
double durationMs
|
|
49
48
|
);
|
|
50
|
-
static id
|
|
49
|
+
static id RunnerContinuousDragPointerPath(
|
|
51
50
|
const RunnerXCTestEventBridge *bridge,
|
|
52
51
|
CGPoint start,
|
|
53
52
|
CGPoint end,
|
|
54
53
|
double durationMs
|
|
55
54
|
);
|
|
55
|
+
static NSString * _Nullable RunnerTrySynthesizeDrag(
|
|
56
|
+
id application,
|
|
57
|
+
CGPoint start,
|
|
58
|
+
CGPoint end,
|
|
59
|
+
double durationMs,
|
|
60
|
+
NSString *recordName,
|
|
61
|
+
RunnerDragPointerPathFactory pathFactory
|
|
62
|
+
);
|
|
63
|
+
// XCTest's proven swipe profile reaches the endpoint in 100 ms, then holds for the planned
|
|
64
|
+
// fling duration. Fast movement is what lets UIKit distinguish a fling from a timed pan.
|
|
65
|
+
static const NSTimeInterval RunnerSwipeMovementDurationSeconds = 0.1;
|
|
56
66
|
static id RunnerTapPointerPath(
|
|
57
67
|
const RunnerXCTestEventBridge *bridge,
|
|
58
68
|
CGPoint point
|
|
59
69
|
);
|
|
60
|
-
static CGPoint RunnerPointerPointAt(
|
|
61
|
-
double x,
|
|
62
|
-
double y,
|
|
63
|
-
double dx,
|
|
64
|
-
double dy,
|
|
65
|
-
double scale,
|
|
66
|
-
double degrees,
|
|
67
|
-
double baseRadius,
|
|
68
|
-
double t,
|
|
69
|
-
double side
|
|
70
|
-
);
|
|
71
|
-
static CGPoint RunnerInterpolatedPoint(CGPoint start, CGPoint end, double t);
|
|
72
|
-
static double RunnerSmoothStep(double t);
|
|
73
70
|
|
|
74
71
|
@implementation RunnerSynthesizedGesture
|
|
75
72
|
|
|
76
|
-
+ (NSString * _Nullable)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
degrees:(double)degrees
|
|
83
|
-
radius:(double)radius
|
|
84
|
-
durationMs:(double)durationMs {
|
|
73
|
+
+ (NSString * _Nullable)synthesizeSwipeWithApplication:(id)application
|
|
74
|
+
x:(double)x
|
|
75
|
+
y:(double)y
|
|
76
|
+
x2:(double)x2
|
|
77
|
+
y2:(double)y2
|
|
78
|
+
durationMs:(double)durationMs {
|
|
85
79
|
@try {
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
durationMs:durationMs];
|
|
80
|
+
return RunnerTrySynthesizeDrag(
|
|
81
|
+
application,
|
|
82
|
+
CGPointMake(x, y),
|
|
83
|
+
CGPointMake(x2, y2),
|
|
84
|
+
durationMs,
|
|
85
|
+
@"agent-device-swipe",
|
|
86
|
+
RunnerSwipePointerPath
|
|
87
|
+
);
|
|
95
88
|
} @catch (NSException *exception) {
|
|
96
89
|
NSString *name = exception.name ?: @"NSException";
|
|
97
90
|
NSString *reason = exception.reason ?: @"private XCTest event synthesis failed";
|
|
@@ -99,19 +92,21 @@ static double RunnerSmoothStep(double t);
|
|
|
99
92
|
}
|
|
100
93
|
}
|
|
101
94
|
|
|
102
|
-
+ (NSString * _Nullable)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
95
|
+
+ (NSString * _Nullable)synthesizeContinuousDragWithApplication:(id)application
|
|
96
|
+
x:(double)x
|
|
97
|
+
y:(double)y
|
|
98
|
+
x2:(double)x2
|
|
99
|
+
y2:(double)y2
|
|
100
|
+
durationMs:(double)durationMs {
|
|
108
101
|
@try {
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
102
|
+
return RunnerTrySynthesizeDrag(
|
|
103
|
+
application,
|
|
104
|
+
CGPointMake(x, y),
|
|
105
|
+
CGPointMake(x2, y2),
|
|
106
|
+
durationMs,
|
|
107
|
+
@"agent-device-continuous-drag",
|
|
108
|
+
RunnerContinuousDragPointerPath
|
|
109
|
+
);
|
|
115
110
|
} @catch (NSException *exception) {
|
|
116
111
|
NSString *name = exception.name ?: @"NSException";
|
|
117
112
|
NSString *reason = exception.reason ?: @"private XCTest event synthesis failed";
|
|
@@ -131,84 +126,94 @@ static double RunnerSmoothStep(double t);
|
|
|
131
126
|
}
|
|
132
127
|
}
|
|
133
128
|
|
|
134
|
-
+ (
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
degrees:(double)degrees
|
|
149
|
-
radius:(double)radius
|
|
150
|
-
durationMs:(double)durationMs {
|
|
151
|
-
RunnerXCTestEventBridge bridge;
|
|
152
|
-
NSString *missing = RunnerResolveXCTestEventBridge(application, &bridge);
|
|
153
|
-
if (missing != nil) {
|
|
154
|
-
return missing;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
NSInteger interfaceOrientation =
|
|
158
|
-
((RunnerMsgSendInteger)objc_msgSend)(application, bridge.interfaceOrientationSelector);
|
|
159
|
-
NSInteger targetProcessID = ((RunnerMsgSendInteger)objc_msgSend)(application, bridge.processIDSelector);
|
|
160
|
-
if (targetProcessID <= 0) {
|
|
161
|
-
return @"private XCTest event synthesis unavailable: could not resolve target process ID";
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
id record = ((RunnerMsgSendInitRecord)objc_msgSend)(
|
|
165
|
-
[bridge.recordClass alloc],
|
|
166
|
-
bridge.initRecordSelector,
|
|
167
|
-
@"agent-device-transform",
|
|
168
|
-
interfaceOrientation
|
|
169
|
-
);
|
|
170
|
-
if (record == nil) {
|
|
171
|
-
return @"private XCTest event synthesis failed: could not create event record";
|
|
172
|
-
}
|
|
173
|
-
((RunnerMsgSendSetInteger)objc_msgSend)(record, bridge.setTargetProcessIDSelector, targetProcessID);
|
|
129
|
+
+ (NSString * _Nullable)synthesizeGestureWithApplication:(id)application
|
|
130
|
+
pointerSamples:(NSArray<NSArray<NSDictionary<NSString *, NSNumber *> *> *> *)pointerSamples {
|
|
131
|
+
@try {
|
|
132
|
+
RunnerXCTestEventBridge bridge;
|
|
133
|
+
NSString *missing = RunnerResolveXCTestEventBridge(application, &bridge);
|
|
134
|
+
if (missing != nil) return missing;
|
|
135
|
+
|
|
136
|
+
NSInteger interfaceOrientation =
|
|
137
|
+
((RunnerMsgSendInteger)objc_msgSend)(application, bridge.interfaceOrientationSelector);
|
|
138
|
+
NSInteger targetProcessID =
|
|
139
|
+
((RunnerMsgSendInteger)objc_msgSend)(application, bridge.processIDSelector);
|
|
140
|
+
if (targetProcessID <= 0) {
|
|
141
|
+
return @"private XCTest event synthesis unavailable: could not resolve target process ID";
|
|
142
|
+
}
|
|
174
143
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
RunnerPointerPointAt(x, y, dx, dy, scale, degrees, radius, 0.0, side),
|
|
181
|
-
x,
|
|
182
|
-
y,
|
|
183
|
-
dx,
|
|
184
|
-
dy,
|
|
185
|
-
scale,
|
|
186
|
-
degrees,
|
|
187
|
-
radius,
|
|
188
|
-
durationMs,
|
|
189
|
-
side
|
|
144
|
+
id record = ((RunnerMsgSendInitRecord)objc_msgSend)(
|
|
145
|
+
[bridge.recordClass alloc],
|
|
146
|
+
bridge.initRecordSelector,
|
|
147
|
+
@"agent-device-gesture-plan",
|
|
148
|
+
interfaceOrientation
|
|
190
149
|
);
|
|
191
|
-
if (
|
|
192
|
-
return @"private XCTest event synthesis failed: could not create
|
|
150
|
+
if (record == nil) {
|
|
151
|
+
return @"private XCTest event synthesis failed: could not create event record";
|
|
152
|
+
}
|
|
153
|
+
((RunnerMsgSendSetInteger)objc_msgSend)(record, bridge.setTargetProcessIDSelector, targetProcessID);
|
|
154
|
+
|
|
155
|
+
for (NSArray<NSDictionary<NSString *, NSNumber *> *> *samples in pointerSamples) {
|
|
156
|
+
NSDictionary<NSString *, NSNumber *> *first = samples.firstObject;
|
|
157
|
+
if (first == nil) return @"private XCTest event synthesis failed: empty pointer path";
|
|
158
|
+
CGPoint start = CGPointMake(first[@"x"].doubleValue, first[@"y"].doubleValue);
|
|
159
|
+
id path = ((RunnerMsgSendInitPath)objc_msgSend)(
|
|
160
|
+
[bridge.pathClass alloc],
|
|
161
|
+
bridge.initPathSelector,
|
|
162
|
+
start,
|
|
163
|
+
first[@"offsetMs"].doubleValue / 1000.0
|
|
164
|
+
);
|
|
165
|
+
if (path == nil) {
|
|
166
|
+
return @"private XCTest event synthesis failed: could not create pointer path";
|
|
167
|
+
}
|
|
168
|
+
for (NSUInteger index = 1; index < samples.count; index += 1) {
|
|
169
|
+
NSDictionary<NSString *, NSNumber *> *sample = samples[index];
|
|
170
|
+
CGPoint point = CGPointMake(sample[@"x"].doubleValue, sample[@"y"].doubleValue);
|
|
171
|
+
((RunnerMsgSendPathMove)objc_msgSend)(
|
|
172
|
+
path,
|
|
173
|
+
bridge.moveSelector,
|
|
174
|
+
point,
|
|
175
|
+
sample[@"offsetMs"].doubleValue / 1000.0
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
NSDictionary<NSString *, NSNumber *> *last = samples.lastObject;
|
|
179
|
+
((RunnerMsgSendPathOffset)objc_msgSend)(
|
|
180
|
+
path,
|
|
181
|
+
bridge.liftSelector,
|
|
182
|
+
last[@"offsetMs"].doubleValue / 1000.0
|
|
183
|
+
);
|
|
184
|
+
((RunnerMsgSendAddPath)objc_msgSend)(record, bridge.addPathSelector, path);
|
|
193
185
|
}
|
|
194
|
-
|
|
186
|
+
|
|
187
|
+
NSError *error = nil;
|
|
188
|
+
BOOL ok = ((RunnerMsgSendSynthesize)objc_msgSend)(record, bridge.synthesizeSelector, &error);
|
|
189
|
+
if (!ok) {
|
|
190
|
+
NSString *detail = error.localizedDescription ?: @"synthesizeWithError returned false";
|
|
191
|
+
return [NSString stringWithFormat:@"private XCTest event synthesis failed: %@", detail];
|
|
192
|
+
}
|
|
193
|
+
return nil;
|
|
194
|
+
} @catch (NSException *exception) {
|
|
195
|
+
NSString *name = exception.name ?: @"NSException";
|
|
196
|
+
NSString *reason = exception.reason ?: @"private XCTest event synthesis failed";
|
|
197
|
+
return [NSString stringWithFormat:@"%@: %@", name, reason];
|
|
195
198
|
}
|
|
199
|
+
}
|
|
196
200
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (!
|
|
200
|
-
|
|
201
|
-
return [NSString stringWithFormat:@"private XCTest event synthesis failed: %@", detail];
|
|
201
|
+
+ (NSInteger)interfaceOrientationForApplication:(id)application {
|
|
202
|
+
SEL selector = NSSelectorFromString(@"interfaceOrientation");
|
|
203
|
+
if (![application respondsToSelector:selector]) {
|
|
204
|
+
return 0; // UIInterfaceOrientationUnknown
|
|
202
205
|
}
|
|
203
|
-
return
|
|
206
|
+
return ((RunnerMsgSendInteger)objc_msgSend)(application, selector);
|
|
204
207
|
}
|
|
205
208
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
209
|
+
static NSString * _Nullable RunnerTrySynthesizeDrag(
|
|
210
|
+
id application,
|
|
211
|
+
CGPoint start,
|
|
212
|
+
CGPoint end,
|
|
213
|
+
double durationMs,
|
|
214
|
+
NSString *recordName,
|
|
215
|
+
RunnerDragPointerPathFactory pathFactory
|
|
216
|
+
) {
|
|
212
217
|
RunnerXCTestEventBridge bridge;
|
|
213
218
|
NSString *missing = RunnerResolveXCTestEventBridge(application, &bridge);
|
|
214
219
|
if (missing != nil) {
|
|
@@ -225,7 +230,7 @@ static double RunnerSmoothStep(double t);
|
|
|
225
230
|
id record = ((RunnerMsgSendInitRecord)objc_msgSend)(
|
|
226
231
|
[bridge.recordClass alloc],
|
|
227
232
|
bridge.initRecordSelector,
|
|
228
|
-
|
|
233
|
+
recordName,
|
|
229
234
|
interfaceOrientation
|
|
230
235
|
);
|
|
231
236
|
if (record == nil) {
|
|
@@ -233,7 +238,7 @@ static double RunnerSmoothStep(double t);
|
|
|
233
238
|
}
|
|
234
239
|
((RunnerMsgSendSetInteger)objc_msgSend)(record, bridge.setTargetProcessIDSelector, targetProcessID);
|
|
235
240
|
|
|
236
|
-
id path =
|
|
241
|
+
id path = pathFactory(&bridge, start, end, durationMs);
|
|
237
242
|
if (path == nil) {
|
|
238
243
|
return @"private XCTest event synthesis failed: could not create pointer path";
|
|
239
244
|
}
|
|
@@ -377,18 +382,12 @@ static NSString * _Nullable RunnerRequireApplicationSelector(
|
|
|
377
382
|
return nil;
|
|
378
383
|
}
|
|
379
384
|
|
|
380
|
-
|
|
385
|
+
|
|
386
|
+
static id RunnerSwipePointerPath(
|
|
381
387
|
const RunnerXCTestEventBridge *bridge,
|
|
382
388
|
CGPoint start,
|
|
383
|
-
|
|
384
|
-
double
|
|
385
|
-
double dx,
|
|
386
|
-
double dy,
|
|
387
|
-
double scale,
|
|
388
|
-
double degrees,
|
|
389
|
-
double radius,
|
|
390
|
-
double durationMs,
|
|
391
|
-
double side
|
|
389
|
+
CGPoint end,
|
|
390
|
+
double durationMs
|
|
392
391
|
) {
|
|
393
392
|
id path =
|
|
394
393
|
((RunnerMsgSendInitPath)objc_msgSend)([bridge->pathClass alloc], bridge->initPathSelector, start, 0.0);
|
|
@@ -396,20 +395,22 @@ static id RunnerPointerPath(
|
|
|
396
395
|
return nil;
|
|
397
396
|
}
|
|
398
397
|
|
|
399
|
-
int frameCount = MAX(3, (int)(durationMs / 16.0));
|
|
400
398
|
NSTimeInterval durationSeconds = durationMs / 1000.0;
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
399
|
+
((RunnerMsgSendPathMove)objc_msgSend)(
|
|
400
|
+
path,
|
|
401
|
+
bridge->moveSelector,
|
|
402
|
+
end,
|
|
403
|
+
RunnerSwipeMovementDurationSeconds
|
|
404
|
+
);
|
|
405
|
+
((RunnerMsgSendPathOffset)objc_msgSend)(
|
|
406
|
+
path,
|
|
407
|
+
bridge->liftSelector,
|
|
408
|
+
RunnerSwipeMovementDurationSeconds + durationSeconds
|
|
409
|
+
);
|
|
409
410
|
return path;
|
|
410
411
|
}
|
|
411
412
|
|
|
412
|
-
static id
|
|
413
|
+
static id RunnerContinuousDragPointerPath(
|
|
413
414
|
const RunnerXCTestEventBridge *bridge,
|
|
414
415
|
CGPoint start,
|
|
415
416
|
CGPoint end,
|
|
@@ -425,9 +426,12 @@ static id RunnerSwipePointerPath(
|
|
|
425
426
|
NSTimeInterval durationSeconds = durationMs / 1000.0;
|
|
426
427
|
for (int index = 1; index <= frameCount; index += 1) {
|
|
427
428
|
double t = (double)index / (double)frameCount;
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
429
|
+
double easedT = t * t * (3.0 - 2.0 * t);
|
|
430
|
+
CGPoint point = CGPointMake(
|
|
431
|
+
start.x + (end.x - start.x) * easedT,
|
|
432
|
+
start.y + (end.y - start.y) * easedT
|
|
433
|
+
);
|
|
434
|
+
((RunnerMsgSendPathMove)objc_msgSend)(path, bridge->moveSelector, point, durationSeconds * t);
|
|
431
435
|
}
|
|
432
436
|
|
|
433
437
|
((RunnerMsgSendPathOffset)objc_msgSend)(path, bridge->liftSelector, durationSeconds);
|
|
@@ -447,39 +451,4 @@ static id RunnerTapPointerPath(
|
|
|
447
451
|
return path;
|
|
448
452
|
}
|
|
449
453
|
|
|
450
|
-
static CGPoint RunnerPointerPointAt(
|
|
451
|
-
double x,
|
|
452
|
-
double y,
|
|
453
|
-
double dx,
|
|
454
|
-
double dy,
|
|
455
|
-
double scale,
|
|
456
|
-
double degrees,
|
|
457
|
-
double baseRadius,
|
|
458
|
-
double t,
|
|
459
|
-
double side
|
|
460
|
-
) {
|
|
461
|
-
double centerX = x + dx * t;
|
|
462
|
-
double centerY = y + dy * t;
|
|
463
|
-
double startRadius = baseRadius / MAX(scale, 1.0);
|
|
464
|
-
double endRadius = baseRadius;
|
|
465
|
-
if (scale < 1.0) {
|
|
466
|
-
startRadius = baseRadius;
|
|
467
|
-
endRadius = baseRadius * scale;
|
|
468
|
-
}
|
|
469
|
-
double radius = startRadius + (endRadius - startRadius) * t;
|
|
470
|
-
double angle = (-M_PI_2) + (degrees * M_PI / 180.0) * t;
|
|
471
|
-
return CGPointMake(centerX + cos(angle) * radius * side, centerY + sin(angle) * radius * side);
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
static CGPoint RunnerInterpolatedPoint(CGPoint start, CGPoint end, double t) {
|
|
475
|
-
return CGPointMake(
|
|
476
|
-
start.x + (end.x - start.x) * t,
|
|
477
|
-
start.y + (end.y - start.y) * t
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
static double RunnerSmoothStep(double t) {
|
|
482
|
-
return t * t * (3.0 - 2.0 * t);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
454
|
@end
|
|
@@ -1,48 +1,89 @@
|
|
|
1
1
|
import XCTest
|
|
2
2
|
|
|
3
3
|
extension RunnerTests {
|
|
4
|
+
enum RunnerAlertSource {
|
|
5
|
+
case blockingSystemModal
|
|
6
|
+
case appAlert
|
|
7
|
+
case dismissPopup
|
|
8
|
+
}
|
|
9
|
+
|
|
4
10
|
struct RunnerAlert {
|
|
5
11
|
let root: XCUIElement
|
|
6
12
|
let ownerApp: XCUIApplication
|
|
7
13
|
let buttons: [XCUIElement]
|
|
14
|
+
let source: RunnerAlertSource
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static let defaultAlertCommandTimeout: TimeInterval = 10
|
|
18
|
+
|
|
19
|
+
static func alertCommandTimeout(timeoutMs: Double?) -> TimeInterval {
|
|
20
|
+
guard let timeoutMs, timeoutMs.isFinite else { return defaultAlertCommandTimeout }
|
|
21
|
+
return max(0.001, timeoutMs / 1000)
|
|
8
22
|
}
|
|
9
23
|
|
|
10
|
-
func resolveAlert(app activeApp: XCUIApplication) -> RunnerAlert? {
|
|
24
|
+
func resolveAlert(app activeApp: XCUIApplication, deadline: Date) -> RunnerAlert? {
|
|
11
25
|
#if !os(macOS)
|
|
12
|
-
|
|
13
|
-
|
|
26
|
+
switch resolveBlockingSystemModal(deadline: deadline) {
|
|
27
|
+
case .resolved(let modal):
|
|
28
|
+
return runnerAlert(modal)
|
|
29
|
+
case .unresolved:
|
|
30
|
+
return nil
|
|
31
|
+
case .absent:
|
|
32
|
+
break
|
|
14
33
|
}
|
|
15
34
|
#endif
|
|
16
|
-
|
|
17
|
-
|
|
35
|
+
// Guard the query: when a remote-hosted modal (e.g. the AccessorySetupKit
|
|
36
|
+
// picker) was just dismissed, the dismissal re-check re-enters here with the
|
|
37
|
+
// now-gone host as `activeApp`, and its `alerts` query raises
|
|
38
|
+
// kAXErrorServerNotFound. safeElementsQuery absorbs it and reports no alert.
|
|
39
|
+
if let alert = firstExistingElement(in: safeElementsQuery { activeApp.alerts.allElementsBoundByIndex }) {
|
|
40
|
+
return runnerAlert(root: alert, ownerApp: activeApp, source: .appAlert)
|
|
18
41
|
}
|
|
19
42
|
if let popup = firstDismissPopupWindow(in: activeApp) {
|
|
20
|
-
return runnerAlert(root: popup, ownerApp: activeApp)
|
|
43
|
+
return runnerAlert(root: popup, ownerApp: activeApp, source: .dismissPopup)
|
|
21
44
|
}
|
|
22
45
|
return nil
|
|
23
46
|
}
|
|
24
47
|
|
|
25
|
-
func handleAlert(_ alert: RunnerAlert, action: String) -> Response {
|
|
48
|
+
func handleAlert(_ alert: RunnerAlert, action: String, deadline: Date) -> Response {
|
|
26
49
|
if action == "accept" || action == "dismiss" {
|
|
27
50
|
guard let button = chooseAlertButton(alert.buttons, action: action) else {
|
|
28
51
|
return Response(ok: false, error: ErrorPayload(message: "alert \(action) button not found"))
|
|
29
52
|
}
|
|
53
|
+
let previousTitle = preferredAlertTitle(alert.root, buttons: alert.buttons)
|
|
54
|
+
let actionButtonLabel = button.label.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
55
|
+
let actionButtonFrame = button.frame
|
|
30
56
|
let outcome = activateElement(app: alert.ownerApp, element: button, action: "alert \(action)")
|
|
31
57
|
if let response = unsupportedResponse(for: outcome) {
|
|
32
58
|
return response
|
|
33
59
|
}
|
|
34
60
|
sleepFor(0.2)
|
|
35
|
-
if alertStillVisible(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
61
|
+
if alertStillVisible(
|
|
62
|
+
in: alert.ownerApp,
|
|
63
|
+
source: alert.source,
|
|
64
|
+
previousTitle: previousTitle,
|
|
65
|
+
actionButtonLabel: actionButtonLabel,
|
|
66
|
+
deadline: deadline
|
|
67
|
+
) {
|
|
68
|
+
if !actionButtonFrame.isNull && !actionButtonFrame.isEmpty {
|
|
69
|
+
let coordinateOutcome = tapAt(
|
|
70
|
+
app: alert.ownerApp,
|
|
71
|
+
x: actionButtonFrame.midX,
|
|
72
|
+
y: actionButtonFrame.midY
|
|
73
|
+
)
|
|
39
74
|
if let response = unsupportedResponse(for: coordinateOutcome) {
|
|
40
75
|
return response
|
|
41
76
|
}
|
|
42
77
|
sleepFor(0.2)
|
|
43
78
|
}
|
|
44
79
|
}
|
|
45
|
-
if alertStillVisible(
|
|
80
|
+
if alertStillVisible(
|
|
81
|
+
in: alert.ownerApp,
|
|
82
|
+
source: alert.source,
|
|
83
|
+
previousTitle: previousTitle,
|
|
84
|
+
actionButtonLabel: actionButtonLabel,
|
|
85
|
+
deadline: deadline
|
|
86
|
+
) {
|
|
46
87
|
return Response(
|
|
47
88
|
ok: false,
|
|
48
89
|
error: ErrorPayload(
|
|
@@ -64,26 +105,79 @@ extension RunnerTests {
|
|
|
64
105
|
)
|
|
65
106
|
}
|
|
66
107
|
|
|
67
|
-
private func runnerAlert(
|
|
108
|
+
private func runnerAlert(_ modal: ResolvedBlockingSystemModal) -> RunnerAlert? {
|
|
109
|
+
let buttons = modal.actions.filter { isEnabledElement($0) }
|
|
110
|
+
guard !buttons.isEmpty else {
|
|
111
|
+
return nil
|
|
112
|
+
}
|
|
113
|
+
return RunnerAlert(
|
|
114
|
+
root: modal.root,
|
|
115
|
+
ownerApp: modal.ownerApp,
|
|
116
|
+
buttons: buttons,
|
|
117
|
+
source: .blockingSystemModal
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private func runnerAlert(
|
|
122
|
+
root: XCUIElement,
|
|
123
|
+
ownerApp: XCUIApplication,
|
|
124
|
+
source: RunnerAlertSource
|
|
125
|
+
) -> RunnerAlert? {
|
|
68
126
|
let buttons = actionableElements(in: root).filter { isEnabledElement($0) }
|
|
69
127
|
guard !buttons.isEmpty else {
|
|
70
128
|
return nil
|
|
71
129
|
}
|
|
72
|
-
return RunnerAlert(root: root, ownerApp: ownerApp, buttons: buttons)
|
|
130
|
+
return RunnerAlert(root: root, ownerApp: ownerApp, buttons: buttons, source: source)
|
|
73
131
|
}
|
|
74
132
|
|
|
75
|
-
private func alertStillVisible(
|
|
76
|
-
|
|
133
|
+
private func alertStillVisible(
|
|
134
|
+
in ownerApp: XCUIApplication,
|
|
135
|
+
source: RunnerAlertSource,
|
|
136
|
+
previousTitle: String,
|
|
137
|
+
actionButtonLabel: String,
|
|
138
|
+
deadline: Date
|
|
139
|
+
) -> Bool {
|
|
140
|
+
guard Date() < deadline,
|
|
141
|
+
let current = resolveAlert(source: source, app: ownerApp, deadline: deadline)
|
|
142
|
+
else {
|
|
77
143
|
return false
|
|
78
144
|
}
|
|
79
|
-
let previousTitle = preferredAlertTitle(alert.root, buttons: alert.buttons)
|
|
80
145
|
let currentTitle = preferredAlertTitle(current.root, buttons: current.buttons)
|
|
81
146
|
if previousTitle == currentTitle {
|
|
82
147
|
return true
|
|
83
148
|
}
|
|
84
|
-
let normalizedActionLabel = actionButtonLabel.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
85
149
|
return current.buttons.contains { button in
|
|
86
|
-
button.label.trimmingCharacters(in: .whitespacesAndNewlines) ==
|
|
150
|
+
button.label.trimmingCharacters(in: .whitespacesAndNewlines) == actionButtonLabel
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private func resolveAlert(
|
|
155
|
+
source: RunnerAlertSource,
|
|
156
|
+
app: XCUIApplication,
|
|
157
|
+
deadline: Date
|
|
158
|
+
) -> RunnerAlert? {
|
|
159
|
+
switch source {
|
|
160
|
+
case .blockingSystemModal:
|
|
161
|
+
#if os(macOS)
|
|
162
|
+
return nil
|
|
163
|
+
#else
|
|
164
|
+
guard case .resolved(let modal) = resolveBlockingSystemModal(deadline: deadline) else {
|
|
165
|
+
return nil
|
|
166
|
+
}
|
|
167
|
+
return runnerAlert(modal)
|
|
168
|
+
#endif
|
|
169
|
+
case .appAlert:
|
|
170
|
+
guard let alert = firstExistingElement(
|
|
171
|
+
in: safeElementsQuery { app.alerts.allElementsBoundByIndex }
|
|
172
|
+
) else {
|
|
173
|
+
return nil
|
|
174
|
+
}
|
|
175
|
+
return runnerAlert(root: alert, ownerApp: app, source: .appAlert)
|
|
176
|
+
case .dismissPopup:
|
|
177
|
+
guard let popup = firstDismissPopupWindow(in: app) else {
|
|
178
|
+
return nil
|
|
179
|
+
}
|
|
180
|
+
return runnerAlert(root: popup, ownerApp: app, source: .dismissPopup)
|
|
87
181
|
}
|
|
88
182
|
}
|
|
89
183
|
|