@mobilewright/driver-mobilecli 0.0.1

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.
Files changed (45) hide show
  1. package/dist/driver.d.ts +37 -0
  2. package/dist/driver.d.ts.map +1 -0
  3. package/dist/driver.js +178 -0
  4. package/dist/driver.js.map +1 -0
  5. package/dist/index.d.ts +4 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +4 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/integration.test.d.ts +2 -0
  10. package/dist/integration.test.d.ts.map +1 -0
  11. package/dist/integration.test.js +36 -0
  12. package/dist/integration.test.js.map +1 -0
  13. package/dist/parsers/android-hierarchy-parser.d.ts +24 -0
  14. package/dist/parsers/android-hierarchy-parser.d.ts.map +1 -0
  15. package/dist/parsers/android-hierarchy-parser.js +78 -0
  16. package/dist/parsers/android-hierarchy-parser.js.map +1 -0
  17. package/dist/parsers/android-hierarchy-parser.test.d.ts +2 -0
  18. package/dist/parsers/android-hierarchy-parser.test.d.ts.map +1 -0
  19. package/dist/parsers/android-hierarchy-parser.test.js +107 -0
  20. package/dist/parsers/android-hierarchy-parser.test.js.map +1 -0
  21. package/dist/parsers/index.d.ts +3 -0
  22. package/dist/parsers/index.d.ts.map +1 -0
  23. package/dist/parsers/index.js +3 -0
  24. package/dist/parsers/index.js.map +1 -0
  25. package/dist/parsers/ios-hierarchy-parser.d.ts +22 -0
  26. package/dist/parsers/ios-hierarchy-parser.d.ts.map +1 -0
  27. package/dist/parsers/ios-hierarchy-parser.js +60 -0
  28. package/dist/parsers/ios-hierarchy-parser.js.map +1 -0
  29. package/dist/parsers/ios-hierarchy-parser.test.d.ts +2 -0
  30. package/dist/parsers/ios-hierarchy-parser.test.d.ts.map +1 -0
  31. package/dist/parsers/ios-hierarchy-parser.test.js +102 -0
  32. package/dist/parsers/ios-hierarchy-parser.test.js.map +1 -0
  33. package/dist/parsers/shared.d.ts +9 -0
  34. package/dist/parsers/shared.d.ts.map +1 -0
  35. package/dist/parsers/shared.js +94 -0
  36. package/dist/parsers/shared.js.map +1 -0
  37. package/dist/resolve-binary.d.ts +7 -0
  38. package/dist/resolve-binary.d.ts.map +1 -0
  39. package/dist/resolve-binary.js +29 -0
  40. package/dist/resolve-binary.js.map +1 -0
  41. package/dist/rpc-client.d.ts +31 -0
  42. package/dist/rpc-client.d.ts.map +1 -0
  43. package/dist/rpc-client.js +112 -0
  44. package/dist/rpc-client.js.map +1 -0
  45. package/package.json +27 -0
@@ -0,0 +1,37 @@
1
+ import type { AppInfo, ConnectionConfig, DeviceInfo, GestureSequence, HardwareButton, LaunchOptions, ListDevicesOptions, MobilewrightDriver, Orientation, RecordingOptions, RecordingResult, ScreenSize, ScreenshotOptions, Session, SwipeDirection, SwipeOptions, ViewNode } from '@mobilewright/protocol';
2
+ export declare const DEFAULT_URL = "ws://localhost:12000/ws";
3
+ export declare class MobilecliDriver implements MobilewrightDriver {
4
+ private session;
5
+ private readonly serverUrl;
6
+ constructor(opts?: {
7
+ url?: string;
8
+ });
9
+ connect(config: ConnectionConfig): Promise<Session>;
10
+ disconnect(): Promise<void>;
11
+ getViewHierarchy(): Promise<ViewNode[]>;
12
+ tap(x: number, y: number): Promise<void>;
13
+ doubleTap(x: number, y: number): Promise<void>;
14
+ longPress(x: number, y: number, duration?: number): Promise<void>;
15
+ typeText(text: string): Promise<void>;
16
+ swipe(direction: SwipeDirection, opts?: SwipeOptions): Promise<void>;
17
+ gesture(gestures: GestureSequence): Promise<void>;
18
+ pressButton(button: HardwareButton): Promise<void>;
19
+ screenshot(opts?: ScreenshotOptions): Promise<Buffer>;
20
+ getScreenSize(): Promise<ScreenSize>;
21
+ getOrientation(): Promise<Orientation>;
22
+ setOrientation(orientation: Orientation): Promise<void>;
23
+ startRecording(opts: RecordingOptions): Promise<void>;
24
+ stopRecording(): Promise<RecordingResult>;
25
+ launchApp(bundleId: string, opts?: LaunchOptions): Promise<void>;
26
+ terminateApp(bundleId: string): Promise<void>;
27
+ listApps(): Promise<AppInfo[]>;
28
+ getForegroundApp(): Promise<AppInfo>;
29
+ installApp(path: string): Promise<void>;
30
+ uninstallApp(bundleId: string): Promise<void>;
31
+ listDevices(opts?: ListDevicesOptions): Promise<DeviceInfo[]>;
32
+ openUrl(url: string): Promise<void>;
33
+ /** RPC call on the active session, auto-injecting deviceId. */
34
+ private call;
35
+ private requireSession;
36
+ }
37
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../src/driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,gBAAgB,EAChB,UAAU,EAGV,eAAe,EACf,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EAEX,gBAAgB,EAChB,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,OAAO,EACP,cAAc,EACd,YAAY,EACZ,QAAQ,EACT,MAAM,wBAAwB,CAAC;AAGhC,eAAO,MAAM,WAAW,4BAA4B,CAAC;AA6ErD,qBAAa,eAAgB,YAAW,kBAAkB;IACxD,OAAO,CAAC,OAAO,CAAyE;IACxF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,IAAI,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE;IAM7B,OAAO,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBnD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B,gBAAgB,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAKvC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9C,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,KAAK,CAAC,SAAS,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAUpE,OAAO,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAMlD,UAAU,CAAC,IAAI,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;IAWrD,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC;IAMpC,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IAKtC,cAAc,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAMvD,cAAc,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAOrD,aAAa,IAAI,OAAO,CAAC,eAAe,CAAC;IAMzC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhE,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C,QAAQ,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAS9B,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IASpC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C,WAAW,CAAC,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAsB7D,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzC,+DAA+D;IAC/D,OAAO,CAAC,IAAI;IAKZ,OAAO,CAAC,cAAc;CAIvB"}
package/dist/driver.js ADDED
@@ -0,0 +1,178 @@
1
+ import { RpcClient } from './rpc-client.js';
2
+ export const DEFAULT_URL = 'ws://localhost:12000/ws';
3
+ function elementToViewNode(el) {
4
+ const bounds = el.rect ?? { x: 0, y: 0, width: 0, height: 0 };
5
+ return {
6
+ type: el.type ?? 'Unknown',
7
+ label: el.label || undefined,
8
+ identifier: el.identifier || el.name || undefined,
9
+ value: el.value || undefined,
10
+ text: el.label || undefined,
11
+ isVisible: typeof el.visible === 'boolean' ? el.visible : bounds.width > 0 && bounds.height > 0,
12
+ isEnabled: el.enabled ?? true,
13
+ bounds,
14
+ children: el.children?.map(elementToViewNode) ?? [],
15
+ raw: el,
16
+ };
17
+ }
18
+ export class MobilecliDriver {
19
+ constructor(opts) {
20
+ this.session = null;
21
+ this.serverUrl = opts?.url ?? DEFAULT_URL;
22
+ }
23
+ // ─── Connection ──────────────────────────────────────────────
24
+ async connect(config) {
25
+ const url = config.url ?? this.serverUrl;
26
+ const rpc = new RpcClient(url, config.timeout);
27
+ await rpc.connect();
28
+ let platform;
29
+ if (config.platform) {
30
+ platform = config.platform;
31
+ }
32
+ else {
33
+ const result = await rpc.call('device.info', { deviceId: config.deviceId });
34
+ const info = result.device ?? result;
35
+ platform = info.platform?.toLowerCase() === 'android' ? 'android' : 'ios';
36
+ }
37
+ this.session = { deviceId: config.deviceId, platform, rpc };
38
+ return { deviceId: config.deviceId, platform };
39
+ }
40
+ async disconnect() {
41
+ this.requireSession().rpc.disconnect();
42
+ this.session = null;
43
+ }
44
+ // ─── Element Operations ──────────────────────────────────────
45
+ async getViewHierarchy() {
46
+ const result = await this.call('device.dump.ui');
47
+ return result.elements.map(elementToViewNode);
48
+ }
49
+ async tap(x, y) {
50
+ await this.call('device.io.tap', { x, y });
51
+ }
52
+ async doubleTap(x, y) {
53
+ await this.call('device.io.tap', { x, y });
54
+ await this.call('device.io.tap', { x, y });
55
+ }
56
+ async longPress(x, y, duration) {
57
+ await this.call('device.io.longpress', { x, y, ...(duration !== undefined && { duration }) });
58
+ }
59
+ async typeText(text) {
60
+ await this.call('device.io.text', { text });
61
+ }
62
+ async swipe(direction, opts) {
63
+ await this.call('device.io.swipe', {
64
+ direction,
65
+ ...(opts?.distance !== undefined && { distance: opts.distance }),
66
+ ...(opts?.duration !== undefined && { duration: opts.duration }),
67
+ ...(opts?.startX !== undefined && { startX: opts.startX }),
68
+ ...(opts?.startY !== undefined && { startY: opts.startY }),
69
+ });
70
+ }
71
+ async gesture(gestures) {
72
+ await this.call('device.io.gesture', { pointers: gestures.pointers });
73
+ }
74
+ async pressButton(button) {
75
+ await this.call('device.io.button', { button });
76
+ }
77
+ // ─── Screen Operations ───────────────────────────────────────
78
+ async screenshot(opts) {
79
+ const result = await this.call('device.screenshot', {
80
+ ...(opts?.format && { format: opts.format }),
81
+ ...(opts?.quality !== undefined && { quality: opts.quality }),
82
+ });
83
+ let b64 = result.data;
84
+ const commaIdx = b64.indexOf(',');
85
+ if (commaIdx !== -1)
86
+ b64 = b64.slice(commaIdx + 1);
87
+ return Buffer.from(b64, 'base64');
88
+ }
89
+ async getScreenSize() {
90
+ const result = await this.call('device.info');
91
+ const info = result.device;
92
+ return info.screenSize ?? { width: info.screenWidth ?? 0, height: info.screenHeight ?? 0 };
93
+ }
94
+ async getOrientation() {
95
+ const result = await this.call('device.io.orientation.get');
96
+ return result.orientation === 'landscape' ? 'landscape' : 'portrait';
97
+ }
98
+ async setOrientation(orientation) {
99
+ await this.call('device.io.orientation.set', { orientation });
100
+ }
101
+ // ─── Recording Operations ─────────────────────────────────────
102
+ async startRecording(opts) {
103
+ await this.call('device.screenrecord', {
104
+ output: opts.output,
105
+ ...(opts.timeLimit && { timeLimit: opts.timeLimit }),
106
+ });
107
+ }
108
+ async stopRecording() {
109
+ return this.call('device.screenrecord.stop');
110
+ }
111
+ // ─── App Operations ──────────────────────────────────────────
112
+ async launchApp(bundleId, opts) {
113
+ await this.call('device.apps.launch', {
114
+ bundleId,
115
+ ...(opts?.locale && { locale: opts.locale }),
116
+ });
117
+ }
118
+ async terminateApp(bundleId) {
119
+ await this.call('device.apps.terminate', { bundleId });
120
+ }
121
+ async listApps() {
122
+ const result = await this.call('device.apps.list');
123
+ return result.apps.map((app) => ({
124
+ bundleId: app.bundleId ?? app.packageName ?? '',
125
+ name: app.appName,
126
+ version: app.version,
127
+ }));
128
+ }
129
+ async getForegroundApp() {
130
+ const result = await this.call('device.apps.foreground');
131
+ return {
132
+ bundleId: result.bundleId ?? result.packageName ?? '',
133
+ name: result.appName,
134
+ version: result.version,
135
+ };
136
+ }
137
+ async installApp(path) {
138
+ await this.call('device.apps.install', { path });
139
+ }
140
+ async uninstallApp(bundleId) {
141
+ await this.call('device.apps.uninstall', { bundleId });
142
+ }
143
+ // ─── Device Operations ───────────────────────────────────────
144
+ async listDevices(opts) {
145
+ const rpc = new RpcClient(this.serverUrl);
146
+ await rpc.connect();
147
+ try {
148
+ const result = await rpc.call('devices.list', opts ? { ...opts } : undefined);
149
+ return result.devices.map((d) => ({
150
+ id: d.id ?? d.udid ?? '',
151
+ name: d.name,
152
+ platform: d.platform,
153
+ type: d.type,
154
+ state: d.state,
155
+ model: d.model,
156
+ osVersion: d.version,
157
+ }));
158
+ }
159
+ finally {
160
+ await rpc.disconnect();
161
+ }
162
+ }
163
+ async openUrl(url) {
164
+ await this.call('device.url', { url });
165
+ }
166
+ // ─── Helpers ─────────────────────────────────────────────────
167
+ /** RPC call on the active session, auto-injecting deviceId. */
168
+ call(method, params) {
169
+ const session = this.requireSession();
170
+ return session.rpc.call(method, { deviceId: session.deviceId, ...params });
171
+ }
172
+ requireSession() {
173
+ if (!this.session)
174
+ throw new Error('No active session. Call connect() first.');
175
+ return this.session;
176
+ }
177
+ }
178
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../src/driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { MobilecliDriver, DEFAULT_URL } from './driver.js';
2
+ export { resolveMobilecliBinary } from './resolve-binary.js';
3
+ export { RpcClient } from './rpc-client.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { MobilecliDriver, DEFAULT_URL } from './driver.js';
2
+ export { resolveMobilecliBinary } from './resolve-binary.js';
3
+ export { RpcClient } from './rpc-client.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.test.d.ts","sourceRoot":"","sources":["../src/integration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Integration test that connects to a real mobilecli server.
3
+ *
4
+ * Requires:
5
+ * - mobilecli server running on localhost:12000
6
+ * - A booted iOS simulator or Android emulator
7
+ * - MOBILEWRIGHT_INTEGRATION=1 environment variable
8
+ * - MOBILEWRIGHT_DEVICE_ID=<udid> environment variable
9
+ */
10
+ import { test, expect } from '@playwright/test';
11
+ import { MobilecliDriver } from './driver.js';
12
+ const INTEGRATION = process.env['MOBILEWRIGHT_INTEGRATION'] === '1';
13
+ const DEVICE_ID = process.env['MOBILEWRIGHT_DEVICE_ID'] ?? '';
14
+ test.describe('mobilecli integration', () => {
15
+ test.skip(!INTEGRATION, 'Requires MOBILEWRIGHT_INTEGRATION=1');
16
+ test('connects, dumps UI hierarchy, and takes a screenshot', async () => {
17
+ const driver = new MobilecliDriver();
18
+ const session = await driver.connect({
19
+ deviceId: DEVICE_ID,
20
+ });
21
+ expect(session.platform).toMatch(/^(ios|android)$/);
22
+ // Dump view hierarchy
23
+ const hierarchy = await driver.getViewHierarchy();
24
+ expect(hierarchy.length).toBeGreaterThan(0);
25
+ expect(hierarchy[0].type).toBeTruthy();
26
+ // Take screenshot
27
+ const screenshot = await driver.screenshot();
28
+ expect(screenshot).toBeInstanceOf(Buffer);
29
+ expect(screenshot.length).toBeGreaterThan(100);
30
+ // Get orientation
31
+ const orientation = await driver.getOrientation();
32
+ expect(orientation).toMatch(/^(portrait|landscape)$/);
33
+ await driver.disconnect();
34
+ });
35
+ });
36
+ //# sourceMappingURL=integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.test.js","sourceRoot":"","sources":["../src/integration.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,KAAK,GAAG,CAAC;AACpE,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,IAAI,EAAE,CAAC;AAE9D,IAAI,CAAC,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IAC1C,IAAI,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,qCAAqC,CAAC,CAAC;IAE/D,IAAI,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QAErC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;YACnC,QAAQ,EAAE,SAAS;SACpB,CAAC,CAAC;QAEH,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAEpD,sBAAsB;QACtB,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAClD,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;QAEvC,kBAAkB;QAClB,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;QAC7C,MAAM,CAAC,UAAU,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAE/C,kBAAkB;QAClB,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,cAAc,EAAE,CAAC;QAClD,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;QAEtD,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { ViewNode } from '@mobilewright/protocol';
2
+ /**
3
+ * Parses Android UIAutomator XML hierarchy into ViewNode[].
4
+ *
5
+ * UIAutomator XML looks like:
6
+ * <hierarchy rotation="0">
7
+ * <node index="0" text="" resource-id="" class="android.widget.FrameLayout"
8
+ * package="com.example" content-desc="" checkable="false" checked="false"
9
+ * clickable="false" enabled="true" focusable="false" focused="false"
10
+ * scrollable="false" long-clickable="false" password="false"
11
+ * selected="false" visible-to-user="true"
12
+ * bounds="[0,0][1080,1920]">
13
+ * <node ... />
14
+ * </node>
15
+ * </hierarchy>
16
+ *
17
+ * Key differences from iOS:
18
+ * - All elements are <node> tags; the type is in the "class" attribute
19
+ * - bounds format is "[left,top][right,bottom]"
20
+ * - content-desc is the accessibility label
21
+ * - resource-id is the identifier
22
+ */
23
+ export declare function parseAndroidHierarchy(xml: string): ViewNode[];
24
+ //# sourceMappingURL=android-hierarchy-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"android-hierarchy-parser.d.ts","sourceRoot":"","sources":["../../src/parsers/android-hierarchy-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGvD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,CAE7D"}
@@ -0,0 +1,78 @@
1
+ import { parseXmlHierarchy } from './shared.js';
2
+ /**
3
+ * Parses Android UIAutomator XML hierarchy into ViewNode[].
4
+ *
5
+ * UIAutomator XML looks like:
6
+ * <hierarchy rotation="0">
7
+ * <node index="0" text="" resource-id="" class="android.widget.FrameLayout"
8
+ * package="com.example" content-desc="" checkable="false" checked="false"
9
+ * clickable="false" enabled="true" focusable="false" focused="false"
10
+ * scrollable="false" long-clickable="false" password="false"
11
+ * selected="false" visible-to-user="true"
12
+ * bounds="[0,0][1080,1920]">
13
+ * <node ... />
14
+ * </node>
15
+ * </hierarchy>
16
+ *
17
+ * Key differences from iOS:
18
+ * - All elements are <node> tags; the type is in the "class" attribute
19
+ * - bounds format is "[left,top][right,bottom]"
20
+ * - content-desc is the accessibility label
21
+ * - resource-id is the identifier
22
+ */
23
+ export function parseAndroidHierarchy(xml) {
24
+ return parseXmlHierarchy(xml, 'hierarchy', androidNodeFromAttrs);
25
+ }
26
+ function androidNodeFromAttrs(_tagName, attrs) {
27
+ // Shorten class name: "android.widget.Button" -> "Button"
28
+ const fullClass = attrs.get('class') || 'View';
29
+ const type = fullClass.includes('.')
30
+ ? fullClass.split('.').pop()
31
+ : fullClass;
32
+ // resource-id often looks like "com.example:id/login_button"
33
+ // Extract just the id part after "/"
34
+ const rawResourceId = attrs.get('resource-id') || '';
35
+ const identifier = rawResourceId.includes('/')
36
+ ? rawResourceId.split('/').pop()
37
+ : rawResourceId || undefined;
38
+ const contentDesc = attrs.get('content-desc') || '';
39
+ const text = attrs.get('text') || '';
40
+ const raw = {};
41
+ for (const [k, v] of attrs) {
42
+ raw[k] = v;
43
+ }
44
+ return {
45
+ type,
46
+ label: contentDesc || undefined,
47
+ identifier: identifier || undefined,
48
+ value: undefined,
49
+ text: text || undefined,
50
+ placeholder: attrs.get('hint') || undefined,
51
+ isVisible: attrs.get('visible-to-user') !== 'false',
52
+ isEnabled: attrs.get('enabled') === 'true',
53
+ isSelected: attrs.get('selected') === 'true' ? true : undefined,
54
+ isFocused: attrs.get('focused') === 'true' ? true : undefined,
55
+ bounds: parseBounds(attrs.get('bounds') || ''),
56
+ children: [],
57
+ raw,
58
+ };
59
+ }
60
+ /**
61
+ * Parse Android bounds format "[left,top][right,bottom]" -> {x, y, width, height}
62
+ */
63
+ function parseBounds(boundsStr) {
64
+ const match = boundsStr.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/);
65
+ if (!match)
66
+ return { x: 0, y: 0, width: 0, height: 0 };
67
+ const left = Number(match[1]);
68
+ const top = Number(match[2]);
69
+ const right = Number(match[3]);
70
+ const bottom = Number(match[4]);
71
+ return {
72
+ x: left,
73
+ y: top,
74
+ width: right - left,
75
+ height: bottom - top,
76
+ };
77
+ }
78
+ //# sourceMappingURL=android-hierarchy-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"android-hierarchy-parser.js","sourceRoot":"","sources":["../../src/parsers/android-hierarchy-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=android-hierarchy-parser.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"android-hierarchy-parser.test.d.ts","sourceRoot":"","sources":["../../src/parsers/android-hierarchy-parser.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,107 @@
1
+ import { test, expect } from '@playwright/test';
2
+ import { parseAndroidHierarchy } from './android-hierarchy-parser.js';
3
+ const SAMPLE_ANDROID_XML = `
4
+ <hierarchy rotation="0">
5
+ <node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.example.app" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,1920]">
6
+ <node index="0" text="" resource-id="com.example.app:id/toolbar" class="android.widget.Toolbar" package="com.example.app" content-desc="Login" enabled="true" visible-to-user="true" bounds="[0,50][1080,150]">
7
+ <node index="0" text="Login" resource-id="" class="android.widget.TextView" package="com.example.app" content-desc="" enabled="true" visible-to-user="true" bounds="[40,60][200,140]"/>
8
+ </node>
9
+ <node index="1" text="" resource-id="com.example.app:id/email_input" class="android.widget.EditText" package="com.example.app" content-desc="Email" enabled="true" visible-to-user="true" focused="false" bounds="[20,200][1060,280]" hint="Enter email"/>
10
+ <node index="2" text="" resource-id="com.example.app:id/password_input" class="android.widget.EditText" package="com.example.app" content-desc="Password" enabled="true" visible-to-user="true" bounds="[20,300][1060,380]"/>
11
+ <node index="3" text="Sign In" resource-id="com.example.app:id/login_btn" class="android.widget.Button" package="com.example.app" content-desc="" enabled="true" visible-to-user="true" bounds="[20,420][1060,500]"/>
12
+ <node index="4" text="Forgot Password?" resource-id="com.example.app:id/forgot_link" class="android.widget.TextView" package="com.example.app" content-desc="" enabled="true" visible-to-user="false" bounds="[20,520][1060,570]"/>
13
+ <node index="5" text="" resource-id="com.example.app:id/remember_switch" class="android.widget.Switch" package="com.example.app" content-desc="Remember Me" enabled="true" visible-to-user="true" selected="false" bounds="[20,600][100,660]"/>
14
+ </node>
15
+ </hierarchy>`;
16
+ test.describe('parseAndroidHierarchy', () => {
17
+ test('parses root element correctly', () => {
18
+ const nodes = parseAndroidHierarchy(SAMPLE_ANDROID_XML);
19
+ expect(nodes).toHaveLength(1);
20
+ expect(nodes[0].type).toBe('FrameLayout');
21
+ });
22
+ test('builds correct tree structure', () => {
23
+ const nodes = parseAndroidHierarchy(SAMPLE_ANDROID_XML);
24
+ const root = nodes[0];
25
+ expect(root.children).toHaveLength(6); // Toolbar + 2 EditText + Button + TextView + Switch
26
+ });
27
+ test('shortens class names to simple type', () => {
28
+ const nodes = parseAndroidHierarchy(SAMPLE_ANDROID_XML);
29
+ const root = nodes[0];
30
+ const types = root.children.map((c) => c.type);
31
+ expect(types).toEqual([
32
+ 'Toolbar',
33
+ 'EditText',
34
+ 'EditText',
35
+ 'Button',
36
+ 'TextView',
37
+ 'Switch',
38
+ ]);
39
+ });
40
+ test('parses Android bounds format [left,top][right,bottom]', () => {
41
+ const nodes = parseAndroidHierarchy(SAMPLE_ANDROID_XML);
42
+ const root = nodes[0];
43
+ // Root: [0,0][1080,1920]
44
+ expect(root.bounds).toEqual({
45
+ x: 0,
46
+ y: 0,
47
+ width: 1080,
48
+ height: 1920,
49
+ });
50
+ // Email input: [20,200][1060,280]
51
+ const emailInput = root.children[1];
52
+ expect(emailInput.bounds).toEqual({
53
+ x: 20,
54
+ y: 200,
55
+ width: 1040,
56
+ height: 80,
57
+ });
58
+ });
59
+ test('extracts resource-id as identifier', () => {
60
+ const nodes = parseAndroidHierarchy(SAMPLE_ANDROID_XML);
61
+ const root = nodes[0];
62
+ const emailInput = root.children[1];
63
+ expect(emailInput.identifier).toBe('email_input');
64
+ const loginBtn = root.children[3];
65
+ expect(loginBtn.identifier).toBe('login_btn');
66
+ });
67
+ test('uses content-desc as label', () => {
68
+ const nodes = parseAndroidHierarchy(SAMPLE_ANDROID_XML);
69
+ const root = nodes[0];
70
+ const emailInput = root.children[1];
71
+ expect(emailInput.label).toBe('Email');
72
+ });
73
+ test('parses text property', () => {
74
+ const nodes = parseAndroidHierarchy(SAMPLE_ANDROID_XML);
75
+ const root = nodes[0];
76
+ const loginBtn = root.children[3];
77
+ expect(loginBtn.text).toBe('Sign In');
78
+ });
79
+ test('parses visible-to-user', () => {
80
+ const nodes = parseAndroidHierarchy(SAMPLE_ANDROID_XML);
81
+ const root = nodes[0];
82
+ const loginBtn = root.children[3];
83
+ expect(loginBtn.isVisible).toBe(true);
84
+ const forgotLink = root.children[4];
85
+ expect(forgotLink.isVisible).toBe(false);
86
+ });
87
+ test('handles nested children', () => {
88
+ const nodes = parseAndroidHierarchy(SAMPLE_ANDROID_XML);
89
+ const toolbar = nodes[0].children[0];
90
+ expect(toolbar.type).toBe('Toolbar');
91
+ expect(toolbar.label).toBe('Login');
92
+ expect(toolbar.children).toHaveLength(1);
93
+ expect(toolbar.children[0].type).toBe('TextView');
94
+ expect(toolbar.children[0].text).toBe('Login');
95
+ });
96
+ test('handles empty hierarchy', () => {
97
+ const nodes = parseAndroidHierarchy('<hierarchy rotation="0"/>');
98
+ expect(nodes).toHaveLength(0);
99
+ });
100
+ test('preserves raw attributes', () => {
101
+ const nodes = parseAndroidHierarchy(SAMPLE_ANDROID_XML);
102
+ const root = nodes[0];
103
+ expect(root.raw).toBeDefined();
104
+ expect(root.raw['package']).toBe('com.example.app');
105
+ });
106
+ });
107
+ //# sourceMappingURL=android-hierarchy-parser.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"android-hierarchy-parser.test.js","sourceRoot":"","sources":["../../src/parsers/android-hierarchy-parser.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { parseIosHierarchy } from './ios-hierarchy-parser.js';
2
+ export { parseAndroidHierarchy } from './android-hierarchy-parser.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parsers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { parseIosHierarchy } from './ios-hierarchy-parser.js';
2
+ export { parseAndroidHierarchy } from './android-hierarchy-parser.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parsers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { ViewNode } from '@mobilewright/protocol';
2
+ /**
3
+ * Parses iOS WDA (WebDriverAgent) XML hierarchy into ViewNode[].
4
+ *
5
+ * WDA XML looks like:
6
+ * <AppiumAUT>
7
+ * <XCUIElementTypeApplication type="XCUIElementTypeApplication"
8
+ * name="MyApp" label="MyApp" enabled="true" visible="true"
9
+ * accessible="false" x="0" y="0" width="390" height="844">
10
+ * <XCUIElementTypeWindow ...>
11
+ * <XCUIElementTypeButton name="loginButton" label="Log In"
12
+ * enabled="true" visible="true" x="20" y="100" width="350" height="44"/>
13
+ * </XCUIElementTypeWindow>
14
+ * </XCUIElementTypeApplication>
15
+ * </AppiumAUT>
16
+ *
17
+ * Each element tag IS the type (e.g. XCUIElementTypeButton).
18
+ * Attributes include: type, name, label, value, enabled, visible,
19
+ * accessible, x, y, width, height, index, selected, focused.
20
+ */
21
+ export declare function parseIosHierarchy(xml: string): ViewNode[];
22
+ //# sourceMappingURL=ios-hierarchy-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ios-hierarchy-parser.d.ts","sourceRoot":"","sources":["../../src/parsers/ios-hierarchy-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGvD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,CAEzD"}
@@ -0,0 +1,60 @@
1
+ import { parseXmlHierarchy } from './shared.js';
2
+ /**
3
+ * Parses iOS WDA (WebDriverAgent) XML hierarchy into ViewNode[].
4
+ *
5
+ * WDA XML looks like:
6
+ * <AppiumAUT>
7
+ * <XCUIElementTypeApplication type="XCUIElementTypeApplication"
8
+ * name="MyApp" label="MyApp" enabled="true" visible="true"
9
+ * accessible="false" x="0" y="0" width="390" height="844">
10
+ * <XCUIElementTypeWindow ...>
11
+ * <XCUIElementTypeButton name="loginButton" label="Log In"
12
+ * enabled="true" visible="true" x="20" y="100" width="350" height="44"/>
13
+ * </XCUIElementTypeWindow>
14
+ * </XCUIElementTypeApplication>
15
+ * </AppiumAUT>
16
+ *
17
+ * Each element tag IS the type (e.g. XCUIElementTypeButton).
18
+ * Attributes include: type, name, label, value, enabled, visible,
19
+ * accessible, x, y, width, height, index, selected, focused.
20
+ */
21
+ export function parseIosHierarchy(xml) {
22
+ return parseXmlHierarchy(xml, 'AppiumAUT', iosNodeFromAttrs);
23
+ }
24
+ function iosNodeFromAttrs(tagName, attrs) {
25
+ // Strip "XCUIElementType" prefix for cleaner type names
26
+ const type = tagName.startsWith('XCUIElementType')
27
+ ? tagName.slice('XCUIElementType'.length)
28
+ : tagName;
29
+ const raw = {};
30
+ for (const [k, v] of attrs) {
31
+ raw[k] = v;
32
+ }
33
+ return {
34
+ type,
35
+ label: attrs.get('label') || undefined,
36
+ identifier: attrs.get('name') || undefined,
37
+ value: attrs.get('value') || undefined,
38
+ text: undefined,
39
+ placeholder: attrs.get('placeholderValue') || undefined,
40
+ isVisible: attrs.get('visible') === 'true',
41
+ isEnabled: attrs.get('enabled') === 'true',
42
+ isSelected: attrs.get('selected') === 'true' ? true : undefined,
43
+ isFocused: attrs.get('focused') === 'true' ? true : undefined,
44
+ bounds: {
45
+ x: parseNumericAttribute(attrs.get('x')),
46
+ y: parseNumericAttribute(attrs.get('y')),
47
+ width: parseNumericAttribute(attrs.get('width')),
48
+ height: parseNumericAttribute(attrs.get('height')),
49
+ },
50
+ children: [],
51
+ raw,
52
+ };
53
+ }
54
+ function parseNumericAttribute(val) {
55
+ if (val === undefined)
56
+ return 0;
57
+ const n = Number(val);
58
+ return Number.isFinite(n) ? n : 0;
59
+ }
60
+ //# sourceMappingURL=ios-hierarchy-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ios-hierarchy-parser.js","sourceRoot":"","sources":["../../src/parsers/ios-hierarchy-parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,iBAAiB,CAAC,GAAG,EAAE,WAAW,EAAE,gBAAgB,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,gBAAgB,CACvB,OAAe,EACf,KAA0B;IAE1B,wDAAwD;IACxD,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,iBAAiB,CAAC;QAChD,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC;QACzC,CAAC,CAAC,OAAO,CAAC;IAEZ,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC;QAC3B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACb,CAAC;IAED,OAAO;QACL,IAAI;QACJ,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,SAAS;QACtC,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,SAAS;QAC1C,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,SAAS;QACtC,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,SAAS;QACvD,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,MAAM;QAC1C,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,MAAM;QAC1C,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;QAC/D,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;QAC7D,MAAM,EAAE;YACN,CAAC,EAAE,qBAAqB,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACxC,CAAC,EAAE,qBAAqB,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACxC,KAAK,EAAE,qBAAqB,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAChD,MAAM,EAAE,qBAAqB,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;SACnD;QACD,QAAQ,EAAE,EAAE;QACZ,GAAG;KACJ,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAAC,GAAuB;IACpD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IAChC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ios-hierarchy-parser.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ios-hierarchy-parser.test.d.ts","sourceRoot":"","sources":["../../src/parsers/ios-hierarchy-parser.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,102 @@
1
+ import { test, expect } from '@playwright/test';
2
+ import { parseIosHierarchy } from './ios-hierarchy-parser.js';
3
+ const SAMPLE_IOS_XML = `
4
+ <AppiumAUT>
5
+ <XCUIElementTypeApplication type="XCUIElementTypeApplication" name="TestApp" label="TestApp" enabled="true" visible="true" x="0" y="0" width="390" height="844">
6
+ <XCUIElementTypeWindow type="XCUIElementTypeWindow" enabled="true" visible="true" x="0" y="0" width="390" height="844">
7
+ <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" x="0" y="0" width="390" height="844">
8
+ <XCUIElementTypeNavigationBar type="XCUIElementTypeNavigationBar" name="Login" label="Login" enabled="true" visible="true" x="0" y="47" width="390" height="44">
9
+ <XCUIElementTypeStaticText type="XCUIElementTypeStaticText" name="Login" label="Login" enabled="true" visible="true" x="164" y="53" width="62" height="32"/>
10
+ </XCUIElementTypeNavigationBar>
11
+ <XCUIElementTypeTextField type="XCUIElementTypeTextField" name="emailField" label="Email" value="" enabled="true" visible="true" x="20" y="120" width="350" height="44" placeholderValue="Enter email"/>
12
+ <XCUIElementTypeSecureTextField type="XCUIElementTypeSecureTextField" name="passwordField" label="Password" value="" enabled="true" visible="true" x="20" y="180" width="350" height="44" placeholderValue="Enter password"/>
13
+ <XCUIElementTypeButton type="XCUIElementTypeButton" name="loginButton" label="Sign In" enabled="true" visible="true" x="20" y="250" width="350" height="50"/>
14
+ <XCUIElementTypeButton type="XCUIElementTypeButton" name="forgotPassword" label="Forgot Password?" enabled="true" visible="false" x="20" y="320" width="350" height="30"/>
15
+ <XCUIElementTypeSwitch type="XCUIElementTypeSwitch" name="rememberMe" label="Remember Me" value="0" enabled="true" visible="true" selected="false" x="20" y="370" width="51" height="31"/>
16
+ </XCUIElementTypeOther>
17
+ </XCUIElementTypeWindow>
18
+ </XCUIElementTypeApplication>
19
+ </AppiumAUT>`;
20
+ test.describe('parseIosHierarchy', () => {
21
+ test('parses root element correctly', () => {
22
+ const nodes = parseIosHierarchy(SAMPLE_IOS_XML);
23
+ expect(nodes).toHaveLength(1);
24
+ expect(nodes[0].type).toBe('Application');
25
+ expect(nodes[0].identifier).toBe('TestApp');
26
+ expect(nodes[0].label).toBe('TestApp');
27
+ });
28
+ test('builds correct tree structure', () => {
29
+ const nodes = parseIosHierarchy(SAMPLE_IOS_XML);
30
+ const app = nodes[0];
31
+ expect(app.children).toHaveLength(1); // Window
32
+ const window = app.children[0];
33
+ expect(window.type).toBe('Window');
34
+ expect(window.children).toHaveLength(1); // Other
35
+ const other = window.children[0];
36
+ expect(other.children).toHaveLength(6); // NavBar + TextField + SecureTextField + 2 Buttons + Switch
37
+ });
38
+ test('strips XCUIElementType prefix from type names', () => {
39
+ const nodes = parseIosHierarchy(SAMPLE_IOS_XML);
40
+ const other = nodes[0].children[0].children[0];
41
+ const types = other.children.map((c) => c.type);
42
+ expect(types).toEqual([
43
+ 'NavigationBar',
44
+ 'TextField',
45
+ 'SecureTextField',
46
+ 'Button',
47
+ 'Button',
48
+ 'Switch',
49
+ ]);
50
+ });
51
+ test('parses bounds correctly', () => {
52
+ const nodes = parseIosHierarchy(SAMPLE_IOS_XML);
53
+ const other = nodes[0].children[0].children[0];
54
+ const emailField = other.children[1]; // TextField
55
+ expect(emailField.bounds).toEqual({
56
+ x: 20,
57
+ y: 120,
58
+ width: 350,
59
+ height: 44,
60
+ });
61
+ });
62
+ test('parses visibility and enabled state', () => {
63
+ const nodes = parseIosHierarchy(SAMPLE_IOS_XML);
64
+ const other = nodes[0].children[0].children[0];
65
+ const loginButton = other.children[3]; // Sign In button
66
+ expect(loginButton.isVisible).toBe(true);
67
+ expect(loginButton.isEnabled).toBe(true);
68
+ const forgotPassword = other.children[4]; // Hidden button
69
+ expect(forgotPassword.isVisible).toBe(false);
70
+ });
71
+ test('parses accessibility identifiers and labels', () => {
72
+ const nodes = parseIosHierarchy(SAMPLE_IOS_XML);
73
+ const other = nodes[0].children[0].children[0];
74
+ const emailField = other.children[1];
75
+ expect(emailField.identifier).toBe('emailField');
76
+ expect(emailField.label).toBe('Email');
77
+ expect(emailField.placeholder).toBe('Enter email');
78
+ });
79
+ test('parses value and selected state', () => {
80
+ const nodes = parseIosHierarchy(SAMPLE_IOS_XML);
81
+ const other = nodes[0].children[0].children[0];
82
+ const toggle = other.children[5]; // Switch
83
+ expect(toggle.value).toBe('0');
84
+ expect(toggle.label).toBe('Remember Me');
85
+ });
86
+ test('preserves raw attributes', () => {
87
+ const nodes = parseIosHierarchy(SAMPLE_IOS_XML);
88
+ const other = nodes[0].children[0].children[0];
89
+ const emailField = other.children[1];
90
+ expect(emailField.raw).toBeDefined();
91
+ expect(emailField.raw['placeholderValue']).toBe('Enter email');
92
+ });
93
+ test('handles empty XML', () => {
94
+ const nodes = parseIosHierarchy('');
95
+ expect(nodes).toHaveLength(0);
96
+ });
97
+ test('handles self-closing AppiumAUT', () => {
98
+ const nodes = parseIosHierarchy('<AppiumAUT/>');
99
+ expect(nodes).toHaveLength(0);
100
+ });
101
+ });
102
+ //# sourceMappingURL=ios-hierarchy-parser.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ios-hierarchy-parser.test.js","sourceRoot":"","sources":["../../src/parsers/ios-hierarchy-parser.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ViewNode } from '@mobilewright/protocol';
2
+ /**
3
+ * Walk XML as a sequence of open/close/self-closing tags, calling
4
+ * `createNode` for each non-wrapper element. Handles wrapper elements
5
+ * (like AppiumAUT, hierarchy) by promoting their children.
6
+ */
7
+ export declare function parseXmlHierarchy(xml: string, wrapperTag: string, createNode: (tagName: string, attrs: Map<string, string>) => ViewNode): ViewNode[];
8
+ export declare function parseAttributes(attrString: string): Map<string, string>;
9
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/parsers/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,QAAQ,GACpE,QAAQ,EAAE,CAqEZ;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQvE"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Walk XML as a sequence of open/close/self-closing tags, calling
3
+ * `createNode` for each non-wrapper element. Handles wrapper elements
4
+ * (like AppiumAUT, hierarchy) by promoting their children.
5
+ */
6
+ export function parseXmlHierarchy(xml, wrapperTag, createNode) {
7
+ const nodes = [];
8
+ const stack = [];
9
+ const tagRegex = /<(\/?)(\w+)((?:\s+[\w:-]+="[^"]*")*)\s*(\/?)>/g;
10
+ let match;
11
+ while ((match = tagRegex.exec(xml)) !== null) {
12
+ const [, isClose, tagName, attrString, isSelfClose] = match;
13
+ if (isClose) {
14
+ const node = stack.pop();
15
+ if (node) {
16
+ const parent = stack[stack.length - 1];
17
+ if (parent) {
18
+ parent.children.push(node);
19
+ }
20
+ else {
21
+ nodes.push(node);
22
+ }
23
+ }
24
+ continue;
25
+ }
26
+ // Skip wrapper elements
27
+ if (tagName === wrapperTag) {
28
+ if (!isSelfClose) {
29
+ stack.push({
30
+ type: '__wrapper__',
31
+ isVisible: true,
32
+ isEnabled: true,
33
+ bounds: { x: 0, y: 0, width: 0, height: 0 },
34
+ children: [],
35
+ });
36
+ }
37
+ continue;
38
+ }
39
+ const attrs = parseAttributes(attrString);
40
+ const node = createNode(tagName, attrs);
41
+ if (isSelfClose) {
42
+ const parent = stack[stack.length - 1];
43
+ if (parent) {
44
+ parent.children.push(node);
45
+ }
46
+ else {
47
+ nodes.push(node);
48
+ }
49
+ }
50
+ else {
51
+ stack.push(node);
52
+ }
53
+ }
54
+ // Drain any remaining stack (shouldn't happen with valid XML)
55
+ while (stack.length > 0) {
56
+ const node = stack.pop();
57
+ if (node.type === '__wrapper__') {
58
+ nodes.push(...node.children);
59
+ }
60
+ else {
61
+ const parent = stack[stack.length - 1];
62
+ if (parent) {
63
+ parent.children.push(node);
64
+ }
65
+ else {
66
+ nodes.push(node);
67
+ }
68
+ }
69
+ }
70
+ return unwrapWrappers(nodes);
71
+ }
72
+ export function parseAttributes(attrString) {
73
+ const attrs = new Map();
74
+ const attrRegex = /([\w:-]+)="([^"]*)"/g;
75
+ let attrMatch;
76
+ while ((attrMatch = attrRegex.exec(attrString)) !== null) {
77
+ attrs.set(attrMatch[1], attrMatch[2]);
78
+ }
79
+ return attrs;
80
+ }
81
+ function unwrapWrappers(nodes) {
82
+ const result = [];
83
+ for (const node of nodes) {
84
+ if (node.type === '__wrapper__') {
85
+ result.push(...unwrapWrappers(node.children));
86
+ }
87
+ else {
88
+ node.children = unwrapWrappers(node.children);
89
+ result.push(node);
90
+ }
91
+ }
92
+ return result;
93
+ }
94
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/parsers/shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Walk up from cwd looking for `node_modules/@mobilenext/mobilecli`.
3
+ * Uses cwd instead of import.meta.url so it works when Playwright
4
+ * transpiles the source to CJS.
5
+ */
6
+ export declare function resolveMobilecliBinary(): string;
7
+ //# sourceMappingURL=resolve-binary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-binary.d.ts","sourceRoot":"","sources":["../src/resolve-binary.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAkB/C"}
@@ -0,0 +1,29 @@
1
+ import { join, dirname } from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ /**
4
+ * Walk up from cwd looking for `node_modules/@mobilenext/mobilecli`.
5
+ * Uses cwd instead of import.meta.url so it works when Playwright
6
+ * transpiles the source to CJS.
7
+ */
8
+ export function resolveMobilecliBinary() {
9
+ let dir = process.cwd();
10
+ while (true) {
11
+ const candidate = join(dir, 'node_modules', '@mobilenext', 'mobilecli');
12
+ if (existsSync(join(candidate, 'package.json'))) {
13
+ const binary = process.platform === 'darwin'
14
+ ? (process.arch === 'arm64' ? 'mobilecli-darwin-arm64' : 'mobilecli-darwin-amd64')
15
+ : process.platform === 'linux'
16
+ ? (process.arch === 'arm64' ? 'mobilecli-linux-arm64' : 'mobilecli-linux-amd64')
17
+ : null;
18
+ if (!binary)
19
+ throw new Error(`Unsupported platform: ${process.platform}-${process.arch}`);
20
+ return join(candidate, 'bin', binary);
21
+ }
22
+ const parent = dirname(dir);
23
+ if (parent === dir)
24
+ break;
25
+ dir = parent;
26
+ }
27
+ throw new Error('@mobilenext/mobilecli is not installed');
28
+ }
29
+ //# sourceMappingURL=resolve-binary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-binary.js","sourceRoot":"","sources":["../src/resolve-binary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC;;;;GAIG;AACH,MAAM,UAAU,sBAAsB;IACpC,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACxB,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;QACxE,IAAI,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;YAChD,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,KAAK,QAAQ;gBAC1C,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,wBAAwB,CAAC;gBAClF,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;oBAC5B,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,uBAAuB,CAAC;oBAChF,CAAC,CAAC,IAAI,CAAC;YACX,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YAC1F,OAAO,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACxC,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,31 @@
1
+ export interface JsonRpcRequest {
2
+ jsonrpc: '2.0';
3
+ id: number;
4
+ method: string;
5
+ params?: Record<string, unknown>;
6
+ }
7
+ export interface JsonRpcResponse {
8
+ jsonrpc: '2.0';
9
+ id: number;
10
+ result?: unknown;
11
+ error?: {
12
+ code: number;
13
+ message: string;
14
+ data?: unknown;
15
+ };
16
+ }
17
+ export declare class RpcClient {
18
+ private url;
19
+ private requestTimeout;
20
+ private ws;
21
+ private nextId;
22
+ private pending;
23
+ private connectionPromise;
24
+ constructor(url: string, requestTimeout?: number);
25
+ connect(): Promise<void>;
26
+ call<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T>;
27
+ disconnect(): Promise<void>;
28
+ get isConnected(): boolean;
29
+ private handleMessage;
30
+ }
31
+ //# sourceMappingURL=rpc-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-client.d.ts","sourceRoot":"","sources":["../src/rpc-client.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC3D;AAQD,qBAAa,SAAS;IAOlB,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,cAAc;IAPxB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,iBAAiB,CAA8B;gBAG7C,GAAG,EAAE,MAAM,EACX,cAAc,SAAS;IAG3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA0CxB,IAAI,CAAC,CAAC,GAAG,OAAO,EACpB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,CAAC,CAAC;IAgCP,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjC,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,OAAO,CAAC,aAAa;CA8BtB"}
@@ -0,0 +1,112 @@
1
+ import WebSocket from 'ws';
2
+ export class RpcClient {
3
+ constructor(url, requestTimeout = 30_000) {
4
+ this.url = url;
5
+ this.requestTimeout = requestTimeout;
6
+ this.ws = null;
7
+ this.nextId = 1;
8
+ this.pending = new Map();
9
+ this.connectionPromise = null;
10
+ }
11
+ async connect() {
12
+ if (this.ws?.readyState === WebSocket.OPEN)
13
+ return;
14
+ if (this.connectionPromise)
15
+ return this.connectionPromise;
16
+ this.connectionPromise = new Promise((resolve, reject) => {
17
+ const ws = new WebSocket(this.url);
18
+ const timeout = setTimeout(() => {
19
+ ws.terminate();
20
+ reject(new Error(`Connection to ${this.url} timed out`));
21
+ }, this.requestTimeout);
22
+ ws.on('open', () => {
23
+ clearTimeout(timeout);
24
+ this.ws = ws;
25
+ this.connectionPromise = null;
26
+ resolve();
27
+ });
28
+ ws.on('error', (err) => {
29
+ clearTimeout(timeout);
30
+ this.connectionPromise = null;
31
+ reject(err);
32
+ });
33
+ ws.on('message', (data) => {
34
+ this.handleMessage(data);
35
+ });
36
+ ws.on('close', () => {
37
+ this.ws = null;
38
+ // Reject all pending requests
39
+ for (const [id, req] of this.pending) {
40
+ clearTimeout(req.timer);
41
+ req.reject(new Error('WebSocket connection closed'));
42
+ this.pending.delete(id);
43
+ }
44
+ });
45
+ });
46
+ return this.connectionPromise;
47
+ }
48
+ async call(method, params) {
49
+ await this.connect();
50
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
51
+ throw new Error('WebSocket is not connected');
52
+ }
53
+ const id = this.nextId++;
54
+ const request = {
55
+ jsonrpc: '2.0',
56
+ id,
57
+ method,
58
+ ...(params !== undefined && { params }),
59
+ };
60
+ return new Promise((resolve, reject) => {
61
+ const timer = setTimeout(() => {
62
+ this.pending.delete(id);
63
+ reject(new Error(`RPC call "${method}" timed out after ${this.requestTimeout}ms`));
64
+ }, this.requestTimeout);
65
+ this.pending.set(id, {
66
+ resolve: resolve,
67
+ reject,
68
+ timer,
69
+ });
70
+ this.ws.send(JSON.stringify(request));
71
+ });
72
+ }
73
+ async disconnect() {
74
+ if (this.ws) {
75
+ this.ws.close();
76
+ this.ws = null;
77
+ }
78
+ }
79
+ get isConnected() {
80
+ return this.ws?.readyState === WebSocket.OPEN;
81
+ }
82
+ handleMessage(data) {
83
+ let response;
84
+ try {
85
+ response = JSON.parse(data.toString());
86
+ }
87
+ catch {
88
+ return; // Ignore malformed messages
89
+ }
90
+ const pending = this.pending.get(response.id);
91
+ if (!pending)
92
+ return;
93
+ this.pending.delete(response.id);
94
+ clearTimeout(pending.timer);
95
+ if (response.error) {
96
+ // Surface the detail from mobilecli — response.error.data often
97
+ // contains the real error (e.g. "exit status 4") while
98
+ // response.error.message is just "Server error".
99
+ const detail = typeof response.error.data === 'string'
100
+ ? response.error.data
101
+ : response.error.message;
102
+ const err = new Error(detail);
103
+ err.code = response.error.code;
104
+ err.data = response.error.data;
105
+ pending.reject(err);
106
+ }
107
+ else {
108
+ pending.resolve(response.result);
109
+ }
110
+ }
111
+ }
112
+ //# sourceMappingURL=rpc-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-client.js","sourceRoot":"","sources":["../src/rpc-client.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@mobilewright/driver-mobilecli",
3
+ "version": "0.0.1",
4
+ "description": "mobilecli driver for Mobilewright",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsc -b",
16
+ "prepublishOnly": "tsc -b"
17
+ },
18
+ "files": ["dist"],
19
+ "dependencies": {
20
+ "@mobilewright/protocol": "^0.0.1",
21
+ "fast-xml-parser": "^5.0.0",
22
+ "ws": "^8.18.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/ws": "^8.5.0"
26
+ }
27
+ }