mobile-debug-mcp 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +244 -31
- package/dist/android/interact.js +106 -0
- package/dist/android/observe.js +313 -0
- package/dist/android/utils.js +82 -0
- package/dist/android.js +131 -1
- package/dist/ios/interact.js +135 -0
- package/dist/ios/observe.js +219 -0
- package/dist/ios/utils.js +114 -0
- package/dist/ios.js +134 -0
- package/dist/server.js +271 -27
- package/docs/CHANGELOG.md +11 -1
- package/package.json +2 -1
- package/smoke-test.ts +17 -10
- package/src/android/interact.ts +126 -0
- package/src/android/observe.ts +360 -0
- package/src/android/utils.ts +94 -0
- package/src/ios/interact.ts +153 -0
- package/src/ios/observe.ts +269 -0
- package/src/ios/utils.ts +133 -0
- package/src/server.ts +322 -28
- package/src/types.ts +71 -0
- package/test/run-real-test.js +24 -0
- package/test/wait_for_element_mock.js +113 -0
- package/test/wait_for_element_real.js +67 -0
- package/test-ui-tree.js +68 -0
- package/test-ui-tree.ts +76 -0
- package/tsconfig.json +2 -1
- package/src/android.ts +0 -222
- package/src/ios.ts +0 -243
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@ This server is designed with security in mind, using strict argument handling to
|
|
|
26
26
|
- Node.js >= 18
|
|
27
27
|
- Android SDK (`adb` in PATH) for Android support
|
|
28
28
|
- Xcode command-line tools (`xcrun simctl`) for iOS support
|
|
29
|
+
- **iOS Device Bridge (`idb`)** for iOS UI tree support
|
|
29
30
|
- Booted iOS simulator for iOS testing
|
|
30
31
|
|
|
31
32
|
---
|
|
@@ -34,7 +35,18 @@ This server is designed with security in mind, using strict argument handling to
|
|
|
34
35
|
|
|
35
36
|
You can install and use **Mobile Debug MCP** in one of two ways:
|
|
36
37
|
|
|
37
|
-
### 1.
|
|
38
|
+
### 1. Install Dependencies
|
|
39
|
+
|
|
40
|
+
**iOS Prerequisite (`idb`):**
|
|
41
|
+
To use the `get_ui_tree` tool on iOS, you must install Facebook's `idb`:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
brew tap facebook/fb
|
|
45
|
+
brew install idb-companion
|
|
46
|
+
pip3 install fb-idb
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 2. Clone the repository for local development
|
|
38
50
|
|
|
39
51
|
```bash
|
|
40
52
|
git clone https://github.com/YOUR_USERNAME/mobile-debug-mcp.git
|
|
@@ -45,7 +57,7 @@ npm run build
|
|
|
45
57
|
|
|
46
58
|
This option is suitable if you want to modify or contribute to the code.
|
|
47
59
|
|
|
48
|
-
###
|
|
60
|
+
### 3. Install via npm for standard use
|
|
49
61
|
|
|
50
62
|
```bash
|
|
51
63
|
npm install -g mobile-debug-mcp
|
|
@@ -55,27 +67,7 @@ This option installs the package globally for easy use without cloning the repo.
|
|
|
55
67
|
|
|
56
68
|
---
|
|
57
69
|
|
|
58
|
-
##
|
|
59
|
-
33.
|
|
60
|
-
34. The repository includes a smoke test script to verify end-to-end functionality on real devices or simulators.
|
|
61
|
-
35.
|
|
62
|
-
36. ```bash
|
|
63
|
-
37. # Run smoke test for Android
|
|
64
|
-
38. npx tsx smoke-test.ts android com.example.package
|
|
65
|
-
39.
|
|
66
|
-
40. # Run smoke test for iOS
|
|
67
|
-
41. npx tsx smoke-test.ts ios com.example.bundleid
|
|
68
|
-
42. ```
|
|
69
|
-
43.
|
|
70
|
-
44. The smoke test performs the following sequence:
|
|
71
|
-
45. 1. Starts the app
|
|
72
|
-
46. 2. Captures a screenshot
|
|
73
|
-
47. 3. Fetches logs
|
|
74
|
-
48. 4. Terminates the app
|
|
75
|
-
49.
|
|
76
|
-
50. ---
|
|
77
|
-
51.
|
|
78
|
-
52. ## MCP Server Configuration
|
|
70
|
+
## MCP Server Configuration
|
|
79
71
|
|
|
80
72
|
Example WebUI MCP config using `npx --yes` and environment variables:
|
|
81
73
|
|
|
@@ -110,7 +102,7 @@ All tools accept a JSON input payload and return a structured JSON response. **E
|
|
|
110
102
|
Launch a mobile app.
|
|
111
103
|
|
|
112
104
|
**Input:**
|
|
113
|
-
```
|
|
105
|
+
```jsonc
|
|
114
106
|
{
|
|
115
107
|
"platform": "android" | "ios",
|
|
116
108
|
"appId": "com.example.app", // Android package or iOS bundle ID (Required)
|
|
@@ -131,7 +123,7 @@ Launch a mobile app.
|
|
|
131
123
|
Fetch recent logs from the app or device.
|
|
132
124
|
|
|
133
125
|
**Input:**
|
|
134
|
-
```
|
|
126
|
+
```jsonc
|
|
135
127
|
{
|
|
136
128
|
"platform": "android" | "ios",
|
|
137
129
|
"appId": "com.example.app", // Optional: filter logs by app
|
|
@@ -155,7 +147,7 @@ Returns two content blocks:
|
|
|
155
147
|
Capture a screenshot of the current device screen.
|
|
156
148
|
|
|
157
149
|
**Input:**
|
|
158
|
-
```
|
|
150
|
+
```jsonc
|
|
159
151
|
{
|
|
160
152
|
"platform": "android" | "ios",
|
|
161
153
|
"deviceId": "emulator-5554" // Optional: target specific device
|
|
@@ -177,7 +169,7 @@ Returns two content blocks:
|
|
|
177
169
|
Terminate a running app.
|
|
178
170
|
|
|
179
171
|
**Input:**
|
|
180
|
-
```
|
|
172
|
+
```jsonc
|
|
181
173
|
{
|
|
182
174
|
"platform": "android" | "ios",
|
|
183
175
|
"appId": "com.example.app", // Android package or iOS bundle ID (Required)
|
|
@@ -197,7 +189,7 @@ Terminate a running app.
|
|
|
197
189
|
Restart an app (terminate then launch).
|
|
198
190
|
|
|
199
191
|
**Input:**
|
|
200
|
-
```
|
|
192
|
+
```jsonc
|
|
201
193
|
{
|
|
202
194
|
"platform": "android" | "ios",
|
|
203
195
|
"appId": "com.example.app", // Android package or iOS bundle ID (Required)
|
|
@@ -218,7 +210,7 @@ Restart an app (terminate then launch).
|
|
|
218
210
|
Clear app storage (reset to fresh install state).
|
|
219
211
|
|
|
220
212
|
**Input:**
|
|
221
|
-
```
|
|
213
|
+
```jsonc
|
|
222
214
|
{
|
|
223
215
|
"platform": "android" | "ios",
|
|
224
216
|
"appId": "com.example.app", // Android package or iOS bundle ID (Required)
|
|
@@ -234,6 +226,206 @@ Clear app storage (reset to fresh install state).
|
|
|
234
226
|
}
|
|
235
227
|
```
|
|
236
228
|
|
|
229
|
+
### get_ui_tree
|
|
230
|
+
Get the current UI hierarchy from the device. Returns a structured JSON representation of the screen content.
|
|
231
|
+
|
|
232
|
+
**Input:**
|
|
233
|
+
```jsonc
|
|
234
|
+
{
|
|
235
|
+
"platform": "android" | "ios",
|
|
236
|
+
"deviceId": "emulator-5554" // Optional
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Response:**
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"device": { /* device info */ },
|
|
244
|
+
"screen": "",
|
|
245
|
+
"resolution": { "width": 1080, "height": 1920 },
|
|
246
|
+
"elements": [
|
|
247
|
+
{
|
|
248
|
+
"text": "Login",
|
|
249
|
+
"contentDescription": null,
|
|
250
|
+
"type": "android.widget.Button",
|
|
251
|
+
"resourceId": "com.example:id/login_button",
|
|
252
|
+
"clickable": true,
|
|
253
|
+
"enabled": true,
|
|
254
|
+
"visible": true,
|
|
255
|
+
"bounds": [120,400,280,450],
|
|
256
|
+
"center": [200, 425],
|
|
257
|
+
"depth": 1,
|
|
258
|
+
"parentId": 0,
|
|
259
|
+
"children": []
|
|
260
|
+
}
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### get_current_screen
|
|
266
|
+
Get the currently visible activity on an Android device.
|
|
267
|
+
|
|
268
|
+
**Input:**
|
|
269
|
+
```jsonc
|
|
270
|
+
{
|
|
271
|
+
"deviceId": "emulator-5554" // Optional: target specific device
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Response:**
|
|
276
|
+
```json
|
|
277
|
+
{
|
|
278
|
+
"device": { /* device info */ },
|
|
279
|
+
"package": "com.example.app",
|
|
280
|
+
"activity": "com.example.app.LoginActivity",
|
|
281
|
+
"shortActivity": "LoginActivity"
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### wait_for_element
|
|
286
|
+
Wait until a UI element with matching text appears on screen or timeout is reached. Useful for handling loading states or transitions.
|
|
287
|
+
|
|
288
|
+
**Input:**
|
|
289
|
+
```jsonc
|
|
290
|
+
{
|
|
291
|
+
"platform": "android" | "ios",
|
|
292
|
+
"text": "Home", // Text to wait for
|
|
293
|
+
"timeout": 5000, // Max wait time in ms (default 10000)
|
|
294
|
+
"deviceId": "emulator-5554" // Optional
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**Response:**
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"device": { /* device info */ },
|
|
302
|
+
"found": true,
|
|
303
|
+
"element": { /* UIElement object if found */ }
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
If the element is not found within the timeout, `found` will be `false`. If a system error occurs (e.g., ADB failure), an `error` field will be present.
|
|
308
|
+
|
|
309
|
+
```json
|
|
310
|
+
{
|
|
311
|
+
"device": { /* device info */ },
|
|
312
|
+
"found": false,
|
|
313
|
+
"error": "Optional error message"
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### tap
|
|
318
|
+
Simulate a finger tap on the device screen at specific coordinates.
|
|
319
|
+
|
|
320
|
+
Platform support and constraints:
|
|
321
|
+
- Android: Implemented via `adb shell input tap` and works when `adb` is available in PATH or configured via `ADB_PATH`.
|
|
322
|
+
- iOS: Requires Facebook's `idb` tooling. The iOS implementation uses `idb` to deliver UI events and is simulator-oriented (works reliably on a booted simulator). Physical device support depends on `idb` capabilities and a running `idb_companion` on the target device; it may not work in all environments.
|
|
323
|
+
|
|
324
|
+
Prerequisites for iOS (if you intend to use tap on iOS):
|
|
325
|
+
```bash
|
|
326
|
+
brew tap facebook/fb
|
|
327
|
+
brew install idb-companion
|
|
328
|
+
pip3 install fb-idb
|
|
329
|
+
```
|
|
330
|
+
Ensure `idb` and `idb_companion` are in your PATH. If you use non-standard tool locations, set `XCRUN_PATH` and/or `ADB_PATH` environment variables as appropriate.
|
|
331
|
+
|
|
332
|
+
Behavior notes:
|
|
333
|
+
- The tool is a primitive input: it only sends a tap at the provided coordinates. It does not inspect or interpret the UI.
|
|
334
|
+
- If `idb` is missing or the simulator/device is not available, the tool will return an error explaining the failure.
|
|
335
|
+
|
|
336
|
+
**Input:**
|
|
337
|
+
```jsonc
|
|
338
|
+
{
|
|
339
|
+
"platform": "android" | "ios", // Optional, defaults to "android"
|
|
340
|
+
"x": 200, // X coordinate (Required)
|
|
341
|
+
"y": 400, // Y coordinate (Required)
|
|
342
|
+
"deviceId": "emulator-5554" // Optional
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Response:**
|
|
347
|
+
```json
|
|
348
|
+
{
|
|
349
|
+
"device": { /* device info */ },
|
|
350
|
+
"success": true,
|
|
351
|
+
"x": 200,
|
|
352
|
+
"y": 400
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
If the tap fails (e.g., missing `adb`/`idb`, device not found), `success` will be `false` and an `error` field will be present.
|
|
357
|
+
|
|
358
|
+
### swipe
|
|
359
|
+
Simulate a swipe gesture on an Android device.
|
|
360
|
+
|
|
361
|
+
**Input:**
|
|
362
|
+
```jsonc
|
|
363
|
+
{
|
|
364
|
+
"platform": "android", // Optional, defaults to "android"
|
|
365
|
+
"x1": 500, // Start X (Required)
|
|
366
|
+
"y1": 1500, // Start Y (Required)
|
|
367
|
+
"x2": 500, // End X (Required)
|
|
368
|
+
"y2": 500, // End Y (Required)
|
|
369
|
+
"duration": 300, // Duration in ms (Required)
|
|
370
|
+
"deviceId": "emulator-5554" // Optional
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**Response:**
|
|
375
|
+
```json
|
|
376
|
+
{
|
|
377
|
+
"device": { /* device info */ },
|
|
378
|
+
"success": true,
|
|
379
|
+
"start": [500, 1500],
|
|
380
|
+
"end": [500, 500],
|
|
381
|
+
"duration": 300
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
If the swipe fails, `success` will be `false` and an `error` field will be present.
|
|
386
|
+
|
|
387
|
+
### type_text
|
|
388
|
+
Type text into the currently focused input field on an Android device.
|
|
389
|
+
|
|
390
|
+
**Input:**
|
|
391
|
+
```jsonc
|
|
392
|
+
{
|
|
393
|
+
"platform": "android", // Optional, defaults to "android"
|
|
394
|
+
"text": "hello world", // Text to type (Required)
|
|
395
|
+
"deviceId": "emulator-5554" // Optional
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**Response:**
|
|
400
|
+
```json
|
|
401
|
+
{
|
|
402
|
+
"device": { /* device info */ },
|
|
403
|
+
"success": true,
|
|
404
|
+
"text": "hello world"
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
If the command fails, `success` will be `false` and an `error` field will be present.
|
|
409
|
+
|
|
410
|
+
### press_back
|
|
411
|
+
Simulate pressing the Android Back button.
|
|
412
|
+
|
|
413
|
+
**Input:**
|
|
414
|
+
```jsonc
|
|
415
|
+
{
|
|
416
|
+
"platform": "android", // Optional
|
|
417
|
+
"deviceId": "emulator-5554" // Optional
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
**Response:**
|
|
422
|
+
```json
|
|
423
|
+
{
|
|
424
|
+
"device": { /* device info */ },
|
|
425
|
+
"success": true
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
237
429
|
---
|
|
238
430
|
|
|
239
431
|
## Recommended Workflow
|
|
@@ -242,8 +434,9 @@ Clear app storage (reset to fresh install state).
|
|
|
242
434
|
2. Use `start_app` to launch the app.
|
|
243
435
|
3. Use `get_logs` to read the latest logs.
|
|
244
436
|
4. Use `capture_screenshot` to visually inspect the app if needed.
|
|
245
|
-
5. Use `
|
|
246
|
-
6. Use `
|
|
437
|
+
5. Use `wait_for_element` to ensure the app is in the expected state before proceeding (e.g., after login).
|
|
438
|
+
6. Use `reset_app_data` to clear state if debugging fresh install scenarios.
|
|
439
|
+
7. Use `restart_app` to quickly reboot the app during development cycles.
|
|
247
440
|
|
|
248
441
|
---
|
|
249
442
|
|
|
@@ -255,6 +448,26 @@ Clear app storage (reset to fresh install state).
|
|
|
255
448
|
|
|
256
449
|
---
|
|
257
450
|
|
|
451
|
+
## Testing
|
|
452
|
+
|
|
453
|
+
The repository includes a smoke test script to verify end-to-end functionality on real devices or simulators.
|
|
454
|
+
|
|
455
|
+
```bash
|
|
456
|
+
# Run smoke test for Android
|
|
457
|
+
npx tsx smoke-test.ts android com.example.package
|
|
458
|
+
|
|
459
|
+
# Run smoke test for iOS
|
|
460
|
+
npx tsx smoke-test.ts ios com.example.bundleid
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
The smoke test performs the following sequence:
|
|
464
|
+
1. Starts the app
|
|
465
|
+
2. Captures a screenshot
|
|
466
|
+
3. Fetches logs
|
|
467
|
+
4. Terminates the app
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
258
471
|
## License
|
|
259
472
|
|
|
260
473
|
MIT License
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { execAdb, getAndroidDeviceMetadata, getDeviceInfo } from "./utils.js";
|
|
2
|
+
import { AndroidObserve } from "./observe.js";
|
|
3
|
+
export class AndroidInteract {
|
|
4
|
+
observe = new AndroidObserve();
|
|
5
|
+
async waitForElement(text, timeout, deviceId) {
|
|
6
|
+
const metadata = await getAndroidDeviceMetadata("", deviceId);
|
|
7
|
+
const deviceInfo = getDeviceInfo(deviceId || 'default', metadata);
|
|
8
|
+
const startTime = Date.now();
|
|
9
|
+
while (Date.now() - startTime < timeout) {
|
|
10
|
+
try {
|
|
11
|
+
const tree = await this.observe.getUITree(deviceId);
|
|
12
|
+
if (tree.error) {
|
|
13
|
+
return { device: deviceInfo, found: false, error: tree.error };
|
|
14
|
+
}
|
|
15
|
+
const element = tree.elements.find(e => e.text === text);
|
|
16
|
+
if (element) {
|
|
17
|
+
return { device: deviceInfo, found: true, element };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
// Ignore errors during polling and retry
|
|
22
|
+
console.error("Error polling UI tree:", e);
|
|
23
|
+
}
|
|
24
|
+
const elapsed = Date.now() - startTime;
|
|
25
|
+
const remaining = timeout - elapsed;
|
|
26
|
+
if (remaining <= 0)
|
|
27
|
+
break;
|
|
28
|
+
await new Promise(resolve => setTimeout(resolve, Math.min(500, remaining)));
|
|
29
|
+
}
|
|
30
|
+
return { device: deviceInfo, found: false };
|
|
31
|
+
}
|
|
32
|
+
async tap(x, y, deviceId) {
|
|
33
|
+
const metadata = await getAndroidDeviceMetadata("", deviceId);
|
|
34
|
+
const deviceInfo = getDeviceInfo(deviceId || 'default', metadata);
|
|
35
|
+
try {
|
|
36
|
+
await execAdb(['shell', 'input', 'tap', x.toString(), y.toString()], deviceId);
|
|
37
|
+
return { device: deviceInfo, success: true, x, y };
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
return { device: deviceInfo, success: false, x, y, error: e instanceof Error ? e.message : String(e) };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async swipe(x1, y1, x2, y2, duration, deviceId) {
|
|
44
|
+
const metadata = await getAndroidDeviceMetadata("", deviceId);
|
|
45
|
+
const deviceInfo = getDeviceInfo(deviceId || 'default', metadata);
|
|
46
|
+
try {
|
|
47
|
+
await execAdb(['shell', 'input', 'swipe', x1.toString(), y1.toString(), x2.toString(), y2.toString(), duration.toString()], deviceId);
|
|
48
|
+
return { device: deviceInfo, success: true, start: [x1, y1], end: [x2, y2], duration };
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
return { device: deviceInfo, success: false, start: [x1, y1], end: [x2, y2], duration, error: e instanceof Error ? e.message : String(e) };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async typeText(text, deviceId) {
|
|
55
|
+
const metadata = await getAndroidDeviceMetadata("", deviceId);
|
|
56
|
+
const deviceInfo = getDeviceInfo(deviceId || 'default', metadata);
|
|
57
|
+
try {
|
|
58
|
+
// Encode spaces as %s to ensure proper input handling by adb shell input text
|
|
59
|
+
const encodedText = text.replace(/\s/g, '%s');
|
|
60
|
+
// Note: 'input text' might fail with some characters or if keyboard isn't ready, but it's the standard ADB way.
|
|
61
|
+
await execAdb(['shell', 'input', 'text', encodedText], deviceId);
|
|
62
|
+
return { device: deviceInfo, success: true, text };
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
return { device: deviceInfo, success: false, text, error: e instanceof Error ? e.message : String(e) };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async pressBack(deviceId) {
|
|
69
|
+
const metadata = await getAndroidDeviceMetadata("", deviceId);
|
|
70
|
+
const deviceInfo = getDeviceInfo(deviceId || 'default', metadata);
|
|
71
|
+
try {
|
|
72
|
+
await execAdb(['shell', 'input', 'keyevent', '4'], deviceId);
|
|
73
|
+
return { device: deviceInfo, success: true };
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
return { device: deviceInfo, success: false, error: e instanceof Error ? e.message : String(e) };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async startApp(appId, deviceId) {
|
|
80
|
+
const metadata = await getAndroidDeviceMetadata(appId, deviceId);
|
|
81
|
+
const deviceInfo = getDeviceInfo(deviceId || 'default', metadata);
|
|
82
|
+
await execAdb(['shell', 'monkey', '-p', appId, '-c', 'android.intent.category.LAUNCHER', '1'], deviceId);
|
|
83
|
+
return { device: deviceInfo, appStarted: true, launchTimeMs: 1000 };
|
|
84
|
+
}
|
|
85
|
+
async terminateApp(appId, deviceId) {
|
|
86
|
+
const metadata = await getAndroidDeviceMetadata(appId, deviceId);
|
|
87
|
+
const deviceInfo = getDeviceInfo(deviceId || 'default', metadata);
|
|
88
|
+
await execAdb(['shell', 'am', 'force-stop', appId], deviceId);
|
|
89
|
+
return { device: deviceInfo, appTerminated: true };
|
|
90
|
+
}
|
|
91
|
+
async restartApp(appId, deviceId) {
|
|
92
|
+
await this.terminateApp(appId, deviceId);
|
|
93
|
+
const startResult = await this.startApp(appId, deviceId);
|
|
94
|
+
return {
|
|
95
|
+
device: startResult.device,
|
|
96
|
+
appRestarted: startResult.appStarted,
|
|
97
|
+
launchTimeMs: startResult.launchTimeMs
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async resetAppData(appId, deviceId) {
|
|
101
|
+
const metadata = await getAndroidDeviceMetadata(appId, deviceId);
|
|
102
|
+
const deviceInfo = getDeviceInfo(deviceId || 'default', metadata);
|
|
103
|
+
const output = await execAdb(['shell', 'pm', 'clear', appId], deviceId);
|
|
104
|
+
return { device: deviceInfo, dataCleared: output === 'Success' };
|
|
105
|
+
}
|
|
106
|
+
}
|