mobile-debug-mcp 0.18.0 → 0.19.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.
- package/dist/interact/index.js +164 -0
- package/dist/server.js +19 -0
- package/dist/utils/android/utils.js +110 -12
- package/dist/utils/cli/ios/preflight-ios.js +1 -1
- package/dist/utils/java.js +91 -34
- package/docs/CHANGELOG.md +9 -0
- package/docs/tools/interact.md +52 -0
- package/package.json +1 -1
- package/src/interact/index.ts +145 -0
- package/src/manage/android.ts +3 -3
- package/src/manage/ios.ts +3 -3
- package/src/server.ts +20 -3
- package/src/utils/android/utils.ts +90 -19
- package/src/utils/cli/ios/preflight-ios.ts +2 -2
- package/src/utils/java.ts +76 -30
- package/test/interact/device/observe_until_device.ts +24 -0
- package/test/interact/unit/observe_until.test.ts +76 -0
- package/test/unit/index.ts +1 -0
- package/test/utils/detect_java.test.ts +25 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ToolsInteract } from '../../../src/interact/index.js'
|
|
2
|
+
import * as Observe from '../../../src/observe/index.js'
|
|
3
|
+
|
|
4
|
+
async function runTests() {
|
|
5
|
+
console.log('Starting observe_until unit tests...')
|
|
6
|
+
|
|
7
|
+
const origFind = (ToolsInteract as any).findElementHandler
|
|
8
|
+
const origReadLog = (Observe as any).ToolsObserve.readLogStreamHandler
|
|
9
|
+
const origGetLogs = (Observe as any).ToolsObserve.getLogsHandler
|
|
10
|
+
const origGetFp = (Observe as any).ToolsObserve.getScreenFingerprintHandler
|
|
11
|
+
const origResolveObserve = (Observe as any).ToolsObserve.resolveObserve
|
|
12
|
+
const origGetScreenFp = (Observe as any).ToolsObserve.getScreenFingerprintHandler
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
// Timeout / snapshot case: ensure snapshot captured when condition not met
|
|
16
|
+
const origCapture = (Observe as any).ToolsObserve.captureDebugSnapshotHandler
|
|
17
|
+
;(Observe as any).ToolsObserve.captureDebugSnapshotHandler = async ({ reason }: any) => ({ reason, fingerprint: 'snap-123', ui_tree: null, logs: [] })
|
|
18
|
+
// make findElement always fail
|
|
19
|
+
(ToolsInteract as any).findElementHandler = async () => ({ found: false })
|
|
20
|
+
const resTimeout = await ToolsInteract.observeUntilHandler({ type: 'ui', query: 'WillNeverExist', timeoutMs: 500, pollIntervalMs: 100, platform: 'android' })
|
|
21
|
+
const okTimeout = resTimeout && !(resTimeout as any).success && (resTimeout as any).snapshot && (resTimeout as any).snapshot.fingerprint === 'snap-123' && (resTimeout as any).telemetry && (resTimeout as any).telemetry.pollCount > 0
|
|
22
|
+
console.log('Timeout Snapshot Test:', okTimeout ? 'PASS' : 'FAIL', JSON.stringify((resTimeout as any).telemetry || {}, null, 2))
|
|
23
|
+
;(Observe as any).ToolsObserve.captureDebugSnapshotHandler = origCapture
|
|
24
|
+
|
|
25
|
+
// UI condition: findElement returns found on 2nd call
|
|
26
|
+
let calls = 0
|
|
27
|
+
;(ToolsInteract as any).findElementHandler = async (args) => {
|
|
28
|
+
calls++
|
|
29
|
+
const query = (args && (args.query || args)) || ''
|
|
30
|
+
if (calls >= 2) return { found: true, element: { text: query } }
|
|
31
|
+
return { found: false }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const resUi = await ToolsInteract.observeUntilHandler({ type: 'ui', query: 'Generate Session', timeoutMs: 3000, pollIntervalMs: 100, platform: 'android' })
|
|
35
|
+
const okUi = resUi && (resUi as any).success && (resUi as any).telemetry && (resUi as any).telemetry.pollCount > 0 && (resUi as any).telemetry.timeToMatch >= 0
|
|
36
|
+
console.log('UI Test:', okUi ? 'PASS' : 'FAIL', JSON.stringify((resUi as any).telemetry || {}, null, 2))
|
|
37
|
+
|
|
38
|
+
// Log condition: stream empty, snapshot contains matching line
|
|
39
|
+
;(Observe as any).ToolsObserve.readLogStreamHandler = async () => ({ entries: [ { message: 'nothing' } ] })
|
|
40
|
+
let glCalls = 0
|
|
41
|
+
;(Observe as any).ToolsObserve.getLogsHandler = async () => {
|
|
42
|
+
glCalls++
|
|
43
|
+
if (glCalls === 1) return { device: {}, logs: ['INFO start'] }
|
|
44
|
+
return { device: {}, logs: ['INFO start', 'ERROR Exception occurred', 'Server: Boom'] }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const resLog = await ToolsInteract.observeUntilHandler({ type: 'log', query: 'Server', timeoutMs: 3000, pollIntervalMs: 100, platform: 'android' })
|
|
48
|
+
const okLog = resLog && (resLog as any).success && (resLog as any).telemetry && (resLog as any).telemetry.pollCount > 0 && (resLog as any).telemetry.matchSource === 'log-snapshot'
|
|
49
|
+
console.log('Log Test:', okLog ? 'PASS' : 'FAIL', JSON.stringify((resLog as any).telemetry || {}, null, 2))
|
|
50
|
+
|
|
51
|
+
// Screen condition: fingerprint changes after a few polls
|
|
52
|
+
let seq = ['A', 'A', 'B']
|
|
53
|
+
;(Observe as any).ToolsObserve.getScreenFingerprintHandler = async () => ({ fingerprint: seq.length ? seq.shift() : null })
|
|
54
|
+
const resScreen = await ToolsInteract.observeUntilHandler({ type: 'screen', timeoutMs: 3000, pollIntervalMs: 100, platform: 'android' })
|
|
55
|
+
const okScreen = resScreen && (resScreen as any).success && (resScreen as any).telemetry && (resScreen as any).telemetry.matchSource === 'screen-fingerprint'
|
|
56
|
+
console.log('Screen Test:', okScreen ? 'PASS' : 'FAIL', JSON.stringify((resScreen as any).telemetry || {}, null, 2))
|
|
57
|
+
|
|
58
|
+
// Idle condition: stable fingerprints observed
|
|
59
|
+
let idleSeq = ['X', 'X', 'X']
|
|
60
|
+
;(Observe as any).ToolsObserve.getScreenFingerprintHandler = async () => ({ fingerprint: idleSeq.length ? idleSeq.shift() : 'X' })
|
|
61
|
+
const resIdle = await ToolsInteract.observeUntilHandler({ type: 'idle', timeoutMs: 3000, pollIntervalMs: 100, platform: 'android' })
|
|
62
|
+
const okIdle = resIdle && (resIdle as any).success && (resIdle as any).telemetry && (resIdle as any).telemetry.matchSource === 'idle-stable'
|
|
63
|
+
console.log('Idle Test:', okIdle ? 'PASS' : 'FAIL', JSON.stringify((resIdle as any).telemetry || {}, null, 2))
|
|
64
|
+
|
|
65
|
+
} finally {
|
|
66
|
+
;(ToolsInteract as any).findElementHandler = origFind
|
|
67
|
+
;(Observe as any).ToolsObserve.readLogStreamHandler = origReadLog
|
|
68
|
+
;(Observe as any).ToolsObserve.getLogsHandler = origGetLogs
|
|
69
|
+
;(Observe as any).ToolsObserve.getScreenFingerprintHandler = origGetFp
|
|
70
|
+
;(Observe as any).ToolsObserve.resolveObserve = origResolveObserve
|
|
71
|
+
;(Observe as any).ToolsObserve.getScreenFingerprintHandler = origGetScreenFp
|
|
72
|
+
;(Observe as any).ToolsObserve.getScreenFingerprintHandler = origGetScreenFp
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
runTests().catch(console.error)
|
package/test/unit/index.ts
CHANGED
|
@@ -13,5 +13,6 @@ import '../manage/unit/mcp_disable_autodetect.test.ts'
|
|
|
13
13
|
import '../interact/unit/wait_for_screen_change.test.ts'
|
|
14
14
|
import '../observe/unit/capture_debug_snapshot.test.ts'
|
|
15
15
|
import '../observe/unit/find_element.test.ts'
|
|
16
|
+
import '../interact/unit/observe_until.test.ts'
|
|
16
17
|
|
|
17
18
|
console.log('Unit tests loaded.')
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { detectJavaHome } from '../../src/utils/java.js'
|
|
4
|
+
|
|
5
|
+
async function run() {
|
|
6
|
+
// Create a temporary fake JDK that reports Java 17
|
|
7
|
+
const tmp = fs.mkdtempSync('/tmp/fakejdk-')
|
|
8
|
+
const bin = path.join(tmp, 'bin')
|
|
9
|
+
fs.mkdirSync(bin)
|
|
10
|
+
const javaSh = path.join(bin, 'java')
|
|
11
|
+
fs.writeFileSync(javaSh, '#!/bin/sh\necho "openjdk version \"17.0.2\"" >&2\nexit 0\n')
|
|
12
|
+
fs.chmodSync(javaSh, 0o755)
|
|
13
|
+
|
|
14
|
+
process.env.ANDROID_STUDIO_JDK = tmp
|
|
15
|
+
|
|
16
|
+
const detected = await detectJavaHome()
|
|
17
|
+
console.log('DETECTED:', detected)
|
|
18
|
+
if (detected !== tmp) {
|
|
19
|
+
console.error('TEST FAIL: expected', tmp, 'got', detected)
|
|
20
|
+
process.exit(2)
|
|
21
|
+
}
|
|
22
|
+
console.log('TEST PASS')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
run().catch(e => { console.error(e); process.exit(1) })
|