@houwert/conductor 0.15.0 → 0.17.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/dist/commands/assert-visible.js +2 -1
- package/dist/commands/clipboard.js +84 -0
- package/dist/commands/crashes.js +262 -0
- package/dist/commands/debug.js +244 -0
- package/dist/commands/flow-record.js +58 -0
- package/dist/commands/gestures.js +196 -0
- package/dist/commands/inspect.js +41 -1
- package/dist/commands/metro.js +109 -0
- package/dist/commands/network.js +194 -0
- package/dist/commands/profile.js +300 -0
- package/dist/commands/run-sequence.js +124 -0
- package/dist/commands/screenshot.js +4 -3
- package/dist/commands/scroll-until-visible.js +17 -0
- package/dist/commands/start-device.js +18 -0
- package/dist/commands/tap.js +2 -1
- package/dist/commands/workspace.js +162 -0
- package/dist/daemon/web-server.js +25 -2
- package/dist/drivers/android.js +10 -1
- package/dist/drivers/direct-ios-selector.js +70 -0
- package/dist/drivers/element-resolver.js +189 -0
- package/dist/drivers/flow-recorder.js +135 -0
- package/dist/drivers/flow-runner.js +5 -3
- package/dist/drivers/ios.js +88 -3
- package/dist/drivers/metro-cdp.js +291 -0
- package/dist/drivers/metro-scripts.js +399 -0
- package/dist/drivers/wait.js +20 -2
- package/dist/drivers/web.js +3 -2
- package/dist/index.js +264 -2
- package/dist/runner.js +18 -0
- package/package.json +1 -1
- package/proto/conductor_android.proto +29 -0
package/dist/index.js
CHANGED
|
@@ -30,6 +30,15 @@ const daemon_js_1 = require("./commands/daemon.js");
|
|
|
30
30
|
const install_js_1 = require("./commands/install.js");
|
|
31
31
|
const device_pool_js_1 = require("./commands/device-pool.js");
|
|
32
32
|
const run_parallel_js_1 = require("./commands/run-parallel.js");
|
|
33
|
+
const run_sequence_js_1 = require("./commands/run-sequence.js");
|
|
34
|
+
const gestures_js_1 = require("./commands/gestures.js");
|
|
35
|
+
const workspace_js_1 = require("./commands/workspace.js");
|
|
36
|
+
const debug_js_1 = require("./commands/debug.js");
|
|
37
|
+
const network_js_1 = require("./commands/network.js");
|
|
38
|
+
const flow_record_js_1 = require("./commands/flow-record.js");
|
|
39
|
+
const profile_js_1 = require("./commands/profile.js");
|
|
40
|
+
const crashes_js_1 = require("./commands/crashes.js");
|
|
41
|
+
const flow_recorder_js_1 = require("./drivers/flow-recorder.js");
|
|
33
42
|
const foreground_app_js_1 = require("./commands/foreground-app.js");
|
|
34
43
|
const list_apps_js_1 = require("./commands/list-apps.js");
|
|
35
44
|
const copy_app_js_1 = require("./commands/copy-app.js");
|
|
@@ -47,6 +56,8 @@ const stop_device_js_1 = require("./commands/stop-device.js");
|
|
|
47
56
|
const delete_device_js_1 = require("./commands/delete-device.js");
|
|
48
57
|
const logs_js_1 = require("./commands/logs.js");
|
|
49
58
|
const memory_js_1 = require("./commands/memory.js");
|
|
59
|
+
const metro_js_1 = require("./commands/metro.js");
|
|
60
|
+
const clipboard_js_1 = require("./commands/clipboard.js");
|
|
50
61
|
const device_picker_js_1 = require("./device-picker.js");
|
|
51
62
|
const update_check_js_1 = require("./update-check.js");
|
|
52
63
|
const pkg_root_js_1 = require("./pkg-root.js");
|
|
@@ -93,8 +104,19 @@ const COMMAND_HELP = {
|
|
|
93
104
|
'daemon-status': daemon_js_1.HELP_DAEMON_STATUS,
|
|
94
105
|
'device-pool': device_pool_js_1.HELP,
|
|
95
106
|
'run-parallel': run_parallel_js_1.HELP,
|
|
107
|
+
'run-sequence': run_sequence_js_1.HELP,
|
|
108
|
+
gestures: gestures_js_1.HELP,
|
|
109
|
+
workspace: workspace_js_1.HELP,
|
|
110
|
+
debug: debug_js_1.HELP,
|
|
111
|
+
network: network_js_1.HELP,
|
|
112
|
+
'flow record': flow_record_js_1.HELP,
|
|
113
|
+
profile: profile_js_1.HELP,
|
|
114
|
+
crashes: crashes_js_1.HELP,
|
|
96
115
|
logs: logs_js_1.HELP,
|
|
97
116
|
memory: memory_js_1.HELP,
|
|
117
|
+
metro: metro_js_1.HELP,
|
|
118
|
+
clipboard: clipboard_js_1.HELP,
|
|
119
|
+
paste: ' paste Trigger OS-level paste (or type clipboard on iOS)',
|
|
98
120
|
};
|
|
99
121
|
const OPTIONS_HELP = `Options:
|
|
100
122
|
--device <id> Target device ID (also keys the session and daemon)
|
|
@@ -133,6 +155,7 @@ async function main() {
|
|
|
133
155
|
'optional',
|
|
134
156
|
'benchmark',
|
|
135
157
|
'dump',
|
|
158
|
+
'tappable',
|
|
136
159
|
'objects',
|
|
137
160
|
'heap',
|
|
138
161
|
'leaks',
|
|
@@ -173,6 +196,24 @@ async function main() {
|
|
|
173
196
|
'vs',
|
|
174
197
|
'top',
|
|
175
198
|
'filter',
|
|
199
|
+
'port',
|
|
200
|
+
'target',
|
|
201
|
+
'at',
|
|
202
|
+
'file',
|
|
203
|
+
'scale',
|
|
204
|
+
'center',
|
|
205
|
+
'degrees',
|
|
206
|
+
'angle',
|
|
207
|
+
'limit',
|
|
208
|
+
'method',
|
|
209
|
+
'body',
|
|
210
|
+
'header',
|
|
211
|
+
'url',
|
|
212
|
+
'out',
|
|
213
|
+
'track',
|
|
214
|
+
'interval',
|
|
215
|
+
'app',
|
|
216
|
+
'since',
|
|
176
217
|
],
|
|
177
218
|
alias: { h: 'help', v: 'verbose', V: 'version' },
|
|
178
219
|
});
|
|
@@ -207,6 +248,8 @@ async function main() {
|
|
|
207
248
|
'copy-app',
|
|
208
249
|
'device-pool',
|
|
209
250
|
'run-parallel',
|
|
251
|
+
'metro',
|
|
252
|
+
'workspace',
|
|
210
253
|
// `logs --list` and `logs --source metro` only query Metro on localhost — no device needed
|
|
211
254
|
// `logs` always needs a device session — Metro discovery is device-scoped.
|
|
212
255
|
// `daemon-stop --all` stops every daemon — no device needed
|
|
@@ -461,7 +504,8 @@ async function main() {
|
|
|
461
504
|
}
|
|
462
505
|
case 'take-screenshot': {
|
|
463
506
|
const outPath = argv['output'];
|
|
464
|
-
|
|
507
|
+
const fullPage = Boolean(argv['full-page']);
|
|
508
|
+
exitCode = await (0, screenshot_js_1.screenshot)(outPath, opts, sessionName, fullPage);
|
|
465
509
|
break;
|
|
466
510
|
}
|
|
467
511
|
case 'capture-ui': {
|
|
@@ -470,7 +514,11 @@ async function main() {
|
|
|
470
514
|
break;
|
|
471
515
|
}
|
|
472
516
|
case 'inspect':
|
|
473
|
-
exitCode = await (0, inspect_js_1.inspect)(opts, sessionName, {
|
|
517
|
+
exitCode = await (0, inspect_js_1.inspect)(opts, sessionName, {
|
|
518
|
+
dump: argv['dump'],
|
|
519
|
+
at: argv['at'],
|
|
520
|
+
tappableOnly: argv['tappable'],
|
|
521
|
+
});
|
|
474
522
|
break;
|
|
475
523
|
case 'focused':
|
|
476
524
|
exitCode = await (0, focused_js_1.focused)(opts, sessionName, {
|
|
@@ -541,17 +589,231 @@ async function main() {
|
|
|
541
589
|
exitCode = await (0, device_pool_js_1.devicePool)(action, releaseId, opts);
|
|
542
590
|
break;
|
|
543
591
|
}
|
|
592
|
+
case 'run-sequence': {
|
|
593
|
+
const file = argv['file'] ?? rest[0];
|
|
594
|
+
exitCode = await (0, run_sequence_js_1.runSequence)(file, opts, sessionName);
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
544
597
|
case 'run-parallel': {
|
|
545
598
|
const flowsDir = argv['flows-dir'] ?? rest[0] ?? '';
|
|
546
599
|
exitCode = await (0, run_parallel_js_1.runParallel)(flowsDir, opts);
|
|
547
600
|
break;
|
|
548
601
|
}
|
|
602
|
+
case 'crashes': {
|
|
603
|
+
const sub = (rest[0] ?? 'list').toLowerCase();
|
|
604
|
+
if (sub === 'list') {
|
|
605
|
+
exitCode = await (0, crashes_js_1.crashesList)(opts, sessionName, {
|
|
606
|
+
app: argv['app'],
|
|
607
|
+
since: argv['since'],
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
else if (sub === 'show') {
|
|
611
|
+
exitCode = await (0, crashes_js_1.crashesShow)(rest[1] ?? '', opts);
|
|
612
|
+
}
|
|
613
|
+
else if (sub === 'tail') {
|
|
614
|
+
exitCode = await (0, crashes_js_1.crashesTail)(opts, sessionName);
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
console.error('Usage: conductor crashes <list|show|tail>');
|
|
618
|
+
exitCode = 1;
|
|
619
|
+
}
|
|
620
|
+
break;
|
|
621
|
+
}
|
|
622
|
+
case 'profile': {
|
|
623
|
+
const sub = (rest[0] ?? '').toLowerCase();
|
|
624
|
+
const port = argv['port'] !== undefined ? Number(argv['port']) : undefined;
|
|
625
|
+
const targetIndex = argv['target'] !== undefined ? Number(argv['target']) : undefined;
|
|
626
|
+
if (sub === 'cpu') {
|
|
627
|
+
const durationSec = argv['duration'] !== undefined ? Number(argv['duration']) : 10;
|
|
628
|
+
exitCode = await (0, profile_js_1.profileCpu)(opts, sessionName, {
|
|
629
|
+
durationSec,
|
|
630
|
+
out: argv['out'],
|
|
631
|
+
appId: rest[1],
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
else if (sub === 'memory') {
|
|
635
|
+
const trackSec = argv['track'] !== undefined ? Number(argv['track']) : 10;
|
|
636
|
+
const intervalMs = argv['interval'] !== undefined ? Number(argv['interval']) : 1000;
|
|
637
|
+
exitCode = await (0, profile_js_1.profileMemory)(opts, sessionName, {
|
|
638
|
+
trackSec,
|
|
639
|
+
intervalMs,
|
|
640
|
+
appId: rest[1],
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
else if (sub === 'react') {
|
|
644
|
+
const sub2 = (rest[1] ?? '').toLowerCase();
|
|
645
|
+
const top = argv['top'] !== undefined ? Number(argv['top']) : 20;
|
|
646
|
+
if (sub2 === 'start') {
|
|
647
|
+
exitCode = await (0, profile_js_1.profileReactStart)(opts, sessionName, { port, targetIndex });
|
|
648
|
+
}
|
|
649
|
+
else if (sub2 === 'stop') {
|
|
650
|
+
exitCode = await (0, profile_js_1.profileReactStop)(opts, sessionName, { port, targetIndex }, top);
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
console.error('Usage: conductor profile react <start|stop>');
|
|
654
|
+
exitCode = 1;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
console.error('Usage: conductor profile <cpu|memory|react> [args]');
|
|
659
|
+
exitCode = 1;
|
|
660
|
+
}
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
case 'flow': {
|
|
664
|
+
const sub1 = (rest[0] ?? '').toLowerCase();
|
|
665
|
+
if (sub1 !== 'record') {
|
|
666
|
+
console.error('Usage: conductor flow record <start|finish|echo|status>');
|
|
667
|
+
exitCode = 1;
|
|
668
|
+
break;
|
|
669
|
+
}
|
|
670
|
+
const sub2 = (rest[1] ?? '').toLowerCase();
|
|
671
|
+
exitCode = await (0, flow_record_js_1.flowRecord)(sub2, rest.slice(2).map(String), opts, sessionName, argv);
|
|
672
|
+
break;
|
|
673
|
+
}
|
|
674
|
+
case 'network': {
|
|
675
|
+
const sub = (rest[0] ?? '').toLowerCase();
|
|
676
|
+
const port = argv['port'] !== undefined ? Number(argv['port']) : undefined;
|
|
677
|
+
const targetIndex = argv['target'] !== undefined ? Number(argv['target']) : undefined;
|
|
678
|
+
if (sub === 'logs') {
|
|
679
|
+
const limit = argv['limit'] !== undefined ? Number(argv['limit']) : undefined;
|
|
680
|
+
exitCode = await (0, network_js_1.networkLogs)(opts, sessionName, { port, targetIndex, limit });
|
|
681
|
+
}
|
|
682
|
+
else if (sub === 'request') {
|
|
683
|
+
const url = rest[1] ?? argv['url'] ?? '';
|
|
684
|
+
const rawHeaders = argv['header'];
|
|
685
|
+
const headers = Array.isArray(rawHeaders)
|
|
686
|
+
? rawHeaders
|
|
687
|
+
: rawHeaders
|
|
688
|
+
? [rawHeaders]
|
|
689
|
+
: [];
|
|
690
|
+
exitCode = await (0, network_js_1.networkRequest)(url, opts, sessionName, {
|
|
691
|
+
port,
|
|
692
|
+
targetIndex,
|
|
693
|
+
method: argv['method'],
|
|
694
|
+
body: argv['body'],
|
|
695
|
+
headers,
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
else {
|
|
699
|
+
console.error('Usage: conductor network <logs|request> [args]');
|
|
700
|
+
exitCode = 1;
|
|
701
|
+
}
|
|
702
|
+
break;
|
|
703
|
+
}
|
|
704
|
+
case 'debug': {
|
|
705
|
+
const sub = (rest[0] ?? '').toLowerCase();
|
|
706
|
+
const debugOpts = {
|
|
707
|
+
port: argv['port'] !== undefined ? Number(argv['port']) : undefined,
|
|
708
|
+
targetIndex: argv['target'] !== undefined ? Number(argv['target']) : undefined,
|
|
709
|
+
};
|
|
710
|
+
if (sub === 'status') {
|
|
711
|
+
exitCode = await (0, debug_js_1.debugStatus)(opts, sessionName, debugOpts);
|
|
712
|
+
}
|
|
713
|
+
else if (sub === 'evaluate' || sub === 'eval') {
|
|
714
|
+
const expr = rest.slice(1).join(' ');
|
|
715
|
+
exitCode = await (0, debug_js_1.debugEvaluate)(expr, opts, sessionName, debugOpts);
|
|
716
|
+
}
|
|
717
|
+
else if (sub === 'component-tree') {
|
|
718
|
+
exitCode = await (0, debug_js_1.debugComponentTree)(opts, sessionName, debugOpts);
|
|
719
|
+
}
|
|
720
|
+
else if (sub === 'inspect-element') {
|
|
721
|
+
const at = rest[1] ?? argv['at'] ?? '';
|
|
722
|
+
exitCode = await (0, debug_js_1.debugInspectElement)(at, opts, sessionName, debugOpts);
|
|
723
|
+
}
|
|
724
|
+
else if (sub === 'log-registry') {
|
|
725
|
+
exitCode = await (0, debug_js_1.debugLogRegistry)(opts, sessionName);
|
|
726
|
+
}
|
|
727
|
+
else if (sub === 'reload') {
|
|
728
|
+
exitCode = await (0, debug_js_1.debugReload)(opts, sessionName, debugOpts);
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
console.error('Usage: conductor debug <status|evaluate|component-tree|inspect-element|log-registry|reload>');
|
|
732
|
+
exitCode = 1;
|
|
733
|
+
}
|
|
734
|
+
break;
|
|
735
|
+
}
|
|
736
|
+
case 'workspace': {
|
|
737
|
+
const sub = (rest[0] ?? 'info').toLowerCase();
|
|
738
|
+
exitCode = await (0, workspace_js_1.workspaceCmd)(sub, opts);
|
|
739
|
+
break;
|
|
740
|
+
}
|
|
741
|
+
case 'pinch':
|
|
742
|
+
exitCode = await (0, gestures_js_1.pinch)(opts, sessionName, {
|
|
743
|
+
scale: argv['scale'] !== undefined ? Number(argv['scale']) : undefined,
|
|
744
|
+
center: argv['center'],
|
|
745
|
+
duration: argv['duration'] !== undefined ? Number(argv['duration']) : undefined,
|
|
746
|
+
angle: argv['angle'] !== undefined ? Number(argv['angle']) : undefined,
|
|
747
|
+
});
|
|
748
|
+
break;
|
|
749
|
+
case 'rotate-gesture':
|
|
750
|
+
exitCode = await (0, gestures_js_1.rotateGesture)(opts, sessionName, {
|
|
751
|
+
degrees: argv['degrees'] !== undefined ? Number(argv['degrees']) : undefined,
|
|
752
|
+
center: argv['center'],
|
|
753
|
+
duration: argv['duration'] !== undefined ? Number(argv['duration']) : undefined,
|
|
754
|
+
});
|
|
755
|
+
break;
|
|
756
|
+
case 'gesture': {
|
|
757
|
+
const file = argv['file'];
|
|
758
|
+
const rawJson = !file ? rest.join(' ') : undefined;
|
|
759
|
+
exitCode = await (0, gestures_js_1.gesture)(rawJson, file, opts, sessionName);
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
case 'clipboard': {
|
|
763
|
+
const sub = (rest[0] ?? '').toLowerCase();
|
|
764
|
+
if (sub === 'read') {
|
|
765
|
+
exitCode = await (0, clipboard_js_1.clipboardRead)(opts, sessionName);
|
|
766
|
+
}
|
|
767
|
+
else if (sub === 'write') {
|
|
768
|
+
const text = rest.slice(1).join(' ');
|
|
769
|
+
exitCode = await (0, clipboard_js_1.clipboardWrite)(text, opts, sessionName);
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
console.error('Usage: conductor clipboard <read|write> [text]');
|
|
773
|
+
exitCode = 1;
|
|
774
|
+
}
|
|
775
|
+
break;
|
|
776
|
+
}
|
|
777
|
+
case 'paste':
|
|
778
|
+
exitCode = await (0, clipboard_js_1.paste)(opts, sessionName);
|
|
779
|
+
break;
|
|
780
|
+
case 'metro': {
|
|
781
|
+
const sub = (rest[0] ?? '').toLowerCase();
|
|
782
|
+
const port = argv['port'] !== undefined ? Number(argv['port']) : undefined;
|
|
783
|
+
const targetIndex = argv['target'] !== undefined ? Number(argv['target']) : undefined;
|
|
784
|
+
const metroSession = argv['device'] ?? 'default';
|
|
785
|
+
if (sub === 'stop') {
|
|
786
|
+
exitCode = await (0, metro_js_1.metroStop)(opts, { port });
|
|
787
|
+
}
|
|
788
|
+
else if (sub === 'reload') {
|
|
789
|
+
exitCode = await (0, metro_js_1.metroReload)(opts, metroSession, { port, targetIndex });
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
console.error('Usage: conductor metro <stop|reload> [--port N] [--target N]');
|
|
793
|
+
exitCode = 1;
|
|
794
|
+
}
|
|
795
|
+
break;
|
|
796
|
+
}
|
|
549
797
|
default:
|
|
550
798
|
// Should be unreachable — unknown commands are caught before device resolution.
|
|
551
799
|
console.error(`Unknown command: ${command}`);
|
|
552
800
|
console.error('Run `conductor --help` for usage.');
|
|
553
801
|
exitCode = 1;
|
|
554
802
|
}
|
|
803
|
+
// Flow recording — append a YAML step for action commands that succeeded.
|
|
804
|
+
if (exitCode === 0 && !NO_DEVICE_COMMANDS.has(command) && command !== 'flow') {
|
|
805
|
+
try {
|
|
806
|
+
const active = await (0, flow_recorder_js_1.getActiveRecording)(sessionName);
|
|
807
|
+
if (active) {
|
|
808
|
+
const step = (0, flow_recorder_js_1.commandToYamlStep)(command, rest.map(String), argv);
|
|
809
|
+
if (step)
|
|
810
|
+
(0, flow_recorder_js_1.appendStep)(active, step);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
catch {
|
|
814
|
+
// Recording is best-effort — never fail the command for a bookkeeping issue.
|
|
815
|
+
}
|
|
816
|
+
}
|
|
555
817
|
process.exit(exitCode);
|
|
556
818
|
}
|
|
557
819
|
main().catch((err) => {
|
package/dist/runner.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.detectFirstDevice = detectFirstDevice;
|
|
4
4
|
exports.getDriver = getDriver;
|
|
5
|
+
exports.prewarmDriver = prewarmDriver;
|
|
5
6
|
exports.runDirect = runDirect;
|
|
6
7
|
exports.spawnCommand = spawnCommand;
|
|
7
8
|
exports.runInlineFlow = runInlineFlow;
|
|
@@ -194,6 +195,23 @@ async function getDriver(sessionName = 'default') {
|
|
|
194
195
|
_driverCache.set(sessionName, driver);
|
|
195
196
|
return driver;
|
|
196
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Eagerly start the device daemon (and its XCTest driver) for `deviceId` so
|
|
200
|
+
* the runner is already warm by the time the first interaction command runs.
|
|
201
|
+
*
|
|
202
|
+
* `start-device` is the natural prewarm point: bringing the driver up there
|
|
203
|
+
* moves the one-off startup cost off the first `tap-on` / `assert-visible`.
|
|
204
|
+
* Best-effort — failures are swallowed since a real command will surface any
|
|
205
|
+
* genuine startup error with a proper message.
|
|
206
|
+
*/
|
|
207
|
+
async function prewarmDriver(deviceId) {
|
|
208
|
+
try {
|
|
209
|
+
await (0, client_js_1.startDaemon)(deviceId);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
/* best-effort: a later command will report a real failure */
|
|
213
|
+
}
|
|
214
|
+
}
|
|
197
215
|
/**
|
|
198
216
|
* Execute a function with the driver for the given session.
|
|
199
217
|
* Returns a RunResult for consistent error handling across commands.
|
package/package.json
CHANGED
|
@@ -12,6 +12,8 @@ service ConductorDriver {
|
|
|
12
12
|
|
|
13
13
|
rpc tap(TapRequest) returns (TapResponse) {}
|
|
14
14
|
|
|
15
|
+
rpc gesturePath(GesturePathRequest) returns (GesturePathResponse) {}
|
|
16
|
+
|
|
15
17
|
rpc inputText(InputTextRequest) returns (InputTextResponse) {}
|
|
16
18
|
|
|
17
19
|
rpc eraseAllText(EraseAllTextRequest) returns (EraseAllTextResponse) {}
|
|
@@ -76,6 +78,33 @@ message TapRequest {
|
|
|
76
78
|
|
|
77
79
|
message TapResponse {}
|
|
78
80
|
|
|
81
|
+
// One step in a finger's path.
|
|
82
|
+
// x, y pixel coordinates on the device's natural orientation.
|
|
83
|
+
// dt_ms delay in milliseconds since the previous step. For the first
|
|
84
|
+
// step in a path, this is the initial offset before the touch
|
|
85
|
+
// goes down.
|
|
86
|
+
message GestureStep {
|
|
87
|
+
double x = 1;
|
|
88
|
+
double y = 2;
|
|
89
|
+
uint32 dt_ms = 3;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// A single finger's traced path. Must have at least one step. The first step
|
|
93
|
+
// is the down position; subsequent steps are moves; the finger lifts after
|
|
94
|
+
// the last step.
|
|
95
|
+
message GestureFingerPath {
|
|
96
|
+
repeated GestureStep steps = 1;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Multi-finger gesture request. paths.size() == 1 is a single-finger gesture;
|
|
100
|
+
// paths.size() >= 2 dispatches a true multi-touch gesture via
|
|
101
|
+
// UiAutomation.dispatchGesture (API 24+).
|
|
102
|
+
message GesturePathRequest {
|
|
103
|
+
repeated GestureFingerPath paths = 1;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
message GesturePathResponse {}
|
|
107
|
+
|
|
79
108
|
message InputTextRequest {
|
|
80
109
|
string text = 1;
|
|
81
110
|
}
|