* feat(browser): replace flat snapshot with ARIA tree and page-side refs * feat(browser): trusted CDP input with actionability waits * feat(browser): auto-handle javascript dialogs * feat(browser): add browser_evaluate * feat(browser): add scroll, resize, and close_tab tools * fix(browser): review fixes for snapshot/input/dialog seams * fix(browser): address review findings on dialog capture, truncation, and keypress targets * fix(harness): drop resize check that cannot run under parked compositor surfaces * fix(browser): second review round — snapshot fidelity, key input, dialog scoping
173 lines
4.8 KiB
TypeScript
173 lines
4.8 KiB
TypeScript
import { describe, expect, test } from "vitest";
|
|
|
|
import { BROWSER_AUTOMATION_COMMAND_NAMES } from "./browser-automation/rpc-schemas.js";
|
|
import { CLIENT_CAPS } from "./client-capabilities.js";
|
|
import {
|
|
MutableDaemonConfigPatchSchema,
|
|
MutableDaemonConfigSchema,
|
|
SessionInboundMessageSchema,
|
|
SessionOutboundMessageSchema,
|
|
WSHelloMessageSchema,
|
|
} from "./messages.js";
|
|
|
|
describe("browser automation protocol integration", () => {
|
|
const browserId = "11111111-1111-4111-8111-111111111111";
|
|
|
|
test("browser host capability parses supported commands in hello", () => {
|
|
expect(
|
|
WSHelloMessageSchema.parse({
|
|
type: "hello",
|
|
clientId: "client-1",
|
|
clientType: "mobile",
|
|
protocolVersion: 1,
|
|
capabilities: {
|
|
[CLIENT_CAPS.browserHost]: {
|
|
supportedCommands: [...BROWSER_AUTOMATION_COMMAND_NAMES],
|
|
hostKind: "desktop app",
|
|
},
|
|
},
|
|
}).capabilities,
|
|
).toMatchObject({
|
|
[CLIENT_CAPS.browserHost]: {
|
|
supportedCommands: [...BROWSER_AUTOMATION_COMMAND_NAMES],
|
|
hostKind: "desktop app",
|
|
},
|
|
});
|
|
});
|
|
|
|
test("browser host capability requires at least one supported command", () => {
|
|
expect(() =>
|
|
WSHelloMessageSchema.parse({
|
|
type: "hello",
|
|
clientId: "client-1",
|
|
clientType: "mobile",
|
|
protocolVersion: 1,
|
|
capabilities: {
|
|
[CLIENT_CAPS.browserHost]: {
|
|
supportedCommands: [],
|
|
hostKind: "desktop app",
|
|
},
|
|
},
|
|
}),
|
|
).toThrow();
|
|
|
|
expect(() =>
|
|
WSHelloMessageSchema.parse({
|
|
type: "hello",
|
|
clientId: "client-2",
|
|
clientType: "mobile",
|
|
protocolVersion: 1,
|
|
capabilities: {
|
|
[CLIENT_CAPS.browserHost]: {},
|
|
},
|
|
}),
|
|
).toThrow();
|
|
|
|
expect(() =>
|
|
WSHelloMessageSchema.parse({
|
|
type: "hello",
|
|
clientId: "client-3",
|
|
clientType: "mobile",
|
|
protocolVersion: 1,
|
|
capabilities: {
|
|
[CLIENT_CAPS.browserHost]: {
|
|
supportedCommands: ["future_command"],
|
|
},
|
|
},
|
|
}),
|
|
).toThrow();
|
|
});
|
|
|
|
test("browser host capability ignores unknown future commands when known commands remain", () => {
|
|
expect(
|
|
WSHelloMessageSchema.parse({
|
|
type: "hello",
|
|
clientId: "client-1",
|
|
clientType: "mobile",
|
|
protocolVersion: 1,
|
|
capabilities: {
|
|
[CLIENT_CAPS.browserHost]: {
|
|
supportedCommands: ["list_tabs", "future_command", "list_tabs"],
|
|
hostKind: "desktop app",
|
|
},
|
|
},
|
|
}).capabilities,
|
|
).toMatchObject({
|
|
[CLIENT_CAPS.browserHost]: {
|
|
supportedCommands: ["list_tabs"],
|
|
hostKind: "desktop app",
|
|
},
|
|
});
|
|
});
|
|
|
|
test("browser host capability accepts new tool commands as supported commands", () => {
|
|
expect(
|
|
WSHelloMessageSchema.parse({
|
|
type: "hello",
|
|
clientId: "client-1",
|
|
clientType: "mobile",
|
|
protocolVersion: 1,
|
|
capabilities: {
|
|
[CLIENT_CAPS.browserHost]: {
|
|
supportedCommands: ["evaluate", "scroll", "resize", "close_tab"],
|
|
hostKind: "desktop app",
|
|
},
|
|
},
|
|
}).capabilities,
|
|
).toMatchObject({
|
|
[CLIENT_CAPS.browserHost]: {
|
|
supportedCommands: ["evaluate", "scroll", "resize", "close_tab"],
|
|
hostKind: "desktop app",
|
|
},
|
|
});
|
|
});
|
|
|
|
test("hello remains valid when no browser host capability is advertised", () => {
|
|
expect(
|
|
WSHelloMessageSchema.parse({
|
|
type: "hello",
|
|
clientId: "old-client",
|
|
clientType: "mobile",
|
|
protocolVersion: 1,
|
|
}).capabilities,
|
|
).toBeUndefined();
|
|
});
|
|
|
|
test("daemon to browser host execute request is an outbound session message", () => {
|
|
const parsed = SessionOutboundMessageSchema.parse({
|
|
type: "browser.automation.execute.request",
|
|
requestId: "req-1",
|
|
command: { command: "snapshot", args: { browserId } },
|
|
});
|
|
|
|
expect(parsed.type).toBe("browser.automation.execute.request");
|
|
});
|
|
|
|
test("browser host to daemon execute response is an inbound session message", () => {
|
|
const parsed = SessionInboundMessageSchema.parse({
|
|
type: "browser.automation.execute.response",
|
|
payload: {
|
|
requestId: "req-1",
|
|
ok: true,
|
|
result: { command: "list_tabs", tabs: [] },
|
|
},
|
|
});
|
|
|
|
expect(parsed.type).toBe("browser.automation.execute.response");
|
|
});
|
|
|
|
test("mutable daemon config defaults browser tools off and accepts opt-in patches", () => {
|
|
expect(
|
|
MutableDaemonConfigSchema.parse({
|
|
mcp: { injectIntoAgents: false },
|
|
}).browserTools,
|
|
).toEqual({ enabled: false });
|
|
|
|
expect(
|
|
MutableDaemonConfigPatchSchema.parse({
|
|
browserTools: { enabled: true },
|
|
}).browserTools,
|
|
).toEqual({ enabled: true });
|
|
});
|
|
});
|