Reuse the exact selected advertised descriptor for SDK output validation instead of compiling the entire cached catalog for every fresh parity-test client. Retain actual catalog discovery, schema validation, and all result assertions under the existing worker heap limit.
115 lines
4.1 KiB
TypeScript
115 lines
4.1 KiB
TypeScript
import { readFile, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect, onTestFinished } from "vitest";
|
|
|
|
import { AdbProvider } from "../../../src/adb/AdbProvider.js";
|
|
import { toolContract } from "../../../src/contracts/toolContracts.js";
|
|
import { parseEvidence } from "../../../src/domain/evidence.js";
|
|
import { createApplicationMcpHarness } from "../../fixtures/applicationMcpHarness.js";
|
|
import { advertisedMcpToolSchemas } from "../../fixtures/advertisedMcpTool.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
|
|
for (const { serial, section, focused, missing } of [
|
|
{ serial: "null-device", section: "windows", focused: false, missing: false },
|
|
{
|
|
serial: "fallback-null-device",
|
|
section: "displays",
|
|
focused: false,
|
|
missing: false,
|
|
},
|
|
{
|
|
serial: "focused-device",
|
|
section: "windows",
|
|
focused: true,
|
|
missing: false,
|
|
},
|
|
{
|
|
serial: "missing-device",
|
|
section: "displays",
|
|
focused: false,
|
|
missing: true,
|
|
},
|
|
]) {
|
|
cliTest.skipIf(process.platform === "win32")(
|
|
`preserves focus presence through CLI and MCP: ${serial}`,
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-adb-window-focus-");
|
|
const binary = join(root, "adb");
|
|
await writeFile(
|
|
binary,
|
|
`#!/bin/sh
|
|
if [ "$1" = version ]; then
|
|
printf 'Android Debug Bridge version 1.0.41\\nVersion 34.0.5-fixture\\nInstalled as %s\\n' "$0"
|
|
exit 0
|
|
fi
|
|
if [ "$1" = -s ] && [ "$3" = shell ] && [ "$4" = dumpsys ] && [ "$5" = window ]; then
|
|
printf '%s\\n' "$6" >> "$0.calls"
|
|
if [ "$2" = missing-device ]; then printf 'no focus declarations\\n'; exit 0; fi
|
|
if [ "$2" = fallback-null-device ] && [ "$6" = windows ]; then printf 'no focus declarations\\n'; exit 0; fi
|
|
if [ "$2" = focused-device ]; then
|
|
printf ' mCurrentFocus=Window{current}\\n mFocusedApp=ActivityRecord{current}\\n'
|
|
else
|
|
printf ' mCurrentFocus=null\\n mFocusedApp=null\\n'
|
|
fi
|
|
exit 0
|
|
fi
|
|
exit 1
|
|
`,
|
|
{ mode: 0o755 },
|
|
);
|
|
const output = await cli.run({
|
|
arguments: ["inspect-adb-window", serial, "--json"],
|
|
environment: { REA_ADB_PATH: binary },
|
|
});
|
|
expect(output.exitCode).toBe(0);
|
|
const provider = new AdbProvider({
|
|
environment: { REA_ADB_PATH: binary },
|
|
});
|
|
const harness = await createApplicationMcpHarness({
|
|
serverOptions: { adbDeviceAnalysis: provider },
|
|
});
|
|
onTestFinished(() => harness.close());
|
|
const schemas = await advertisedMcpToolSchemas(
|
|
harness.client,
|
|
"inspect_adb_window",
|
|
);
|
|
const args = { serial };
|
|
expect(schemas.ajv.validate(schemas.inputSchema, args)).toBe(true);
|
|
const response = await harness.client.callTool(
|
|
{ name: schemas.name, arguments: args },
|
|
{ toolDefinition: schemas.toolDefinition },
|
|
);
|
|
expect(response.isError).not.toBe(true);
|
|
expect(
|
|
schemas.ajv.validate(schemas.outputSchema, response.structuredContent),
|
|
).toBe(true);
|
|
for (const raw of [output.json, response.structuredContent]) {
|
|
const evidence = parseEvidence(
|
|
toolContract("inspect_adb_window").outputSchema.parse(raw),
|
|
);
|
|
expect(evidence.normalized_result).toMatchObject({
|
|
focused_window: focused ? "Window{current}" : null,
|
|
focused_app: focused ? "ActivityRecord{current}" : null,
|
|
observed_lines: missing
|
|
? []
|
|
: [
|
|
` mCurrentFocus=${focused ? "Window{current}" : "null"}`,
|
|
` mFocusedApp=${focused ? "ActivityRecord{current}" : "null"}`,
|
|
],
|
|
});
|
|
expect(evidence.limitations).toContainEqual(
|
|
missing
|
|
? expect.stringContaining("focus is unknown, not absent")
|
|
: `Focus declarations were read from \`dumpsys window ${section}\``,
|
|
);
|
|
}
|
|
const sections =
|
|
section === "windows" ? ["windows"] : ["windows", "displays"];
|
|
expect(
|
|
(await readFile(`${binary}.calls`, "utf8")).trim().split("\n"),
|
|
).toEqual([...sections, ...sections]);
|
|
},
|
|
);
|
|
}
|