Files
openclaw/extensions/browser/src/cli/browser-cli-manage.test.ts
T
FMLS 3a88142ddd fix(browser): document stable tab references (#88393)
Summary:
- The branch documents friendly browser tab references across docs, the browser skill, CLI help, and tool schema descriptions, and adds tests for target reference resolution and tab alias behavior.
- PR surface: Source +24, Tests +328, Docs +9. Total +361 across 21 files.
- Reproducibility: yes. for the documentation mismatch by source inspection: current main supports friendly ta ... schema/help surfaces still emphasize raw CDP target ids. Runtime behavior itself is not a new failing path.

Automerge notes:
- PR branch already contained follow-up commit before automerge: refactor(browser): share tab reference CLI help

Validation:
- ClawSweeper review passed for head 118af80b0b.
- Required merge gates passed before the squash merge.

Prepared head SHA: 118af80b0b
Review: https://github.com/openclaw/openclaw/pull/88393#issuecomment-4583558133

Co-authored-by: FMLS <kfliuyang@gmail.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
2026-05-31 12:09:50 +00:00

383 lines
12 KiB
TypeScript

import { beforeEach, describe, expect, it } from "vitest";
import {
createBrowserManageProgram,
getBrowserManageCallBrowserRequestMock,
} from "./browser-cli-manage.test-helpers.js";
import { getBrowserCliRuntime, getBrowserCliRuntimeCapture } from "./browser-cli.test-support.js";
function lastRuntimeLog(): string {
const calls = getBrowserCliRuntime().log.mock.calls;
const value = calls[calls.length - 1]?.[0];
if (typeof value !== "string") {
throw new Error("expected browser CLI runtime log");
}
return value;
}
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,
headlessSource: "default",
noSandbox: false,
executablePath: null,
attachOnly: true,
}
: {},
);
const program = createBrowserManageProgram();
await program.parseAsync(["browser", "--browser-profile", "chrome-live", "status"], {
from: "user",
});
const output = lastRuntimeLog();
expect(output).toContain("transport: chrome-mcp");
expect(output).toContain("headless: false (default)");
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 = lastRuntimeLog();
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 = lastRuntimeLog();
expect(output).toContain("chrome-live: running (2 tabs) [existing-session]");
expect(output).toContain("transport: chrome-mcp");
expect(output).not.toContain("port: 0");
});
it("redacts remote cdpUrl details in browser profiles output", async () => {
getBrowserManageCallBrowserRequestMock().mockImplementation(async (_opts: unknown, req) =>
req.path === "/profiles"
? {
profiles: [
{
name: "remote",
driver: "openclaw",
transport: "cdp",
running: true,
tabCount: 1,
isDefault: false,
isRemote: true,
cdpPort: null,
cdpUrl:
"https://alice:supersecretpasswordvalue1234@example.com/chrome?token=supersecrettokenvalue1234567890",
color: "#00AA00",
},
],
}
: {},
);
const program = createBrowserManageProgram();
await program.parseAsync(["browser", "profiles"], { from: "user" });
const output = lastRuntimeLog();
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");
});
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 = lastRuntimeLog();
expect(output).toContain('Created profile "chrome-live"');
expect(output).toContain("transport: chrome-mcp");
expect(output).not.toContain("port: 0");
});
it("redacts remote cdpUrl details after creating a remote profile", async () => {
getBrowserManageCallBrowserRequestMock().mockImplementation(async (_opts: unknown, req) =>
req.path === "/profiles/create"
? {
ok: true,
profile: "remote",
transport: "cdp",
cdpPort: null,
cdpUrl:
"https://alice:supersecretpasswordvalue1234@example.com/chrome?token=supersecrettokenvalue1234567890",
userDataDir: null,
color: "#00AA00",
isRemote: true,
}
: {},
);
const program = createBrowserManageProgram();
await program.parseAsync(
[
"browser",
"create-profile",
"--name",
"remote",
"--cdp-url",
"https://alice:supersecretpasswordvalue1234@example.com/chrome?token=supersecrettokenvalue1234567890",
],
{ from: "user" },
);
const output = lastRuntimeLog();
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");
});
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 = lastRuntimeLog();
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");
});
it("prints suggested tab references while keeping raw target ids visible", async () => {
getBrowserManageCallBrowserRequestMock().mockImplementation(async (_opts: unknown, req) =>
req.path === "/tabs"
? {
running: true,
tabs: [
{
targetId: "RAW_TARGET_1",
suggestedTargetId: "docs",
tabId: "t1",
label: "docs",
title: "Docs",
url: "https://docs.example.com",
},
],
}
: {},
);
const program = createBrowserManageProgram();
await program.parseAsync(["browser", "tabs"], { from: "user" });
const output = lastRuntimeLog();
expect(output).toContain("use: docs");
expect(output).toContain("tab: t1");
expect(output).toContain("label:docs");
expect(output).toContain("id: RAW_TARGET_1");
});
it("rejects non-integer tab indexes without calling browser actions", async () => {
const program = createBrowserManageProgram();
await expect(
program.parseAsync(["browser", "tab", "select", "1.9"], { from: "user" }),
).rejects.toThrow("__exit__:1");
expect(getBrowserCliRuntimeCapture().runtimeErrors.at(-1)).toContain(
"index must be a positive integer",
);
getBrowserCliRuntimeCapture().resetRuntimeCapture();
await expect(
program.parseAsync(["browser", "tab", "close", "abc"], { from: "user" }),
).rejects.toThrow("__exit__:1");
expect(getBrowserCliRuntimeCapture().runtimeErrors.at(-1)).toContain(
"index must be a positive integer",
);
expect(getBrowserManageCallBrowserRequestMock()).not.toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({ path: "/tabs/action" }),
expect.anything(),
);
});
it("accepts signed decimal tab indexes", async () => {
const program = createBrowserManageProgram();
await program.parseAsync(["browser", "tab", "select", "+2"], { from: "user" });
expect(getBrowserManageCallBrowserRequestMock()).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
path: "/tabs/action",
body: { action: "select", index: 1 },
}),
expect.anything(),
);
});
it("prints a readable browser doctor report", async () => {
getBrowserManageCallBrowserRequestMock().mockImplementation(async (_opts: unknown, req) => {
if (req.path === "/") {
return {
enabled: true,
profile: "openclaw",
driver: "openclaw",
transport: "cdp",
running: true,
cdpReady: true,
cdpHttp: true,
pid: 4321,
cdpPort: 18792,
cdpUrl: "http://127.0.0.1:18792",
chosenBrowser: "chrome",
userDataDir: null,
color: "#00AA00",
headless: false,
noSandbox: false,
executablePath: null,
attachOnly: false,
};
}
if (req.path === "/profiles") {
return { profiles: [{ name: "openclaw", running: true }] };
}
if (req.path === "/tabs") {
return {
running: true,
tabs: [
{
targetId: "abc",
tabId: "t1",
suggestedTargetId: "t1",
title: "Example",
url: "https://example.com",
},
],
};
}
return {};
});
const program = createBrowserManageProgram();
await program.parseAsync(["browser", "doctor"], { from: "user" });
const output = lastRuntimeLog();
expect(output).toContain("OK gateway: browser control endpoint reachable");
expect(output).toContain("OK tabs: 1 visible, use tab reference t1");
});
});