mobile-debug-mcp 0.21.0 → 0.21.2
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/android.js +0 -27
- package/dist/interact/index.js +23 -38
- package/dist/interact/ios.js +0 -26
- package/dist/observe/android.js +85 -21
- package/dist/observe/ios.js +50 -1
- package/dist/server.js +16 -39
- package/dist/utils/image.js +18 -0
- package/dist/utils/resolve-device.js +5 -0
- package/docs/CHANGELOG.md +8 -2
- package/docs/tools/interact.md +7 -27
- package/package.json +4 -3
- package/src/interact/android.ts +1 -32
- package/src/interact/index.ts +38 -46
- package/src/interact/ios.ts +1 -31
- package/src/observe/android.ts +84 -20
- package/src/observe/ios.ts +54 -3
- package/src/server.ts +17 -39
- package/src/types.ts +6 -0
- package/src/utils/image.ts +14 -0
- package/src/utils/resolve-device.ts +6 -0
- package/test/interact/device/run-real-test.ts +3 -19
- package/test/interact/unit/{observe_until.test.ts → wait_for_ui.test.ts} +6 -6
- package/test/observe/device/wait_for_element_real.ts +3 -80
- package/test/observe/unit/wait_for_element_mock.ts +2 -104
- package/test/observe/unit/{observe_until_edge_cases.test.ts → wait_for_ui_edge_cases.test.ts} +5 -5
- package/test/observe/unit/{observe_until_stability.test.ts → wait_for_ui_stability.test.ts} +3 -3
- package/test/unit/index.ts +27 -15
|
@@ -2,7 +2,7 @@ import { ToolsInteract } from '../../../src/interact/index.js'
|
|
|
2
2
|
import * as Observe from '../../../src/observe/index.js'
|
|
3
3
|
|
|
4
4
|
async function runTests() {
|
|
5
|
-
console.log('Starting
|
|
5
|
+
console.log('Starting wait_for_ui unit tests...')
|
|
6
6
|
|
|
7
7
|
const origFind = (ToolsInteract as any).findElementHandler
|
|
8
8
|
const origReadLog = (Observe as any).ToolsObserve.readLogStreamHandler
|
|
@@ -17,7 +17,7 @@ async function runTests() {
|
|
|
17
17
|
;(Observe as any).ToolsObserve.captureDebugSnapshotHandler = async ({ reason }: any) => ({ reason, fingerprint: 'snap-123', ui_tree: null, logs: [] })
|
|
18
18
|
// make findElement always fail
|
|
19
19
|
(ToolsInteract as any).findElementHandler = async () => ({ found: false })
|
|
20
|
-
const resTimeout = await ToolsInteract.
|
|
20
|
+
const resTimeout = await ToolsInteract.waitForUIHandler({ type: 'ui', query: 'WillNeverExist', timeoutMs: 500, pollIntervalMs: 100, platform: 'android' })
|
|
21
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
22
|
console.log('Timeout Snapshot Test:', okTimeout ? 'PASS' : 'FAIL', JSON.stringify((resTimeout as any).telemetry || {}, null, 2))
|
|
23
23
|
;(Observe as any).ToolsObserve.captureDebugSnapshotHandler = origCapture
|
|
@@ -31,7 +31,7 @@ async function runTests() {
|
|
|
31
31
|
return { found: false }
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const resUi = await ToolsInteract.
|
|
34
|
+
const resUi = await ToolsInteract.waitForUIHandler({ type: 'ui', query: 'Generate Session', timeoutMs: 3000, pollIntervalMs: 100, platform: 'android' })
|
|
35
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
36
|
console.log('UI Test:', okUi ? 'PASS' : 'FAIL', JSON.stringify((resUi as any).telemetry || {}, null, 2))
|
|
37
37
|
|
|
@@ -44,21 +44,21 @@ async function runTests() {
|
|
|
44
44
|
return { device: {}, logs: ['INFO start', 'ERROR Exception occurred', 'Server: Boom'] }
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
const resLog = await ToolsInteract.
|
|
47
|
+
const resLog = await ToolsInteract.waitForUIHandler({ type: 'log', query: 'Server', timeoutMs: 3000, pollIntervalMs: 100, platform: 'android' })
|
|
48
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
49
|
console.log('Log Test:', okLog ? 'PASS' : 'FAIL', JSON.stringify((resLog as any).telemetry || {}, null, 2))
|
|
50
50
|
|
|
51
51
|
// Screen condition: fingerprint changes after a few polls
|
|
52
52
|
let seq = ['A', 'A', 'B']
|
|
53
53
|
;(Observe as any).ToolsObserve.getScreenFingerprintHandler = async () => ({ fingerprint: seq.length ? seq.shift() : null })
|
|
54
|
-
const resScreen = await ToolsInteract.
|
|
54
|
+
const resScreen = await ToolsInteract.waitForUIHandler({ type: 'screen', timeoutMs: 3000, pollIntervalMs: 100, platform: 'android' })
|
|
55
55
|
const okScreen = resScreen && (resScreen as any).success && (resScreen as any).telemetry && (resScreen as any).telemetry.matchSource === 'screen-fingerprint'
|
|
56
56
|
console.log('Screen Test:', okScreen ? 'PASS' : 'FAIL', JSON.stringify((resScreen as any).telemetry || {}, null, 2))
|
|
57
57
|
|
|
58
58
|
// Idle condition: stable fingerprints observed
|
|
59
59
|
let idleSeq = ['X', 'X', 'X']
|
|
60
60
|
;(Observe as any).ToolsObserve.getScreenFingerprintHandler = async () => ({ fingerprint: idleSeq.length ? idleSeq.shift() : 'X' })
|
|
61
|
-
const resIdle = await ToolsInteract.
|
|
61
|
+
const resIdle = await ToolsInteract.waitForUIHandler({ type: 'idle', timeoutMs: 3000, pollIntervalMs: 100, platform: 'android' })
|
|
62
62
|
const okIdle = resIdle && (resIdle as any).success && (resIdle as any).telemetry && (resIdle as any).telemetry.matchSource === 'idle-stable'
|
|
63
63
|
console.log('Idle Test:', okIdle ? 'PASS' : 'FAIL', JSON.stringify((resIdle as any).telemetry || {}, null, 2))
|
|
64
64
|
|
|
@@ -1,80 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// Usage: npx tsx test/wait_for_element_real.ts <deviceId> <appId>
|
|
5
|
-
const args = process.argv.slice(2);
|
|
6
|
-
const DEVICE_ID = args[0] || process.env.DEVICE_ID;
|
|
7
|
-
const APP_ID = args[1] || process.env.APP_ID;
|
|
8
|
-
|
|
9
|
-
if (!DEVICE_ID || !APP_ID) {
|
|
10
|
-
console.error("Usage: npx tsx test/wait_for_element_real.ts <deviceId> <appId> or set DEVICE_ID and APP_ID env vars");
|
|
11
|
-
process.exit(1);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async function runRealTest() {
|
|
15
|
-
console.log(`Connecting to device ${DEVICE_ID}...`);
|
|
16
|
-
const interact = new AndroidInteract();
|
|
17
|
-
const observe = new AndroidObserve();
|
|
18
|
-
try {
|
|
19
|
-
console.log(`\nStarting app ${APP_ID}...`);
|
|
20
|
-
await interact.startApp(APP_ID, DEVICE_ID);
|
|
21
|
-
console.log("Waiting 3s for app to render...");
|
|
22
|
-
await new Promise(r => setTimeout(r, 3000));
|
|
23
|
-
|
|
24
|
-
console.log("\nFetching UI Tree to find a target text...");
|
|
25
|
-
const tree = await observe.getUITree(DEVICE_ID);
|
|
26
|
-
if (tree.error) {
|
|
27
|
-
console.error("Failed to get UI Tree:", tree.error);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const targetElement = tree.elements.find(e => e.text && e.text.length > 0 && e.visible);
|
|
32
|
-
if (!targetElement || !targetElement.text) {
|
|
33
|
-
console.warn("No visible text elements found on screen to test with.");
|
|
34
|
-
console.log("Elements found:", tree.elements.length);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const targetText = targetElement.text;
|
|
39
|
-
console.log(`Found target element: "${targetText}"`);
|
|
40
|
-
|
|
41
|
-
console.log(`\nTest 1: Waiting for existing element "${targetText}" (should succeed)...`);
|
|
42
|
-
const start1 = Date.now();
|
|
43
|
-
const result1 = await interact.waitForElement(targetText, 5000, DEVICE_ID);
|
|
44
|
-
const elapsed1 = Date.now() - start1;
|
|
45
|
-
console.log(`Result: ${result1.found ? "PASS" : "FAIL"}`);
|
|
46
|
-
console.log(`Found Element: ${result1.element?.text}`);
|
|
47
|
-
console.log(`Time taken: ${elapsed1}ms`);
|
|
48
|
-
|
|
49
|
-
const missingText = "THIS_TEXT_SHOULD_NOT_EXIST_XYZ_123";
|
|
50
|
-
console.log(`\nTest 2: Waiting for missing element "${missingText}" (should timeout)...`);
|
|
51
|
-
const start2 = Date.now();
|
|
52
|
-
const result2 = await interact.waitForElement(missingText, 2000, DEVICE_ID);
|
|
53
|
-
const elapsed2 = Date.now() - start2;
|
|
54
|
-
console.log(`Result: ${!result2.found ? "PASS" : "FAIL"}`);
|
|
55
|
-
console.log(`Found: ${result2.found}`);
|
|
56
|
-
console.log(`Time taken: ${elapsed2}ms (expected ~2000ms)`);
|
|
57
|
-
|
|
58
|
-
console.log(`\nTest 3: Found after polling`);
|
|
59
|
-
let calls = 0;
|
|
60
|
-
AndroidObserve.prototype.getUITree = async function() {
|
|
61
|
-
calls++;
|
|
62
|
-
if (calls < 3) {
|
|
63
|
-
return { device: { platform: "android", id: "mock", osVersion: "12", model: "Pixel", simulator: true }, screen: "", resolution: { width: 1080, height: 1920 }, elements: [] };
|
|
64
|
-
}
|
|
65
|
-
return { device: { platform: "android", id: "mock", osVersion: "12", model: "Pixel", simulator: true }, screen: "", resolution: { width: 1080, height: 1920 }, elements: [{ text: "Target", type: "Button", contentDescription: null, clickable: true, enabled: true, visible: true, bounds: [0,0,100,100], resourceId: null }] };
|
|
66
|
-
} as any;
|
|
67
|
-
|
|
68
|
-
const start3 = Date.now();
|
|
69
|
-
const result3 = await interact.waitForElement("Target", 2000, DEVICE_ID);
|
|
70
|
-
const elapsed3 = Date.now() - start3;
|
|
71
|
-
console.log(`Result: ${result3.found ? "PASS" : "FAIL"}`);
|
|
72
|
-
console.log(`Calls: ${calls} ${calls === 3 ? "PASS" : "FAIL"}`);
|
|
73
|
-
console.log(`Elapsed time (should be >= 1000ms): ${elapsed3} ${elapsed3 >= 1000 ? "PASS" : "FAIL"}`);
|
|
74
|
-
|
|
75
|
-
} catch {
|
|
76
|
-
console.error("Test failed with error:", error);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
runRealTest();
|
|
1
|
+
// wait_for_element device runner removed
|
|
2
|
+
console.log('wait_for_element device test removed');
|
|
3
|
+
process.exit(0);
|
|
@@ -1,104 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const originalGetUITree = (AndroidObserve as any).prototype.getUITree;
|
|
5
|
-
|
|
6
|
-
async function runTests() {
|
|
7
|
-
console.log("Starting tests for wait_for_element...");
|
|
8
|
-
const interact = new AndroidInteract();
|
|
9
|
-
|
|
10
|
-
console.log("\nTest 1: Element found immediately");
|
|
11
|
-
(AndroidObserve as any).prototype.getUITree = async () => ({
|
|
12
|
-
device: { platform: "android", id: "mock", osVersion: "12", model: "Pixel", simulator: true },
|
|
13
|
-
screen: "",
|
|
14
|
-
resolution: { width: 1080, height: 1920 },
|
|
15
|
-
elements: [{
|
|
16
|
-
text: "Target",
|
|
17
|
-
type: "Button",
|
|
18
|
-
contentDescription: null,
|
|
19
|
-
clickable: true,
|
|
20
|
-
enabled: true,
|
|
21
|
-
visible: true,
|
|
22
|
-
bounds: [0, 0, 100, 100],
|
|
23
|
-
resourceId: null
|
|
24
|
-
}]
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const start1 = Date.now();
|
|
28
|
-
const result1 = await interact.waitForElement("Target", 1000);
|
|
29
|
-
const elapsed1 = Date.now() - start1;
|
|
30
|
-
console.log("Result:", result1.found === true ? "PASS" : "FAIL");
|
|
31
|
-
console.log("Element:", result1.element ? "FOUND" : "MISSING");
|
|
32
|
-
console.log("Elapsed:", elapsed1, "ms");
|
|
33
|
-
|
|
34
|
-
console.log("\nTest 2: Element not found (timeout)");
|
|
35
|
-
(AndroidObserve as any).prototype.getUITree = async () => ({
|
|
36
|
-
device: { platform: "android", id: "mock", osVersion: "12", model: "Pixel", simulator: true },
|
|
37
|
-
screen: "",
|
|
38
|
-
resolution: { width: 1080, height: 1920 },
|
|
39
|
-
elements: []
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const start2 = Date.now();
|
|
43
|
-
const result2 = await interact.waitForElement("Target", 1200);
|
|
44
|
-
const elapsed2 = Date.now() - start2;
|
|
45
|
-
console.log("Result:", result2.found === false ? "PASS" : "FAIL");
|
|
46
|
-
console.log("Elapsed time (should be >= 1200ms):", elapsed2, elapsed2 >= 1200 ? "PASS" : "FAIL");
|
|
47
|
-
|
|
48
|
-
console.log("\nTest 3: Element found after polling");
|
|
49
|
-
let calls = 0;
|
|
50
|
-
(AndroidObserve as any).prototype.getUITree = async () => {
|
|
51
|
-
calls++;
|
|
52
|
-
if (calls < 3) {
|
|
53
|
-
return {
|
|
54
|
-
device: { platform: "android", id: "mock", osVersion: "12", model: "Pixel", simulator: true },
|
|
55
|
-
screen: "",
|
|
56
|
-
resolution: { width: 1080, height: 1920 },
|
|
57
|
-
elements: []
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
return {
|
|
61
|
-
device: { platform: "android", id: "mock", osVersion: "12", model: "Pixel", simulator: true },
|
|
62
|
-
screen: "",
|
|
63
|
-
resolution: { width: 1080, height: 1920 },
|
|
64
|
-
elements: [{
|
|
65
|
-
text: "Target",
|
|
66
|
-
type: "Button",
|
|
67
|
-
contentDescription: null,
|
|
68
|
-
clickable: true,
|
|
69
|
-
enabled: true,
|
|
70
|
-
visible: true,
|
|
71
|
-
bounds: [0, 0, 100, 100],
|
|
72
|
-
resourceId: null
|
|
73
|
-
}]
|
|
74
|
-
};
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const start3 = Date.now();
|
|
78
|
-
const result3 = await interact.waitForElement("Target", 2000);
|
|
79
|
-
const elapsed3 = Date.now() - start3;
|
|
80
|
-
console.log("Result:", result3.found === true ? "PASS" : "FAIL");
|
|
81
|
-
console.log("Calls:", calls, calls >= 3 ? "PASS" : "FAIL");
|
|
82
|
-
console.log("Elapsed time (should be >= 1000ms):", elapsed3, elapsed3 >= 1000 ? "PASS" : "FAIL");
|
|
83
|
-
|
|
84
|
-
console.log("\nTest 4: Error handling (fast failure)");
|
|
85
|
-
(AndroidObserve as any).prototype.getUITree = async () => ({
|
|
86
|
-
device: { platform: "android", id: "mock", osVersion: "12", model: "Pixel", simulator: true },
|
|
87
|
-
screen: "",
|
|
88
|
-
resolution: { width: 0, height: 0 },
|
|
89
|
-
elements: [],
|
|
90
|
-
error: "ADB Connection Failed"
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const start4 = Date.now();
|
|
94
|
-
const result4 = await interact.waitForElement("Target", 5000);
|
|
95
|
-
const elapsed4 = Date.now() - start4;
|
|
96
|
-
console.log("Result:", result4.found === false && result4.error === "ADB Connection Failed" ? "PASS" : "FAIL");
|
|
97
|
-
console.log("Error Message:", result4.error);
|
|
98
|
-
console.log("Elapsed time (should be < 1000ms):", elapsed4, elapsed4 < 1000 ? "PASS" : "FAIL");
|
|
99
|
-
|
|
100
|
-
// Restore
|
|
101
|
-
(AndroidObserve as any).prototype.getUITree = originalGetUITree;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
runTests().catch(console.error);
|
|
1
|
+
// wait_for_element tests removed — tool deprecated
|
|
2
|
+
console.log('wait_for_element unit tests removed');
|
package/test/observe/unit/{observe_until_edge_cases.test.ts → wait_for_ui_edge_cases.test.ts}
RENAMED
|
@@ -2,7 +2,7 @@ import { ToolsInteract } from '../../../../src/interact/index.js'
|
|
|
2
2
|
import * as Observe from '../../../../src/observe/index.js'
|
|
3
3
|
|
|
4
4
|
async function run() {
|
|
5
|
-
console.log('Unit:
|
|
5
|
+
console.log('Unit: wait_for_ui edge cases')
|
|
6
6
|
|
|
7
7
|
const origFind = (ToolsInteract as any).findElementHandler
|
|
8
8
|
const origFp = (Observe as any).ToolsObserve.getScreenFingerprintHandler
|
|
@@ -10,26 +10,26 @@ async function run() {
|
|
|
10
10
|
try {
|
|
11
11
|
// 1) Immediate absence should pass for match='absent'
|
|
12
12
|
(ToolsInteract as any).findElementHandler = async () => ({ found: false })
|
|
13
|
-
const r1 = await (ToolsInteract as any).
|
|
13
|
+
const r1 = await (ToolsInteract as any).waitForUIHandler({ type: 'ui', query: 'Nothing', timeoutMs: 2000, pollIntervalMs: 100, stability_ms: 200, match: 'absent', platform: 'android' })
|
|
14
14
|
console.log('Immediate absent test:', r1 && (r1 as any).success ? 'PASS' : 'FAIL', JSON.stringify({ poll_count: (r1 as any).poll_count, duration_ms: (r1 as any).duration_ms, stable_duration_ms: (r1 as any).stable_duration_ms, matchSource: (r1 as any).matchSource }, null, 2))
|
|
15
15
|
|
|
16
16
|
// 2) Boundary stability: condition becomes true and stays exactly long enough
|
|
17
17
|
// Use pollInterval 100ms and stability 300ms -> need ~3 consecutive trues
|
|
18
18
|
let seq2 = [false, true, true, true]
|
|
19
19
|
(ToolsInteract as any).findElementHandler = async () => ({ found: seq2.shift() ?? true })
|
|
20
|
-
const r2 = await (ToolsInteract as any).
|
|
20
|
+
const r2 = await (ToolsInteract as any).waitForUIHandler({ type: 'ui', query: 'Boundary', timeoutMs: 2000, pollIntervalMs: 100, stability_ms: 300, match: 'present', platform: 'android' })
|
|
21
21
|
console.log('Boundary stability test:', r2 && (r2 as any).success ? 'PASS' : 'FAIL', JSON.stringify({ poll_count: (r2 as any).poll_count, duration_ms: (r2 as any).duration_ms, stable_duration_ms: (r2 as any).stable_duration_ms, matchSource: (r2 as any).matchSource }, null, 2))
|
|
22
22
|
|
|
23
23
|
// 3) Long flicker that never stabilizes should timeout/fail
|
|
24
24
|
// Sequence toggles true/false repeatedly
|
|
25
25
|
let seq3 = [false, true, false, true, false, true, false]
|
|
26
26
|
(ToolsInteract as any).findElementHandler = async () => ({ found: seq3.shift() ?? false })
|
|
27
|
-
const r3 = await (ToolsInteract as any).
|
|
27
|
+
const r3 = await (ToolsInteract as any).waitForUIHandler({ type: 'ui', query: 'Flicker', timeoutMs: 1200, pollIntervalMs: 150, stability_ms: 400, match: 'present', platform: 'android' })
|
|
28
28
|
console.log('Long flicker timeout test:', !(r3 && (r3 as any).success) ? 'PASS' : 'FAIL', JSON.stringify({ poll_count: (r3 as any).poll_count, duration_ms: (r3 as any).duration_ms, stable_duration_ms: (r3 as any).stable_duration_ms, matchSource: (r3 as any).matchSource }, null, 2))
|
|
29
29
|
|
|
30
30
|
// 4) Very short stability requirement should pass quickly
|
|
31
31
|
(ToolsInteract as any).findElementHandler = async () => ({ found: true })
|
|
32
|
-
const r4 = await (ToolsInteract as any).
|
|
32
|
+
const r4 = await (ToolsInteract as any).waitForUIHandler({ type: 'ui', query: 'ShortStable', timeoutMs: 2000, pollIntervalMs: 200, stability_ms: 50, match: 'present', platform: 'android' })
|
|
33
33
|
console.log('Short stability test:', r4 && (r4 as any).success ? 'PASS' : 'FAIL', JSON.stringify({ poll_count: (r4 as any).poll_count, duration_ms: (r4 as any).duration_ms, stable_duration_ms: (r4 as any).stable_duration_ms, matchSource: (r4 as any).matchSource }, null, 2))
|
|
34
34
|
|
|
35
35
|
} finally {
|
|
@@ -2,7 +2,7 @@ import { ToolsInteract } from '../../../../src/interact/index.js'
|
|
|
2
2
|
import * as Observe from '../../../../src/observe/index.js'
|
|
3
3
|
|
|
4
4
|
async function run() {
|
|
5
|
-
console.log('Unit:
|
|
5
|
+
console.log('Unit: wait_for_ui stability behavior')
|
|
6
6
|
|
|
7
7
|
const origFind = (ToolsInteract as any).findElementHandler
|
|
8
8
|
const origFp = (Observe as any).ToolsObserve.getScreenFingerprintHandler
|
|
@@ -12,13 +12,13 @@ async function run() {
|
|
|
12
12
|
const seq = [false, true, false, true, true, true]
|
|
13
13
|
(ToolsInteract as any).findElementHandler = async () => ({ found: seq.shift() ?? true })
|
|
14
14
|
|
|
15
|
-
const res = await (ToolsInteract as any).
|
|
15
|
+
const res = await (ToolsInteract as any).waitForUIHandler({ type: 'ui', query: 'X', timeoutMs: 5000, pollIntervalMs: 100, stability_ms: 500, platform: 'android' })
|
|
16
16
|
const ok = res && (res as any).success
|
|
17
17
|
console.log('Flicker stability test:', ok ? 'PASS' : 'FAIL', JSON.stringify((res as any).telemetry || {}, null, 2))
|
|
18
18
|
|
|
19
19
|
// Simulate immediate stable presence
|
|
20
20
|
(ToolsInteract as any).findElementHandler = async () => ({ found: true })
|
|
21
|
-
const res2 = await (ToolsInteract as any).
|
|
21
|
+
const res2 = await (ToolsInteract as any).waitForUIHandler({ type: 'ui', query: 'Y', timeoutMs: 2000, pollIntervalMs: 100, stability_ms: 300, platform: 'android' })
|
|
22
22
|
console.log('Immediate stable test:', res2 && (res2 as any).success ? 'PASS' : 'FAIL', JSON.stringify((res2 as any).telemetry || {}, null, 2))
|
|
23
23
|
|
|
24
24
|
} finally {
|
package/test/unit/index.ts
CHANGED
|
@@ -1,18 +1,30 @@
|
|
|
1
1
|
// Aggregator entrypoint for unit tests (updated to new test layout)
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import
|
|
2
|
+
(async function() {
|
|
3
|
+
// Core unit tests that do not require real devices
|
|
4
|
+
await import('../observe/unit/logparse.test.ts')
|
|
5
|
+
await import('../observe/unit/logstream.test.ts')
|
|
6
|
+
await import('../observe/unit/get_screen_fingerprint.test.ts')
|
|
6
7
|
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import '../observe/unit/capture_debug_snapshot.test.ts'
|
|
15
|
-
import '../observe/unit/find_element.test.ts'
|
|
16
|
-
import '../interact/unit/observe_until.test.ts'
|
|
8
|
+
await import('../manage/unit/install.test.ts')
|
|
9
|
+
await import('../manage/unit/build.test.ts')
|
|
10
|
+
await import('../manage/unit/build_and_install.test.ts')
|
|
11
|
+
await import('../manage/unit/diagnostics.test.ts')
|
|
12
|
+
await import('../manage/unit/detection.test.ts')
|
|
13
|
+
await import('../manage/unit/mcp_disable_autodetect.test.ts')
|
|
14
|
+
await import('../interact/unit/wait_for_screen_change.test.ts')
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
// Conditionally include device-dependent unit tests. Set SKIP_DEVICE_TESTS=1 to exclude.
|
|
17
|
+
if (process.env.SKIP_DEVICE_TESTS !== '1') {
|
|
18
|
+
try {
|
|
19
|
+
await import('../observe/unit/capture_debug_snapshot.test.ts')
|
|
20
|
+
await import('../observe/unit/find_element.test.ts')
|
|
21
|
+
await import('../interact/unit/wait_for_ui.test.ts')
|
|
22
|
+
} catch (e) {
|
|
23
|
+
console.warn('Skipping some device-dependent tests due to import error:', e instanceof Error ? e.message : String(e))
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
console.log('SKIP_DEVICE_TESTS=1 detected - skipping device-dependent unit tests')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log('Unit tests loaded.')
|
|
30
|
+
})().catch(e => { console.error(e); process.exit(1) })
|