mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 21:21:10 +00:00
refactor: remove core browser test duplicates
This commit is contained in:
@@ -1,30 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readFields } from "../../../extensions/browser/src/cli/browser-cli-actions-input/shared.js";
|
||||
|
||||
describe("readFields", () => {
|
||||
it.each([
|
||||
{
|
||||
name: "keeps explicit type",
|
||||
fields: '[{"ref":"6","type":"textbox","value":"hello"}]',
|
||||
expected: [{ ref: "6", type: "textbox", value: "hello" }],
|
||||
},
|
||||
{
|
||||
name: "defaults missing type to text",
|
||||
fields: '[{"ref":"7","value":"world"}]',
|
||||
expected: [{ ref: "7", type: "text", value: "world" }],
|
||||
},
|
||||
{
|
||||
name: "defaults blank type to text",
|
||||
fields: '[{"ref":"8","type":" ","value":"blank"}]',
|
||||
expected: [{ ref: "8", type: "text", value: "blank" }],
|
||||
},
|
||||
])("$name", async ({ fields, expected }) => {
|
||||
await expect(readFields({ fields })).resolves.toEqual(expected);
|
||||
});
|
||||
|
||||
it("requires ref", async () => {
|
||||
await expect(readFields({ fields: '[{"type":"textbox","value":"world"}]' })).rejects.toThrow(
|
||||
"fields[0] must include ref",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,155 +0,0 @@
|
||||
import { Command } from "commander";
|
||||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { createCliRuntimeCapture } from "./test-runtime-capture.js";
|
||||
|
||||
const { defaultRuntime: runtime, resetRuntimeCapture } = createCliRuntimeCapture();
|
||||
|
||||
const gatewayMocks = vi.hoisted(() => ({
|
||||
callGatewayFromCli: vi.fn(async () => ({
|
||||
ok: true,
|
||||
format: "ai",
|
||||
targetId: "t1",
|
||||
url: "https://example.com",
|
||||
snapshot: "ok",
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("./gateway-rpc.js", () => ({
|
||||
callGatewayFromCli: gatewayMocks.callGatewayFromCli,
|
||||
}));
|
||||
|
||||
const configMocks = vi.hoisted(() => ({
|
||||
loadConfig: vi.fn(() => ({ browser: {} })),
|
||||
}));
|
||||
vi.mock("../config/config.js", () => configMocks);
|
||||
|
||||
const sharedMocks = vi.hoisted(() => ({
|
||||
callBrowserRequest: vi.fn(
|
||||
async (_opts: unknown, params: { path?: string; query?: Record<string, unknown> }) => {
|
||||
const format = params.query?.format === "aria" ? "aria" : "ai";
|
||||
if (format === "aria") {
|
||||
return {
|
||||
ok: true,
|
||||
format: "aria",
|
||||
targetId: "t1",
|
||||
url: "https://example.com",
|
||||
nodes: [],
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
format: "ai",
|
||||
targetId: "t1",
|
||||
url: "https://example.com",
|
||||
snapshot: "ok",
|
||||
};
|
||||
},
|
||||
),
|
||||
}));
|
||||
vi.mock("../../extensions/browser/src/cli/browser-cli-shared.js", () => ({
|
||||
callBrowserRequest: sharedMocks.callBrowserRequest,
|
||||
}));
|
||||
|
||||
vi.mock("../../extensions/browser/src/core-api.js", async () => ({
|
||||
...(await vi.importActual<object>("../../extensions/browser/src/core-api.js")),
|
||||
defaultRuntime: runtime,
|
||||
loadConfig: configMocks.loadConfig,
|
||||
}));
|
||||
|
||||
let registerBrowserInspectCommands: typeof import("../../extensions/browser/src/cli/browser-cli-inspect.js").registerBrowserInspectCommands;
|
||||
|
||||
type SnapshotDefaultsCase = {
|
||||
label: string;
|
||||
args: string[];
|
||||
expectMode: "efficient" | undefined;
|
||||
};
|
||||
|
||||
describe("browser cli snapshot defaults", () => {
|
||||
const runBrowserInspect = async (args: string[], withJson = false) => {
|
||||
const program = new Command();
|
||||
const browser = program.command("browser").option("--json", "JSON output", false);
|
||||
registerBrowserInspectCommands(browser, () => ({}));
|
||||
await program.parseAsync(withJson ? ["browser", "--json", ...args] : ["browser", ...args], {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
const [, params] = sharedMocks.callBrowserRequest.mock.calls.at(-1) ?? [];
|
||||
return params as { path?: string; query?: Record<string, unknown> } | undefined;
|
||||
};
|
||||
|
||||
const runSnapshot = async (args: string[]) => await runBrowserInspect(["snapshot", ...args]);
|
||||
|
||||
beforeAll(async () => {
|
||||
({ registerBrowserInspectCommands } =
|
||||
await import("../../extensions/browser/src/cli/browser-cli-inspect.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
resetRuntimeCapture();
|
||||
configMocks.loadConfig.mockReturnValue({ browser: {} });
|
||||
});
|
||||
|
||||
it.each<SnapshotDefaultsCase>([
|
||||
{
|
||||
label: "uses config snapshot defaults when mode is not provided",
|
||||
args: [],
|
||||
expectMode: "efficient",
|
||||
},
|
||||
{
|
||||
label: "does not apply config snapshot defaults to aria snapshots",
|
||||
args: ["--format", "aria"],
|
||||
expectMode: undefined,
|
||||
},
|
||||
])("$label", async ({ args, expectMode }) => {
|
||||
configMocks.loadConfig.mockReturnValue({
|
||||
browser: { snapshotDefaults: { mode: "efficient" } },
|
||||
});
|
||||
|
||||
if (args.includes("--format")) {
|
||||
gatewayMocks.callGatewayFromCli.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
format: "aria",
|
||||
targetId: "t1",
|
||||
url: "https://example.com",
|
||||
snapshot: "ok",
|
||||
});
|
||||
}
|
||||
|
||||
const params = await runSnapshot(args);
|
||||
expect(params?.path).toBe("/snapshot");
|
||||
if (expectMode === undefined) {
|
||||
expect((params?.query as { mode?: unknown } | undefined)?.mode).toBeUndefined();
|
||||
} else {
|
||||
expect(params?.query).toMatchObject({
|
||||
format: "ai",
|
||||
mode: expectMode,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("does not set mode when config defaults are absent", async () => {
|
||||
configMocks.loadConfig.mockReturnValue({ browser: {} });
|
||||
const params = await runSnapshot([]);
|
||||
expect((params?.query as { mode?: unknown } | undefined)?.mode).toBeUndefined();
|
||||
});
|
||||
|
||||
it("applies explicit efficient mode without config defaults", async () => {
|
||||
configMocks.loadConfig.mockReturnValue({ browser: {} });
|
||||
const params = await runSnapshot(["--efficient"]);
|
||||
expect(params?.query).toMatchObject({
|
||||
format: "ai",
|
||||
mode: "efficient",
|
||||
});
|
||||
});
|
||||
|
||||
it("sends screenshot request with trimmed target id and jpeg type", async () => {
|
||||
const params = await runBrowserInspect(["screenshot", " tab-1 ", "--type", "jpeg"], true);
|
||||
expect(params?.path).toBe("/screenshot");
|
||||
expect((params as { body?: Record<string, unknown> } | undefined)?.body).toMatchObject({
|
||||
targetId: "tab-1",
|
||||
type: "jpeg",
|
||||
fullPage: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,61 +0,0 @@
|
||||
import { vi } from "vitest";
|
||||
import { registerBrowserManageCommands } from "../../extensions/browser/src/cli/browser-cli-manage.js";
|
||||
import { createBrowserProgram } from "./browser-cli-test-helpers.js";
|
||||
|
||||
type BrowserRequest = { path?: string };
|
||||
type BrowserRuntimeOptions = { timeoutMs?: number };
|
||||
|
||||
export type BrowserManageCall = [unknown, BrowserRequest, BrowserRuntimeOptions | undefined];
|
||||
|
||||
const browserManageMocks = vi.hoisted(() => ({
|
||||
callBrowserRequest: vi.fn<
|
||||
(
|
||||
opts: unknown,
|
||||
req: BrowserRequest,
|
||||
runtimeOpts?: BrowserRuntimeOptions,
|
||||
) => Promise<Record<string, unknown>>
|
||||
>(async (_opts: unknown, req: BrowserRequest) =>
|
||||
req.path === "/"
|
||||
? {
|
||||
enabled: true,
|
||||
running: true,
|
||||
pid: 1,
|
||||
cdpPort: 18800,
|
||||
chosenBrowser: "chrome",
|
||||
userDataDir: "/tmp/openclaw",
|
||||
color: "blue",
|
||||
headless: true,
|
||||
attachOnly: false,
|
||||
}
|
||||
: {},
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("../../extensions/browser/src/cli/browser-cli-shared.js", () => ({
|
||||
callBrowserRequest: browserManageMocks.callBrowserRequest,
|
||||
}));
|
||||
|
||||
vi.mock("../../extensions/browser/src/core-api.js", async () => ({
|
||||
...(await vi.importActual<object>("../../extensions/browser/src/core-api.js")),
|
||||
...(await (await import("./browser-cli-test-helpers.js")).createBrowserCliRuntimeMockModule()),
|
||||
...(await (await import("./browser-cli-test-helpers.js")).createBrowserCliUtilsMockModule()),
|
||||
}));
|
||||
|
||||
export function createBrowserManageProgram(params?: { withParentTimeout?: boolean }) {
|
||||
const { program, browser, parentOpts } = createBrowserProgram();
|
||||
if (params?.withParentTimeout) {
|
||||
browser.option("--timeout <ms>", "Timeout in ms", "30000");
|
||||
}
|
||||
registerBrowserManageCommands(browser, parentOpts);
|
||||
return program;
|
||||
}
|
||||
|
||||
export function getBrowserManageCallBrowserRequestMock() {
|
||||
return browserManageMocks.callBrowserRequest;
|
||||
}
|
||||
|
||||
export function findBrowserManageCall(path: string): BrowserManageCall | undefined {
|
||||
return browserManageMocks.callBrowserRequest.mock.calls.find(
|
||||
(call) => (call[1] ?? {}).path === path,
|
||||
) as BrowserManageCall | undefined;
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
createBrowserManageProgram,
|
||||
getBrowserManageCallBrowserRequestMock,
|
||||
} from "./browser-cli-manage.test-helpers.js";
|
||||
import { getBrowserCliRuntime, getBrowserCliRuntimeCapture } from "./browser-cli-test-helpers.js";
|
||||
|
||||
describe("browser manage output", () => {
|
||||
beforeEach(() => {
|
||||
getBrowserManageCallBrowserRequestMock().mockClear();
|
||||
getBrowserCliRuntimeCapture().resetRuntimeCapture();
|
||||
});
|
||||
|
||||
it("shows chrome-mcp transport for existing-session status without fake CDP fields", async () => {
|
||||
getBrowserManageCallBrowserRequestMock().mockImplementation(async (_opts: unknown, req) =>
|
||||
req.path === "/"
|
||||
? {
|
||||
enabled: true,
|
||||
profile: "chrome-live",
|
||||
driver: "existing-session",
|
||||
transport: "chrome-mcp",
|
||||
running: true,
|
||||
cdpReady: true,
|
||||
cdpHttp: true,
|
||||
pid: 4321,
|
||||
cdpPort: null,
|
||||
cdpUrl: null,
|
||||
chosenBrowser: null,
|
||||
userDataDir: null,
|
||||
color: "#00AA00",
|
||||
headless: false,
|
||||
noSandbox: false,
|
||||
executablePath: null,
|
||||
attachOnly: true,
|
||||
}
|
||||
: {},
|
||||
);
|
||||
|
||||
const program = createBrowserManageProgram();
|
||||
await program.parseAsync(["browser", "--browser-profile", "chrome-live", "status"], {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
const output = getBrowserCliRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
expect(output).toContain("transport: chrome-mcp");
|
||||
expect(output).not.toContain("cdpPort:");
|
||||
expect(output).not.toContain("cdpUrl:");
|
||||
});
|
||||
|
||||
it("shows configured userDataDir for existing-session status", async () => {
|
||||
getBrowserManageCallBrowserRequestMock().mockImplementation(async (_opts: unknown, req) =>
|
||||
req.path === "/"
|
||||
? {
|
||||
enabled: true,
|
||||
profile: "brave-live",
|
||||
driver: "existing-session",
|
||||
transport: "chrome-mcp",
|
||||
running: true,
|
||||
cdpReady: true,
|
||||
cdpHttp: true,
|
||||
pid: 4321,
|
||||
cdpPort: null,
|
||||
cdpUrl: null,
|
||||
chosenBrowser: null,
|
||||
userDataDir: "/Users/test/Library/Application Support/BraveSoftware/Brave-Browser",
|
||||
color: "#FB542B",
|
||||
headless: false,
|
||||
noSandbox: false,
|
||||
executablePath: null,
|
||||
attachOnly: true,
|
||||
}
|
||||
: {},
|
||||
);
|
||||
|
||||
const program = createBrowserManageProgram();
|
||||
await program.parseAsync(["browser", "--browser-profile", "brave-live", "status"], {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
const output = getBrowserCliRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
expect(output).toContain(
|
||||
"userDataDir: /Users/test/Library/Application Support/BraveSoftware/Brave-Browser",
|
||||
);
|
||||
});
|
||||
|
||||
it("shows chrome-mcp transport in browser profiles output", async () => {
|
||||
getBrowserManageCallBrowserRequestMock().mockImplementation(async (_opts: unknown, req) =>
|
||||
req.path === "/profiles"
|
||||
? {
|
||||
profiles: [
|
||||
{
|
||||
name: "chrome-live",
|
||||
driver: "existing-session",
|
||||
transport: "chrome-mcp",
|
||||
running: true,
|
||||
tabCount: 2,
|
||||
isDefault: false,
|
||||
isRemote: false,
|
||||
cdpPort: null,
|
||||
cdpUrl: null,
|
||||
color: "#00AA00",
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
);
|
||||
|
||||
const program = createBrowserManageProgram();
|
||||
await program.parseAsync(["browser", "profiles"], { from: "user" });
|
||||
|
||||
const output = getBrowserCliRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
expect(output).toContain("chrome-live: running (2 tabs) [existing-session]");
|
||||
expect(output).toContain("transport: chrome-mcp");
|
||||
expect(output).not.toContain("port: 0");
|
||||
});
|
||||
|
||||
it("shows chrome-mcp transport after creating an existing-session profile", async () => {
|
||||
getBrowserManageCallBrowserRequestMock().mockImplementation(async (_opts: unknown, req) =>
|
||||
req.path === "/profiles/create"
|
||||
? {
|
||||
ok: true,
|
||||
profile: "chrome-live",
|
||||
transport: "chrome-mcp",
|
||||
cdpPort: null,
|
||||
cdpUrl: null,
|
||||
userDataDir: null,
|
||||
color: "#00AA00",
|
||||
isRemote: false,
|
||||
}
|
||||
: {},
|
||||
);
|
||||
|
||||
const program = createBrowserManageProgram();
|
||||
await program.parseAsync(
|
||||
["browser", "create-profile", "--name", "chrome-live", "--driver", "existing-session"],
|
||||
{ from: "user" },
|
||||
);
|
||||
|
||||
const output = getBrowserCliRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
expect(output).toContain('Created profile "chrome-live"');
|
||||
expect(output).toContain("transport: chrome-mcp");
|
||||
expect(output).not.toContain("port: 0");
|
||||
});
|
||||
|
||||
it("redacts sensitive remote cdpUrl details in status output", async () => {
|
||||
getBrowserManageCallBrowserRequestMock().mockImplementation(async (_opts: unknown, req) =>
|
||||
req.path === "/"
|
||||
? {
|
||||
enabled: true,
|
||||
profile: "remote",
|
||||
driver: "openclaw",
|
||||
transport: "cdp",
|
||||
running: true,
|
||||
cdpReady: true,
|
||||
cdpHttp: true,
|
||||
pid: null,
|
||||
cdpPort: 9222,
|
||||
cdpUrl:
|
||||
"https://alice:supersecretpasswordvalue1234@example.com/chrome?token=supersecrettokenvalue1234567890",
|
||||
chosenBrowser: null,
|
||||
userDataDir: null,
|
||||
color: "#00AA00",
|
||||
headless: false,
|
||||
noSandbox: false,
|
||||
executablePath: null,
|
||||
attachOnly: true,
|
||||
}
|
||||
: {},
|
||||
);
|
||||
|
||||
const program = createBrowserManageProgram();
|
||||
await program.parseAsync(["browser", "--browser-profile", "remote", "status"], {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
const output = getBrowserCliRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
expect(output).toContain("cdpUrl: https://example.com/chrome?token=supers…7890");
|
||||
expect(output).not.toContain("alice");
|
||||
expect(output).not.toContain("supersecretpasswordvalue1234");
|
||||
expect(output).not.toContain("supersecrettokenvalue1234567890");
|
||||
});
|
||||
});
|
||||
@@ -1,56 +0,0 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
createBrowserManageProgram,
|
||||
findBrowserManageCall,
|
||||
getBrowserManageCallBrowserRequestMock,
|
||||
} from "./browser-cli-manage.test-helpers.js";
|
||||
import { getBrowserCliRuntimeCapture } from "./browser-cli-test-helpers.js";
|
||||
|
||||
describe("browser manage start timeout option", () => {
|
||||
beforeEach(() => {
|
||||
getBrowserManageCallBrowserRequestMock().mockClear();
|
||||
getBrowserCliRuntimeCapture().resetRuntimeCapture();
|
||||
});
|
||||
|
||||
it("uses parent --timeout for browser start instead of hardcoded 15s", async () => {
|
||||
const program = createBrowserManageProgram({ withParentTimeout: true });
|
||||
await program.parseAsync(["browser", "--timeout", "60000", "start"], { from: "user" });
|
||||
|
||||
const startCall = findBrowserManageCall("/start");
|
||||
expect(startCall).toBeDefined();
|
||||
expect(startCall?.[0]).toMatchObject({ timeout: "60000" });
|
||||
expect(startCall?.[2]).toBeUndefined();
|
||||
});
|
||||
|
||||
it("uses a longer built-in timeout for browser status", async () => {
|
||||
const program = createBrowserManageProgram({ withParentTimeout: true });
|
||||
await program.parseAsync(["browser", "status"], { from: "user" });
|
||||
|
||||
const statusCall = findBrowserManageCall("/");
|
||||
expect(statusCall?.[2]).toEqual({ timeoutMs: 45_000 });
|
||||
});
|
||||
|
||||
it("uses a longer built-in timeout for browser tabs", async () => {
|
||||
const program = createBrowserManageProgram({ withParentTimeout: true });
|
||||
await program.parseAsync(["browser", "tabs"], { from: "user" });
|
||||
|
||||
const tabsCall = findBrowserManageCall("/tabs");
|
||||
expect(tabsCall?.[2]).toEqual({ timeoutMs: 45_000 });
|
||||
});
|
||||
|
||||
it("uses a longer built-in timeout for browser profiles", async () => {
|
||||
const program = createBrowserManageProgram({ withParentTimeout: true });
|
||||
await program.parseAsync(["browser", "profiles"], { from: "user" });
|
||||
|
||||
const profilesCall = findBrowserManageCall("/profiles");
|
||||
expect(profilesCall?.[2]).toEqual({ timeoutMs: 45_000 });
|
||||
});
|
||||
|
||||
it("uses a longer built-in timeout for browser open", async () => {
|
||||
const program = createBrowserManageProgram({ withParentTimeout: true });
|
||||
await program.parseAsync(["browser", "open", "https://example.com"], { from: "user" });
|
||||
|
||||
const openCall = findBrowserManageCall("/tabs/open");
|
||||
expect(openCall?.[2]).toEqual({ timeoutMs: 45_000 });
|
||||
});
|
||||
});
|
||||
@@ -1,173 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { registerBrowserStateCommands } from "../../extensions/browser/src/cli/browser-cli-state.js";
|
||||
import {
|
||||
createBrowserProgram as createBrowserProgramShared,
|
||||
getBrowserCliRuntime,
|
||||
getBrowserCliRuntimeCapture,
|
||||
} from "./browser-cli-test-helpers.js";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
callBrowserRequest: vi.fn(async (..._args: unknown[]) => ({ ok: true })),
|
||||
runBrowserResizeWithOutput: vi.fn(async (_params: unknown) => {}),
|
||||
}));
|
||||
|
||||
vi.mock("../../extensions/browser/src/cli/browser-cli-shared.js", () => ({
|
||||
callBrowserRequest: mocks.callBrowserRequest,
|
||||
}));
|
||||
|
||||
vi.mock("../../extensions/browser/src/cli/browser-cli-resize.js", () => ({
|
||||
runBrowserResizeWithOutput: mocks.runBrowserResizeWithOutput,
|
||||
}));
|
||||
|
||||
vi.mock("../../extensions/browser/src/core-api.js", async () => ({
|
||||
...(await vi.importActual<object>("../../extensions/browser/src/core-api.js")),
|
||||
...(await (await import("./browser-cli-test-helpers.js")).createBrowserCliRuntimeMockModule()),
|
||||
...(await (await import("./browser-cli-test-helpers.js")).createBrowserCliUtilsMockModule()),
|
||||
}));
|
||||
|
||||
describe("browser state option collisions", () => {
|
||||
const createStateProgram = ({ withGatewayUrl = false } = {}) => {
|
||||
const { program, browser, parentOpts } = createBrowserProgramShared({ withGatewayUrl });
|
||||
registerBrowserStateCommands(browser, parentOpts);
|
||||
return program;
|
||||
};
|
||||
|
||||
const getLastRequest = () => {
|
||||
const call = mocks.callBrowserRequest.mock.calls.at(-1);
|
||||
expect(call).toBeDefined();
|
||||
if (!call) {
|
||||
throw new Error("expected browser request call");
|
||||
}
|
||||
return call[1] as { body?: Record<string, unknown> };
|
||||
};
|
||||
|
||||
const runBrowserCommand = async (argv: string[]) => {
|
||||
const program = createStateProgram();
|
||||
await program.parseAsync(["browser", ...argv], { from: "user" });
|
||||
};
|
||||
|
||||
const runBrowserCommandAndGetRequest = async (argv: string[]) => {
|
||||
await runBrowserCommand(argv);
|
||||
return getLastRequest();
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mocks.callBrowserRequest.mockClear();
|
||||
mocks.runBrowserResizeWithOutput.mockClear();
|
||||
getBrowserCliRuntimeCapture().resetRuntimeCapture();
|
||||
getBrowserCliRuntime().exit.mockImplementation(() => {});
|
||||
});
|
||||
|
||||
it("forwards parent-captured --target-id on `browser cookies set`", async () => {
|
||||
const request = await runBrowserCommandAndGetRequest([
|
||||
"cookies",
|
||||
"set",
|
||||
"session",
|
||||
"abc",
|
||||
"--url",
|
||||
"https://example.com",
|
||||
"--target-id",
|
||||
"tab-1",
|
||||
]);
|
||||
|
||||
expect((request as { body?: { targetId?: string } }).body?.targetId).toBe("tab-1");
|
||||
});
|
||||
|
||||
it("resolves --url via parent when addGatewayClientOptions captures it", async () => {
|
||||
const program = createStateProgram({ withGatewayUrl: true });
|
||||
await program.parseAsync(
|
||||
[
|
||||
"browser",
|
||||
"--url",
|
||||
"ws://gw",
|
||||
"cookies",
|
||||
"set",
|
||||
"session",
|
||||
"abc",
|
||||
"--url",
|
||||
"https://example.com",
|
||||
],
|
||||
{ from: "user" },
|
||||
);
|
||||
const call = mocks.callBrowserRequest.mock.calls.at(-1);
|
||||
expect(call).toBeDefined();
|
||||
const request = call![1] as { body?: { cookie?: { url?: string } } };
|
||||
expect(request.body?.cookie?.url).toBe("https://example.com");
|
||||
});
|
||||
|
||||
it("inherits --url from parent when subcommand does not provide it", async () => {
|
||||
const program = createStateProgram({ withGatewayUrl: true });
|
||||
await program.parseAsync(
|
||||
["browser", "--url", "https://inherited.example.com", "cookies", "set", "session", "abc"],
|
||||
{ from: "user" },
|
||||
);
|
||||
const call = mocks.callBrowserRequest.mock.calls.at(-1);
|
||||
expect(call).toBeDefined();
|
||||
const request = call![1] as { body?: { cookie?: { url?: string } } };
|
||||
expect(request.body?.cookie?.url).toBe("https://inherited.example.com");
|
||||
});
|
||||
|
||||
it("accepts legacy parent `--json` by parsing payload via positional headers fallback", async () => {
|
||||
const request = (await runBrowserCommandAndGetRequest([
|
||||
"set",
|
||||
"headers",
|
||||
"--json",
|
||||
'{"x-auth":"ok"}',
|
||||
])) as {
|
||||
body?: { headers?: Record<string, string> };
|
||||
};
|
||||
expect(request.body?.headers).toEqual({ "x-auth": "ok" });
|
||||
});
|
||||
|
||||
it("filters non-string header values from JSON payload", async () => {
|
||||
const request = (await runBrowserCommandAndGetRequest([
|
||||
"set",
|
||||
"headers",
|
||||
"--json",
|
||||
'{"x-auth":"ok","retry":3,"enabled":true}',
|
||||
])) as {
|
||||
body?: { headers?: Record<string, string> };
|
||||
};
|
||||
expect(request.body?.headers).toEqual({ "x-auth": "ok" });
|
||||
});
|
||||
|
||||
it("errors when set offline receives an invalid value", async () => {
|
||||
await runBrowserCommand(["set", "offline", "maybe"]);
|
||||
|
||||
expect(mocks.callBrowserRequest).not.toHaveBeenCalled();
|
||||
expect(getBrowserCliRuntime().error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Expected on|off"),
|
||||
);
|
||||
expect(getBrowserCliRuntime().exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it("errors when set media receives an invalid value", async () => {
|
||||
await runBrowserCommand(["set", "media", "sepia"]);
|
||||
|
||||
expect(mocks.callBrowserRequest).not.toHaveBeenCalled();
|
||||
expect(getBrowserCliRuntime().error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Expected dark|light|none"),
|
||||
);
|
||||
expect(getBrowserCliRuntime().exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it("errors when headers JSON is missing", async () => {
|
||||
await runBrowserCommand(["set", "headers"]);
|
||||
|
||||
expect(mocks.callBrowserRequest).not.toHaveBeenCalled();
|
||||
expect(getBrowserCliRuntime().error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Missing headers JSON"),
|
||||
);
|
||||
expect(getBrowserCliRuntime().exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it("errors when headers JSON is not an object", async () => {
|
||||
await runBrowserCommand(["set", "headers", "--json", "[]"]);
|
||||
|
||||
expect(mocks.callBrowserRequest).not.toHaveBeenCalled();
|
||||
expect(getBrowserCliRuntime().error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Headers JSON must be a JSON object"),
|
||||
);
|
||||
expect(getBrowserCliRuntime().exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
});
|
||||
@@ -1,60 +0,0 @@
|
||||
import { Command } from "commander";
|
||||
import type { GatewayRpcOpts } from "./gateway-rpc.js";
|
||||
import { createCliRuntimeCapture } from "./test-runtime-capture.js";
|
||||
import type { CliRuntimeCapture } from "./test-runtime-capture.js";
|
||||
|
||||
type BrowserParentOpts = GatewayRpcOpts & {
|
||||
json?: boolean;
|
||||
browserProfile?: string;
|
||||
};
|
||||
|
||||
export function createBrowserProgram(params?: { withGatewayUrl?: boolean }): {
|
||||
program: Command;
|
||||
browser: Command;
|
||||
parentOpts: (cmd: Command) => BrowserParentOpts;
|
||||
} {
|
||||
const program = new Command();
|
||||
const browser = program
|
||||
.command("browser")
|
||||
.option("--browser-profile <name>", "Browser profile")
|
||||
.option("--json", "Output JSON", false);
|
||||
if (params?.withGatewayUrl) {
|
||||
browser.option("--url <url>", "Gateway WebSocket URL");
|
||||
}
|
||||
const parentOpts = (cmd: Command) => cmd.parent?.opts?.() as BrowserParentOpts;
|
||||
return { program, browser, parentOpts };
|
||||
}
|
||||
|
||||
const browserCliRuntimeState = { capture: null as CliRuntimeCapture | null };
|
||||
|
||||
export function getBrowserCliRuntimeCapture(): CliRuntimeCapture {
|
||||
if (!browserCliRuntimeState.capture) {
|
||||
throw new Error("runtime capture not initialized");
|
||||
}
|
||||
return browserCliRuntimeState.capture;
|
||||
}
|
||||
|
||||
export function getBrowserCliRuntime() {
|
||||
return getBrowserCliRuntimeCapture().defaultRuntime;
|
||||
}
|
||||
|
||||
export async function mockBrowserCliDefaultRuntime() {
|
||||
browserCliRuntimeState.capture ??= createCliRuntimeCapture();
|
||||
return { defaultRuntime: browserCliRuntimeState.capture.defaultRuntime };
|
||||
}
|
||||
|
||||
export async function runCommandWithRuntimeMock(
|
||||
_runtime: unknown,
|
||||
action: () => Promise<void>,
|
||||
onError: (err: unknown) => void,
|
||||
) {
|
||||
return await action().catch(onError);
|
||||
}
|
||||
|
||||
export async function createBrowserCliUtilsMockModule() {
|
||||
return { runCommandWithRuntime: runCommandWithRuntimeMock };
|
||||
}
|
||||
|
||||
export async function createBrowserCliRuntimeMockModule() {
|
||||
return await mockBrowserCliDefaultRuntime();
|
||||
}
|
||||
Reference in New Issue
Block a user