mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-05 22:32:12 +00:00
test: cover attach-only browser stop cleanup (#60097) (thanks @pedh)
This commit is contained in:
@@ -151,6 +151,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Image generation/providers: stop inferring private-network access from configured OpenAI, MiniMax, and fal image base URLs, and cap shared HTTP error-body reads so hostile or misconfigured endpoints fail closed without relaxing SSRF policy or buffering unbounded error payloads. Thanks @vincentkoc.
|
||||
- Browser/host inspection: keep static Chrome inspection helpers out of the activated browser runtime so `openclaw doctor browser` and related checks do not eagerly load the bundled browser plugin. (#59471) Thanks @vincentkoc.
|
||||
- Browser/CDP: normalize trailing-dot localhost absolute-form hosts before loopback checks so remote CDP websocket URLs like `ws://localhost.:...` rewrite back to the configured remote host. (#59236) Thanks @mappel-nv.
|
||||
- Browser/attach-only profiles: disconnect cached Playwright CDP sessions when stopping attach-only or remote CDP profiles, while still reporting never-started local managed profiles as not stopped. (#60097) Thanks @pedh.
|
||||
- Agents/output sanitization: strip namespaced `antml:thinking` blocks from user-visible text so Anthropic-style internal monologue tags do not leak into replies. (#59550) Thanks @obviyus.
|
||||
- Kimi Coding/tools: normalize Anthropic tool payloads into the OpenAI-compatible function shape Kimi Coding expects so tool calls stop losing required arguments. (#59440) Thanks @obviyus.
|
||||
- Image tool/paths: resolve relative local media paths against the agent `workspaceDir` instead of `process.cwd()` so inputs like `inbox/receipt.png` pass the local-path allowlist reliably. (#57222) Thanks Priyansh Gupta.
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { createProfileAvailability } from "./server-context.availability.js";
|
||||
import type { BrowserServerState, ProfileRuntimeState } from "./server-context.types.js";
|
||||
|
||||
const pwAiMocks = vi.hoisted(() => ({
|
||||
closePlaywrightBrowserConnection: vi.fn(async () => {}),
|
||||
}));
|
||||
|
||||
vi.mock("./pw-ai.js", () => pwAiMocks);
|
||||
vi.mock("./chrome.js", () => ({
|
||||
isChromeCdpReady: vi.fn(async () => true),
|
||||
isChromeReachable: vi.fn(async () => true),
|
||||
launchOpenClawChrome: vi.fn(async () => {
|
||||
throw new Error("unexpected launch");
|
||||
}),
|
||||
stopOpenClawChrome: vi.fn(async () => {}),
|
||||
}));
|
||||
vi.mock("./chrome-mcp.js", () => ({
|
||||
closeChromeMcpSession: vi.fn(async () => false),
|
||||
ensureChromeMcpAvailable: vi.fn(async () => {}),
|
||||
listChromeMcpTabs: vi.fn(async () => []),
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
function makeProfile(
|
||||
overrides: Partial<Parameters<typeof createProfileAvailability>[0]["profile"]> = {},
|
||||
): Parameters<typeof createProfileAvailability>[0]["profile"] {
|
||||
return {
|
||||
name: "openclaw",
|
||||
cdpUrl: "http://127.0.0.1:18800",
|
||||
cdpHost: "127.0.0.1",
|
||||
cdpIsLoopback: true,
|
||||
cdpPort: 18800,
|
||||
color: "#f60",
|
||||
driver: "openclaw",
|
||||
attachOnly: false,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function makeState(
|
||||
profile: Parameters<typeof createProfileAvailability>[0]["profile"],
|
||||
): BrowserServerState {
|
||||
return {
|
||||
server: null,
|
||||
port: 0,
|
||||
resolved: {
|
||||
enabled: true,
|
||||
evaluateEnabled: false,
|
||||
controlPort: 18791,
|
||||
cdpProtocol: "http",
|
||||
cdpHost: profile.cdpHost,
|
||||
cdpIsLoopback: profile.cdpIsLoopback,
|
||||
cdpPortRangeStart: 18800,
|
||||
cdpPortRangeEnd: 18810,
|
||||
remoteCdpTimeoutMs: 1500,
|
||||
remoteCdpHandshakeTimeoutMs: 3000,
|
||||
extraArgs: [],
|
||||
color: profile.color,
|
||||
headless: true,
|
||||
noSandbox: false,
|
||||
attachOnly: false,
|
||||
ssrfPolicy: { dangerouslyAllowPrivateNetwork: true },
|
||||
defaultProfile: profile.name,
|
||||
profiles: {
|
||||
[profile.name]: profile,
|
||||
},
|
||||
},
|
||||
profiles: new Map(),
|
||||
};
|
||||
}
|
||||
|
||||
function createStopHarness(profile: Parameters<typeof createProfileAvailability>[0]["profile"]) {
|
||||
const state = makeState(profile);
|
||||
const runtimeState: ProfileRuntimeState = {
|
||||
profile,
|
||||
running: null,
|
||||
lastTargetId: null,
|
||||
reconcile: null,
|
||||
};
|
||||
state.profiles.set(profile.name, runtimeState);
|
||||
|
||||
const ops = createProfileAvailability({
|
||||
opts: { getState: () => state },
|
||||
profile,
|
||||
state: () => state,
|
||||
getProfileState: () => runtimeState,
|
||||
setProfileRunning: (running) => {
|
||||
runtimeState.running = running;
|
||||
},
|
||||
});
|
||||
|
||||
return { ops };
|
||||
}
|
||||
|
||||
describe("createProfileAvailability.stopRunningBrowser", () => {
|
||||
it("disconnects attachOnly loopback profiles without an owned process", async () => {
|
||||
const profile = makeProfile({ attachOnly: true });
|
||||
const { ops } = createStopHarness(profile);
|
||||
|
||||
await expect(ops.stopRunningBrowser()).resolves.toEqual({ stopped: true });
|
||||
expect(pwAiMocks.closePlaywrightBrowserConnection).toHaveBeenCalledWith({
|
||||
cdpUrl: "http://127.0.0.1:18800",
|
||||
});
|
||||
});
|
||||
|
||||
it("disconnects remote CDP profiles without an owned process", async () => {
|
||||
const profile = makeProfile({
|
||||
cdpUrl: "http://10.0.0.5:9222",
|
||||
cdpHost: "10.0.0.5",
|
||||
cdpIsLoopback: false,
|
||||
cdpPort: 9222,
|
||||
});
|
||||
const { ops } = createStopHarness(profile);
|
||||
|
||||
await expect(ops.stopRunningBrowser()).resolves.toEqual({ stopped: true });
|
||||
expect(pwAiMocks.closePlaywrightBrowserConnection).toHaveBeenCalledWith({
|
||||
cdpUrl: "http://10.0.0.5:9222",
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps never-started local managed profiles as not stopped", async () => {
|
||||
const profile = makeProfile();
|
||||
const { ops } = createStopHarness(profile);
|
||||
|
||||
await expect(ops.stopRunningBrowser()).resolves.toEqual({ stopped: false });
|
||||
expect(pwAiMocks.closePlaywrightBrowserConnection).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user