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.
@@ -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)
@@ -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) })