agent-device 0.21.15 → 0.21.16
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.15.apk → agent-device-android-ime-helper-0.21.16.apk} +0 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.16.apk.sha256 +1 -0
- package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.15.manifest.json → agent-device-android-ime-helper-0.21.16.manifest.json} +4 -4
- package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.15.apk → agent-device-android-snapshot-helper-0.21.16.apk} +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.16.apk.sha256 +1 -0
- package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.15.manifest.json → agent-device-android-snapshot-helper-0.21.16.manifest.json} +6 -6
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +1 -4
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.h +9 -9
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.m +13 -22
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSystemSurfaceHostPolicy.swift +0 -9
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +202 -73
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +17 -2
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +109 -128
- package/dist/src/adapter.js +1 -1
- package/dist/src/adb-executor.js +1 -1
- package/dist/src/adb-failure.js +1 -1
- package/dist/src/adb-transport.js +1 -1
- package/dist/src/agent-browser-provider.js +1 -1
- package/dist/src/agent-browser-tool.js +1 -1
- package/dist/src/agent-device-client.js +2 -2
- package/dist/src/ai-sdk.js +1 -1
- package/dist/src/alert-runtime.js +1 -1
- package/dist/src/android-input-ownership.js +1 -1
- package/dist/src/app-catalog.js +1 -1
- package/dist/src/app-deployment-resolution.js +1 -1
- package/dist/src/app-inventory.js +1 -1
- package/dist/src/app-launch.js +1 -1
- package/dist/src/app-lifecycle.js +1 -1
- package/dist/src/app-lifecycle2.js +1 -1
- package/dist/src/app-log-files.js +1 -1
- package/dist/src/app-log-runtime2.js +1 -1
- package/dist/src/app-resolution.js +1 -1
- package/dist/src/apple-runner-platform.js +1 -1
- package/dist/src/application-lifecycle-interaction.js +1 -1
- package/dist/src/archive.js +1 -1
- package/dist/src/artifact-download.js +1 -1
- package/dist/src/auth-session.js +1 -1
- package/dist/src/auth.js +1 -1
- package/dist/src/backend-snapshot-options.js +1 -1
- package/dist/src/boot-diagnostics.js +1 -1
- package/dist/src/capture-disclosure.js +1 -1
- package/dist/src/capture.js +1 -1
- package/dist/src/cli-config.js +1 -1
- package/dist/src/cli-help.js +3 -2
- package/dist/src/cli.js +4 -4
- package/dist/src/click-button.js +1 -1
- package/dist/src/client-metro.js +1 -1
- package/dist/src/client-types.d.ts +1 -1
- package/dist/src/clipboard-shell-response.js +1 -1
- package/dist/src/command-schema.js +1 -1
- package/dist/src/command-tools.js +2 -2
- package/dist/src/connection-runtime.js +1 -1
- package/dist/src/connection.js +5 -5
- package/dist/src/contracts.js +1 -1
- package/dist/src/daemon-client-lease-beat.js +1 -0
- package/dist/src/daemon-client-lifecycle.js +1 -1
- package/dist/src/daemon-client-transport.js +1 -0
- package/dist/src/daemon-shutdown-report.js +1 -1
- package/dist/src/daemon.js +1 -1
- package/dist/src/debug-symbols-facade.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-inventory-context.js +1 -1
- package/dist/src/device-rotation.js +1 -1
- package/dist/src/device-selection-resolver.js +1 -1
- package/dist/src/device-session.js +1 -1
- package/dist/src/device-shell.js +1 -1
- package/dist/src/device.js +1 -1
- package/dist/src/device2.js +1 -1
- package/dist/src/devicectl.js +1 -1
- package/dist/src/diagnostics.js +1 -1
- package/dist/src/dispatch-resolve.js +1 -1
- package/dist/src/doctor-facade.js +1 -1
- package/dist/src/doctor2.js +1 -1
- package/dist/src/durable-resource.js +1 -1
- package/dist/src/engine-eval-script.js +1 -1
- package/dist/src/error.js +1 -1
- package/dist/src/exec.js +1 -1
- package/dist/src/file.js +1 -1
- package/dist/src/find.js +1 -1
- package/dist/src/format.js +1 -1
- package/dist/src/gesture-admission.js +1 -1
- package/dist/src/gesture-plan.js +1 -1
- package/dist/src/gesture-runtime.js +1 -1
- package/dist/src/harmonyos.js +1 -1
- package/dist/src/helper.js +1 -1
- package/dist/src/host.js +1 -1
- package/dist/src/human-control-contract.js +1 -1
- package/dist/src/human-control.js +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/input-actions.js +1 -1
- package/dist/src/input-actions2.js +1 -1
- package/dist/src/install-artifact-facade.js +1 -1
- package/dist/src/install-source-network-transport.js +1 -1
- package/dist/src/install-source2.js +1 -1
- package/dist/src/interaction-positionals.js +1 -1
- package/dist/src/interaction-ref-policy.js +1 -1
- package/dist/src/interaction2.js +1 -1
- package/dist/src/interactor-operation-binding.js +1 -1
- package/dist/src/interactor.js +5 -4
- package/dist/src/interactor2.js +1 -1
- 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/internal/png-worker.js +1 -1
- package/dist/src/internal/run-script-http-child.js +1 -1
- package/dist/src/inventory.js +1 -1
- package/dist/src/inventory2.js +1 -1
- package/dist/src/inventory3.js +1 -1
- package/dist/src/inventory5.js +1 -1
- package/dist/src/ios-snapshot-engine.js +1 -1
- package/dist/src/ios-snapshot-runtime.js +1 -1
- package/dist/src/lease-context.js +1 -1
- package/dist/src/lease-scope.js +1 -1
- package/dist/src/lease.js +1 -1
- package/dist/src/limrun-download-file.js +1 -1
- package/dist/src/limrun-runtime-dependencies.js +1 -1
- package/dist/src/linux.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 +15 -15
- package/dist/src/multitouch-support.js +1 -1
- package/dist/src/native-build-cache.js +1 -1
- package/dist/src/observation.js +1 -1
- package/dist/src/overlay.js +2 -2
- package/dist/src/parameterized-recorded-fill.js +1 -1
- package/dist/src/perf-facade.js +1 -1
- package/dist/src/perf-runtime-operation-builder.js +1 -1
- package/dist/src/perf-runtime-plan.js +1 -1
- package/dist/src/perf-target.js +1 -1
- package/dist/src/perf.js +1 -1
- package/dist/src/physical-device-control.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-apple-simulator-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-process-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-size.js +1 -1
- package/dist/src/png-worker-client.js +1 -1
- package/dist/src/png.js +1 -1
- package/dist/src/prepare-kind.js +1 -1
- package/dist/src/provider-session-ownership.js +1 -1
- package/dist/src/provider-webdriver.js +1 -1
- package/dist/src/proxy.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.js +1 -1
- package/dist/src/rect-center.js +1 -1
- package/dist/src/register-builtins.js +1 -1
- package/dist/src/registry.js +13 -13
- package/dist/src/remote-config2.js +1 -1
- package/dist/src/replay-divergence.js +1 -1
- package/dist/src/replay.js +1 -1
- package/dist/src/reporting.js +1 -1
- package/dist/src/request-admission.js +1 -1
- package/dist/src/request-cancel.js +1 -1
- package/dist/src/request-runtime-binding.js +1 -1
- package/dist/src/result-serialization.js +1 -1
- package/dist/src/retry.js +1 -1
- package/dist/src/runner-artifact-manifest.js +1 -0
- package/dist/src/runner-cache-metadata.js +3 -2
- package/dist/src/runner-client.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-error-classification.js +3 -3
- package/dist/src/runner-operations-facade.js +1 -1
- package/dist/src/runner.js +1 -1
- package/dist/src/runtime-transport-hints.js +1 -1
- package/dist/src/runtime.js +1 -1
- package/dist/src/runtime10.js +2 -2
- package/dist/src/runtime2.js +3 -3
- package/dist/src/runtime3.js +1 -1
- package/dist/src/runtime4.js +4 -4
- 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-completion.js +1 -1
- package/dist/src/screen-recording-options.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-image.js +1 -1
- package/dist/src/screenshot-runtime.js +1 -1
- package/dist/src/screenshot.js +1 -1
- package/dist/src/screenshot3.js +1 -1
- package/dist/src/script.js +1 -1
- package/dist/src/scroll-command.js +1 -1
- package/dist/src/scroll-gesture.js +1 -1
- package/dist/src/sdk-batch-runner.js +2 -2
- package/dist/src/sdk-batch.js +1 -1
- package/dist/src/sdk-contracts.js +2 -2
- package/dist/src/sdk-device.js +1 -1
- package/dist/src/sdk-io.js +1 -1
- package/dist/src/sdk-remote-config.js +1 -1
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/server.js +1 -1
- package/dist/src/session-allocation.js +1 -1
- package/dist/src/session-routing.js +1 -1
- package/dist/src/session-runtime-admission.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/settings.js +1 -1
- package/dist/src/settings2.js +1 -1
- package/dist/src/simctl.js +1 -1
- package/dist/src/simulator-boot.js +1 -1
- package/dist/src/simulator.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/snapshot2.js +1 -1
- package/dist/src/snapshot3.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/src8.js +1 -1
- package/dist/src/stop-sequence.js +1 -1
- package/dist/src/string-enum.js +1 -1
- package/dist/src/takeover.js +1 -1
- package/dist/src/text-entry-focus.js +1 -1
- package/dist/src/tool-provider.js +1 -1
- package/dist/src/tool-provider2.js +1 -1
- package/dist/src/touch-runtime.js +1 -1
- package/dist/src/transport.js +1 -1
- package/dist/src/tv-remote.js +1 -1
- package/dist/src/type-text-runtime.js +1 -1
- package/dist/src/verified-file.js +1 -1
- package/dist/src/video.js +1 -1
- package/dist/src/viewport.js +1 -1
- package/dist/src/web.js +1 -1
- package/dist/src/web2.js +1 -1
- package/dist/src/webdriver-source.js +1 -1
- package/package.json +2 -2
- package/server.json +2 -2
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.15.apk.sha256 +0 -1
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.15.apk.sha256 +0 -1
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2b9f418df9ee3ddce97502fbb39dd34d06562d2747cf45e41393725a928d1007 agent-device-android-ime-helper-0.21.16.apk
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "android-ime-helper",
|
|
3
|
-
"version": "0.21.
|
|
4
|
-
"assetName": "agent-device-android-ime-helper-0.21.
|
|
5
|
-
"sha256": "
|
|
3
|
+
"version": "0.21.16",
|
|
4
|
+
"assetName": "agent-device-android-ime-helper-0.21.16.apk",
|
|
5
|
+
"sha256": "2b9f418df9ee3ddce97502fbb39dd34d06562d2747cf45e41393725a928d1007",
|
|
6
6
|
"packageName": "com.callstack.agentdevice.imehelper",
|
|
7
|
-
"versionCode":
|
|
7
|
+
"versionCode": 21016,
|
|
8
8
|
"serviceComponent": "com.callstack.agentdevice.imehelper/.TestInputMethodService",
|
|
9
9
|
"broadcastProtocol": "android-ime-helper-v1"
|
|
10
10
|
}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
a7db56d04eaee4009721cc4bf0947a12de4eb491b4a16e6b2bfa3b987a43012d agent-device-android-snapshot-helper-0.21.16.apk
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "android-snapshot-helper",
|
|
3
|
-
"version": "0.21.
|
|
4
|
-
"releaseTag": "v0.21.
|
|
5
|
-
"assetName": "agent-device-android-snapshot-helper-0.21.
|
|
3
|
+
"version": "0.21.16",
|
|
4
|
+
"releaseTag": "v0.21.16",
|
|
5
|
+
"assetName": "agent-device-android-snapshot-helper-0.21.16.apk",
|
|
6
6
|
"apkUrl": null,
|
|
7
|
-
"sha256": "
|
|
8
|
-
"checksumName": "agent-device-android-snapshot-helper-0.21.
|
|
7
|
+
"sha256": "a7db56d04eaee4009721cc4bf0947a12de4eb491b4a16e6b2bfa3b987a43012d",
|
|
8
|
+
"checksumName": "agent-device-android-snapshot-helper-0.21.16.apk.sha256",
|
|
9
9
|
"packageName": "com.callstack.agentdevice.snapshothelper",
|
|
10
|
-
"versionCode":
|
|
10
|
+
"versionCode": 21016,
|
|
11
11
|
"instrumentationRunner": "com.callstack.agentdevice.snapshothelper/.SnapshotInstrumentation",
|
|
12
12
|
"minSdk": 23,
|
|
13
13
|
"targetSdk": 36,
|
|
@@ -66,7 +66,6 @@ int main(int argc, const char *argv[]) {
|
|
|
66
66
|
@property(nonatomic, assign) NSUInteger firstAlertActions;
|
|
67
67
|
@property(nonatomic, assign) NSUInteger replacementAlertActions;
|
|
68
68
|
@property(nonatomic, strong) UILabel *textEntryWriteBackStatus;
|
|
69
|
-
@property(nonatomic, assign) NSUInteger textEntryRenderedEdits;
|
|
70
69
|
@property(nonatomic, assign) NSUInteger textEntryWriteBacks;
|
|
71
70
|
@property(nonatomic, copy, nullable) NSString *textEntryRenderedValue;
|
|
72
71
|
@property(nonatomic, assign) NSTimeInterval textEntryLastEditTime;
|
|
@@ -187,8 +186,7 @@ static NSTimeInterval AgentDeviceAlertActivationBusyWindow(void) {
|
|
|
187
186
|
- (void)updateTextEntryWriteBackStatus {
|
|
188
187
|
NSTimeInterval burstSpan = self.textEntryLastEditTime - self.textEntryBurstStartTime;
|
|
189
188
|
self.textEntryWriteBackStatus.text = [NSString
|
|
190
|
-
stringWithFormat:@"
|
|
191
|
-
(unsigned long)self.textEntryRenderedEdits,
|
|
189
|
+
stringWithFormat:@"write-backs=%lu burst-edits=%lu burst-ms=%lu min-gap-ms=%lu",
|
|
192
190
|
(unsigned long)self.textEntryWriteBacks,
|
|
193
191
|
(unsigned long)self.textEntryBurstEdits,
|
|
194
192
|
(unsigned long)llround(burstSpan * 1000),
|
|
@@ -292,7 +290,6 @@ static const NSTimeInterval AgentDeviceTextEntryBurstBreakSeconds = 1.0;
|
|
|
292
290
|
}
|
|
293
291
|
} else {
|
|
294
292
|
self.textEntryRenderedValue = [textField.text copy];
|
|
295
|
-
self.textEntryRenderedEdits += 1;
|
|
296
293
|
}
|
|
297
294
|
[self updateTextEntryWriteBackStatus];
|
|
298
295
|
}
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.h
CHANGED
|
@@ -17,21 +17,21 @@ typedef NS_ENUM(NSInteger, RunnerSynthesizedTextEntryStatus) {
|
|
|
17
17
|
|
|
18
18
|
@interface RunnerSynthesizedTextEntry : NSObject
|
|
19
19
|
|
|
20
|
-
// Characters per second the synthesized text-input records are typed at. Declared here, where the
|
|
21
|
-
// typing happens, so the delivery budget that bounds a burst is charged the same pace the app sees.
|
|
22
|
-
// The edit-acknowledge window that pace is sized for is a separate assumption about the app
|
|
23
|
-
// (TextEntryTiming.synthesizedAcknowledgeWindowSeconds), not a value derived from this one.
|
|
24
|
-
+ (NSUInteger)typingSpeedCharactersPerSecond;
|
|
25
|
-
|
|
26
20
|
// Synthesizes keyboard input for the current first responder without resolving an
|
|
27
21
|
// XCUIElement or serializing the application's accessibility tree.
|
|
22
|
+
//
|
|
23
|
+
// `charactersPerSecond` is XCTest's `typingSpeed:` argument. The caller declares it because the
|
|
24
|
+
// same number is what the caller's delivery budget charges a burst (#2955): a pace owned here and
|
|
25
|
+
// read back from Swift would let a call site type at a speed its own budget never charged.
|
|
28
26
|
+ (RunnerSynthesizedTextEntryResult *)synthesizeTextWithApplication:(id)application
|
|
29
|
-
text:(NSString *)text
|
|
27
|
+
text:(NSString *)text
|
|
28
|
+
charactersPerSecond:(NSUInteger)charactersPerSecond;
|
|
30
29
|
|
|
31
30
|
// Replaces the current first responder's contents with one synthesized Command-A record
|
|
32
|
-
// followed by a text-input record, typed at the
|
|
31
|
+
// followed by a text-input record, typed at the pace the caller declares.
|
|
33
32
|
+ (RunnerSynthesizedTextEntryResult *)replaceTextWithApplication:(id)application
|
|
34
|
-
text:(NSString *)text
|
|
33
|
+
text:(NSString *)text
|
|
34
|
+
charactersPerSecond:(NSUInteger)charactersPerSecond;
|
|
35
35
|
|
|
36
36
|
@end
|
|
37
37
|
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.m
CHANGED
|
@@ -5,19 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
static NSString *const RunnerTextSynthesisSurface = @"text";
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// erases what was typed while it was in flight, leaving a value that is stable short of the
|
|
13
|
-
// request. 12 characters/second spaces them ~83 ms apart on average, which reduces that loss but
|
|
14
|
-
// does not remove it: XCTest does not space the characters evenly, and two of them can reach the
|
|
15
|
-
// app a few milliseconds apart. Against a fixture app that acknowledges each edit within 40 ms, 60
|
|
16
|
-
// characters/second left 1 of 11 characters in 20 of 20 bursts, and this pace left 10 or 11. The
|
|
17
|
-
// command refuses a field left short; back-pressure from the field (#2906) is what would prevent
|
|
18
|
-
// it. The app-owned-value lane test pins the average spacing the app sees, and the delivery budget
|
|
19
|
-
// in TextEntryTiming bounds what the pace costs a long text.
|
|
20
|
-
static const NSUInteger RunnerTextEntryTypingSpeedCharactersPerSecond = 12;
|
|
8
|
+
// The pace (`typingSpeed:`) is a caller argument, not a constant here: TextEntryTiming owns it so
|
|
9
|
+
// the synthesized delivery budget charges the pace the app actually sees (#2955). XCTest's
|
|
10
|
+
// `typingSpeed:` is characters per second, and it spaces them unevenly — two characters of a paced
|
|
11
|
+
// burst can reach the app a few milliseconds apart at any pace.
|
|
21
12
|
|
|
22
13
|
typedef id (*RunnerTextMsgSendInit)(id, SEL, NSString *);
|
|
23
14
|
typedef id (*RunnerTextMsgSendInitPath)(id, SEL);
|
|
@@ -49,6 +40,7 @@ static RunnerSynthesizedTextEntryResult *RunnerTextEntryFailed(NSString *message
|
|
|
49
40
|
static RunnerSynthesizedTextEntryResult *RunnerSynthesizeTextWithMode(
|
|
50
41
|
id application,
|
|
51
42
|
NSString *text,
|
|
43
|
+
NSUInteger charactersPerSecond,
|
|
52
44
|
BOOL replace
|
|
53
45
|
);
|
|
54
46
|
|
|
@@ -65,18 +57,16 @@ static RunnerSynthesizedTextEntryResult *RunnerSynthesizeTextWithMode(
|
|
|
65
57
|
|
|
66
58
|
@implementation RunnerSynthesizedTextEntry
|
|
67
59
|
|
|
68
|
-
+ (NSUInteger)typingSpeedCharactersPerSecond {
|
|
69
|
-
return RunnerTextEntryTypingSpeedCharactersPerSecond;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
60
|
+ (RunnerSynthesizedTextEntryResult *)synthesizeTextWithApplication:(id)application
|
|
73
|
-
text:(NSString *)text
|
|
74
|
-
|
|
61
|
+
text:(NSString *)text
|
|
62
|
+
charactersPerSecond:(NSUInteger)charactersPerSecond {
|
|
63
|
+
return RunnerSynthesizeTextWithMode(application, text, charactersPerSecond, NO);
|
|
75
64
|
}
|
|
76
65
|
|
|
77
66
|
+ (RunnerSynthesizedTextEntryResult *)replaceTextWithApplication:(id)application
|
|
78
|
-
text:(NSString *)text
|
|
79
|
-
|
|
67
|
+
text:(NSString *)text
|
|
68
|
+
charactersPerSecond:(NSUInteger)charactersPerSecond {
|
|
69
|
+
return RunnerSynthesizeTextWithMode(application, text, charactersPerSecond, YES);
|
|
80
70
|
}
|
|
81
71
|
|
|
82
72
|
@end
|
|
@@ -84,6 +74,7 @@ static RunnerSynthesizedTextEntryResult *RunnerSynthesizeTextWithMode(
|
|
|
84
74
|
static RunnerSynthesizedTextEntryResult *RunnerSynthesizeTextWithMode(
|
|
85
75
|
id application,
|
|
86
76
|
NSString *text,
|
|
77
|
+
NSUInteger charactersPerSecond,
|
|
87
78
|
BOOL replace
|
|
88
79
|
) {
|
|
89
80
|
@try {
|
|
@@ -151,7 +142,7 @@ static RunnerSynthesizedTextEntryResult *RunnerSynthesizeTextWithMode(
|
|
|
151
142
|
bridge.typeTextSelector,
|
|
152
143
|
text,
|
|
153
144
|
0.0,
|
|
154
|
-
|
|
145
|
+
charactersPerSecond,
|
|
155
146
|
YES
|
|
156
147
|
);
|
|
157
148
|
((RunnerMsgSendAddPath)objc_msgSend)(record, bridge.core.addPathSelector, path);
|
|
@@ -20,13 +20,4 @@ enum SystemSurfaceHostRegistry {
|
|
|
20
20
|
SystemSurfaceHost(bundleId: "com.apple.SafariViewService", kind: .webAuth),
|
|
21
21
|
SystemSurfaceHost(bundleId: "com.apple.PassbookUIService", kind: .payment),
|
|
22
22
|
]
|
|
23
|
-
|
|
24
|
-
static func host(forBundleId bundleId: String?) -> SystemSurfaceHost? {
|
|
25
|
-
guard let bundleId else { return nil }
|
|
26
|
-
return hosts.first { $0.bundleId == bundleId }
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
static func isSystemSurfaceHost(_ bundleId: String?) -> Bool {
|
|
30
|
-
host(forBundleId: bundleId) != nil
|
|
31
|
-
}
|
|
32
23
|
}
|
|
@@ -42,8 +42,16 @@ extension RunnerTests {
|
|
|
42
42
|
#if os(iOS)
|
|
43
43
|
let postStartedAt = Date()
|
|
44
44
|
let result = replacingExistingText
|
|
45
|
-
? RunnerSynthesizedTextEntry.replaceText(
|
|
46
|
-
|
|
45
|
+
? RunnerSynthesizedTextEntry.replaceText(
|
|
46
|
+
withApplication: app,
|
|
47
|
+
text: text,
|
|
48
|
+
charactersPerSecond: TextEntryTiming.synthesizedCharactersPerSecond
|
|
49
|
+
)
|
|
50
|
+
: RunnerSynthesizedTextEntry.synthesizeText(
|
|
51
|
+
withApplication: app,
|
|
52
|
+
text: text,
|
|
53
|
+
charactersPerSecond: TextEntryTiming.synthesizedCharactersPerSecond
|
|
54
|
+
)
|
|
47
55
|
NSLog(
|
|
48
56
|
"[DEBUG-1874] synthesize posted %d chars status=%d tookMs=%.0f",
|
|
49
57
|
text.count, result.status.rawValue, postStartedAt.timeIntervalSinceNow * -1000
|
|
@@ -73,90 +81,206 @@ extension RunnerTests {
|
|
|
73
81
|
#endif
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
struct SynthesizedReplacementStep: Equatable {
|
|
77
|
-
let text: String
|
|
78
|
-
let replacesExistingText: Bool
|
|
79
|
-
}
|
|
80
84
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
struct SynthesizedTextPlan: Equatable {
|
|
89
|
+
|
|
90
|
+
struct Step: Equatable {
|
|
91
|
+
let characterCount: Int
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
var replacesExistingText = false
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
var pauseAfterSeconds: TimeInterval = 0
|
|
100
|
+
|
|
101
|
+
var warmsUpField = false
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
var synthesizeCallCount: Int {
|
|
105
|
+
replacesExistingText ? 2 : 1
|
|
106
|
+
}
|
|
88
107
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
108
|
+
|
|
109
|
+
let steps: [Step]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
static func spacedSteps(
|
|
114
|
+
characterCount: Int,
|
|
115
|
+
delaySeconds: Double,
|
|
116
|
+
replacesExistingTextOnFirstPost: Bool = false
|
|
117
|
+
) -> [Step] {
|
|
118
|
+
(0..<characterCount).map { index in
|
|
119
|
+
Step(
|
|
120
|
+
characterCount: 1,
|
|
121
|
+
replacesExistingText: replacesExistingTextOnFirstPost && index == 0,
|
|
122
|
+
pauseAfterSeconds: index + 1 < characterCount ? delaySeconds : 0
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
var seconds: TimeInterval {
|
|
130
|
+
steps.reduce(0) { total, step in
|
|
131
|
+
total
|
|
132
|
+
+ Double(step.characterCount) * TextEntryTiming.synthesizedCharacterInterval
|
|
133
|
+
+ Double(step.synthesizeCallCount) * TextEntryTiming.synthesizeCallOverhead
|
|
134
|
+
+ step.pauseAfterSeconds
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
var pacesEveryCharacter: Bool {
|
|
142
|
+
steps.count > 1 && !steps.contains(where: \.warmsUpField)
|
|
94
143
|
}
|
|
95
144
|
}
|
|
96
145
|
|
|
97
146
|
|
|
98
147
|
|
|
99
|
-
static func
|
|
148
|
+
static func synthesizedDeliveryIsSpaced(characterCount: Int, delaySeconds: Double) -> Bool {
|
|
100
149
|
delaySeconds > 0 && characterCount > 1
|
|
101
150
|
}
|
|
102
151
|
|
|
103
152
|
|
|
104
153
|
|
|
105
154
|
|
|
106
|
-
enum SynthesizedDeliveryBudget {
|
|
107
155
|
|
|
108
|
-
static var characterInterval: TimeInterval {
|
|
109
|
-
1.0 / Double(RunnerSynthesizedTextEntry.typingSpeedCharactersPerSecond())
|
|
110
|
-
}
|
|
111
156
|
|
|
112
157
|
|
|
113
158
|
|
|
114
159
|
|
|
160
|
+
static func synthesizedTextPlan(
|
|
161
|
+
characterCount: Int,
|
|
162
|
+
delaySeconds: Double,
|
|
163
|
+
selectsExistingText: Bool,
|
|
164
|
+
peelsWarmupCharacter: Bool = false
|
|
165
|
+
) -> SynthesizedTextPlan {
|
|
166
|
+
if synthesizedDeliveryIsSpaced(characterCount: characterCount, delaySeconds: delaySeconds) {
|
|
167
|
+
return SynthesizedTextPlan(
|
|
168
|
+
steps: SynthesizedTextPlan.spacedSteps(
|
|
169
|
+
characterCount: characterCount,
|
|
170
|
+
delaySeconds: delaySeconds,
|
|
171
|
+
replacesExistingTextOnFirstPost: selectsExistingText
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
guard peelsWarmupCharacter && characterCount > 1 else {
|
|
176
|
+
return SynthesizedTextPlan(
|
|
177
|
+
steps: [
|
|
178
|
+
SynthesizedTextPlan.Step(
|
|
179
|
+
characterCount: characterCount,
|
|
180
|
+
replacesExistingText: selectsExistingText
|
|
181
|
+
),
|
|
182
|
+
]
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
return SynthesizedTextPlan(
|
|
186
|
+
steps: [
|
|
187
|
+
SynthesizedTextPlan.Step(
|
|
188
|
+
characterCount: 1,
|
|
189
|
+
pauseAfterSeconds: TextEntryTiming.pollInterval,
|
|
190
|
+
warmsUpField: true
|
|
191
|
+
),
|
|
192
|
+
SynthesizedTextPlan.Step(characterCount: characterCount - 1),
|
|
193
|
+
]
|
|
194
|
+
)
|
|
195
|
+
}
|
|
115
196
|
|
|
116
197
|
|
|
117
|
-
static func projectedSeconds(
|
|
118
|
-
textLength: Int,
|
|
119
|
-
delaySeconds: TimeInterval,
|
|
120
|
-
typeWarmup: Bool = false
|
|
121
|
-
) -> TimeInterval {
|
|
122
|
-
let spaced = synthesizedReplacementIsSpaced(characterCount: textLength, delaySeconds: delaySeconds)
|
|
123
|
-
let warmupSplit = typeWarmup && textLength > 1 && !spaced
|
|
124
|
-
let calls = spaced ? textLength : (warmupSplit ? 2 : 1)
|
|
125
|
-
return Double(textLength) * characterInterval
|
|
126
|
-
+ Double(calls) * TextEntryTiming.synthesizeCallOverhead
|
|
127
|
-
+ Double(calls - 1) * delaySeconds
|
|
128
|
-
+ (warmupSplit ? TextEntryTiming.pollInterval : 0)
|
|
129
|
-
}
|
|
130
198
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
typeWarmup: Bool = false
|
|
135
|
-
) -> Bool {
|
|
136
|
-
projectedSeconds(textLength: textLength, delaySeconds: delaySeconds, typeWarmup: typeWarmup)
|
|
137
|
-
> TextEntryTiming.synthesizedDeliveryCeiling
|
|
199
|
+
enum SynthesizedDeliveryBudget {
|
|
200
|
+
static func exceeds(_ plan: SynthesizedTextPlan) -> Bool {
|
|
201
|
+
plan.seconds > TextEntryTiming.synthesizedDeliveryCeiling
|
|
138
202
|
}
|
|
139
203
|
|
|
140
204
|
|
|
205
|
+
|
|
206
|
+
|
|
141
207
|
static func maxTextLength(delaySeconds: TimeInterval) -> Int {
|
|
142
208
|
var length = 1
|
|
143
|
-
while !exceeds(
|
|
209
|
+
while !exceeds(
|
|
210
|
+
synthesizedTextPlan(
|
|
211
|
+
characterCount: length + 1,
|
|
212
|
+
delaySeconds: delaySeconds,
|
|
213
|
+
selectsExistingText: true
|
|
214
|
+
)
|
|
215
|
+
) {
|
|
144
216
|
length += 1
|
|
145
217
|
}
|
|
146
218
|
return length
|
|
147
219
|
}
|
|
148
220
|
}
|
|
149
221
|
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
enum SynthesizedStepDispatch {
|
|
225
|
+
case posted
|
|
226
|
+
case stop
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
struct SynthesizedPlanRun {
|
|
230
|
+
let postedCharacterCount: Int
|
|
231
|
+
|
|
232
|
+
let stoppedEarly: Bool
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@MainActor
|
|
246
|
+
func runSynthesizedTextPlan(
|
|
247
|
+
_ plan: SynthesizedTextPlan,
|
|
248
|
+
text: String,
|
|
249
|
+
post: (_ characters: String, _ step: SynthesizedTextPlan.Step) -> SynthesizedStepDispatch,
|
|
250
|
+
waitAfterWarmupCharacter: (_ characters: String) -> Void,
|
|
251
|
+
didPostStep: (_ step: SynthesizedTextPlan.Step) -> Void = { _ in }
|
|
252
|
+
) -> SynthesizedPlanRun {
|
|
253
|
+
let characters = Array(text)
|
|
254
|
+
var postedCount = 0
|
|
255
|
+
for step in plan.steps {
|
|
256
|
+
let nextCount = postedCount + step.characterCount
|
|
257
|
+
if post(String(characters[postedCount..<nextCount]), step) == .stop {
|
|
258
|
+
|
|
259
|
+
return SynthesizedPlanRun(postedCharacterCount: postedCount, stoppedEarly: true)
|
|
260
|
+
}
|
|
261
|
+
postedCount = nextCount
|
|
262
|
+
didPostStep(step)
|
|
263
|
+
if step.warmsUpField {
|
|
264
|
+
waitAfterWarmupCharacter(String(characters[postedCount - step.characterCount..<postedCount]))
|
|
265
|
+
} else if step.pauseAfterSeconds > 0 {
|
|
266
|
+
sleepFor(step.pauseAfterSeconds)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return SynthesizedPlanRun(postedCharacterCount: postedCount, stoppedEarly: false)
|
|
270
|
+
}
|
|
271
|
+
|
|
150
272
|
@MainActor
|
|
151
273
|
func runSynthesizedReplacementRoute(
|
|
152
274
|
_ request: SynthesizedReplacementRequest
|
|
153
275
|
) -> SynthesizedReplacementRouteOutcome {
|
|
154
276
|
#if os(iOS)
|
|
155
277
|
NSLog("AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=synthesized-first-responder-replacement")
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
delaySeconds: request.delaySeconds
|
|
159
|
-
|
|
278
|
+
let plan = Self.synthesizedTextPlan(
|
|
279
|
+
characterCount: request.text.count,
|
|
280
|
+
delaySeconds: request.delaySeconds,
|
|
281
|
+
selectsExistingText: true
|
|
282
|
+
)
|
|
283
|
+
if SynthesizedDeliveryBudget.exceeds(plan) {
|
|
160
284
|
NSLog(
|
|
161
285
|
"AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=synthesized-first-responder-replacement "
|
|
162
286
|
+ "reason=delivery-budget-refused chars=%d budgetChars=%d",
|
|
@@ -174,33 +298,38 @@ extension RunnerTests {
|
|
|
174
298
|
)
|
|
175
299
|
)
|
|
176
300
|
}
|
|
177
|
-
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
let synthesisAvailable = runSynthesizedTextPlan(
|
|
304
|
+
plan,
|
|
178
305
|
text: request.text,
|
|
179
|
-
|
|
306
|
+
post: { slice, step in
|
|
307
|
+
switch request.synthesizer.enterText(
|
|
308
|
+
app: request.app,
|
|
309
|
+
text: slice,
|
|
310
|
+
replacingExistingText: step.replacesExistingText
|
|
311
|
+
) {
|
|
312
|
+
case .continueTyping:
|
|
313
|
+
return .posted
|
|
314
|
+
case .fallback:
|
|
315
|
+
return .stop
|
|
316
|
+
case .raise(let message):
|
|
317
|
+
NSException(
|
|
318
|
+
name: NSExceptionName.internalInconsistencyException,
|
|
319
|
+
reason: message ?? "private XCTest text synthesis failed"
|
|
320
|
+
).raise()
|
|
321
|
+
return .stop
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
waitAfterWarmupCharacter: { _ in }
|
|
180
326
|
)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
)
|
|
187
|
-
case .fallback:
|
|
188
|
-
NSLog("AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=verified-fallback reason=synthesis-unavailable")
|
|
189
|
-
guard let point = request.target.refreshPoint else { return .notApplicable }
|
|
190
|
-
return .fallback(
|
|
191
|
-
focusTextInputForTextEntry(app: request.app, x: point.x, y: point.y)
|
|
192
|
-
)
|
|
193
|
-
case .raise(let message):
|
|
194
|
-
NSException(
|
|
195
|
-
name: NSExceptionName.internalInconsistencyException,
|
|
196
|
-
reason: message ?? "private XCTest text synthesis failed"
|
|
197
|
-
).raise()
|
|
198
|
-
case .continueTyping:
|
|
199
|
-
break
|
|
200
|
-
}
|
|
201
|
-
if index + 1 < steps.count {
|
|
202
|
-
sleepFor(request.delaySeconds)
|
|
203
|
-
}
|
|
327
|
+
if synthesisAvailable.stoppedEarly {
|
|
328
|
+
NSLog("AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=verified-fallback reason=synthesis-unavailable")
|
|
329
|
+
guard let point = request.target.refreshPoint else { return .notApplicable }
|
|
330
|
+
return .fallback(
|
|
331
|
+
focusTextInputForTextEntry(app: request.app, x: point.x, y: point.y)
|
|
332
|
+
)
|
|
204
333
|
}
|
|
205
334
|
|
|
206
335
|
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift
CHANGED
|
@@ -36,7 +36,11 @@ extension RunnerTests {
|
|
|
36
36
|
let recoveryBudget = SynthesizedDeliveryBudget.maxTextLength(
|
|
37
37
|
delaySeconds: Double(recoveryDelay) / 1000
|
|
38
38
|
)
|
|
39
|
-
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
return "Fill at most \(SynthesizedDeliveryBudget.maxTextLength(delaySeconds: 0)) characters per command without --delay-ms and append the rest with separate type commands. --delay-ms lowers the limit: each character then gets its own synthesize call and each gap pays the delay, so \(recoveryDelay) ms fits \(recoveryBudget). This route is chosen when the accessibility channel is already degraded, so a longer timeout does not help."
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
}
|
|
@@ -76,7 +80,18 @@ extension RunnerTests {
|
|
|
76
80
|
|
|
77
81
|
|
|
78
82
|
|
|
79
|
-
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
static let synthesizedCharactersPerSecond: UInt = 12
|
|
93
|
+
|
|
94
|
+
static let synthesizedCharacterInterval: TimeInterval = 1.0 / Double(synthesizedCharactersPerSecond)
|
|
80
95
|
|
|
81
96
|
|
|
82
97
|
|