test: share browser route fixtures

This commit is contained in:
Peter Steinberger
2026-04-20 20:29:53 +01:00
parent 978e379079
commit 28f7745a5e
3 changed files with 41 additions and 75 deletions

View File

@@ -97,6 +97,10 @@ describe("existing-session interaction navigation guard", () => {
}
async function expectActionToReject(body: Record<string, unknown>) {
await expectActionToThrow(body, "Unable to verify stable post-interaction navigation");
}
async function expectActionToThrow(body: Record<string, unknown>, message: string) {
const handler = getActPostHandler();
const response = createBrowserRouteResponse();
const pending = handler?.({ params: {}, query: {}, body }, response.res) ?? Promise.resolve();
@@ -106,7 +110,7 @@ describe("existing-session interaction navigation guard", () => {
await pending;
})();
await expect(completion).rejects.toThrow("Unable to verify stable post-interaction navigation");
await expect(completion).rejects.toThrow(message);
}
function expectNavigationProbeUrls(urls: string[]) {
@@ -215,18 +219,7 @@ describe("existing-session interaction navigation guard", () => {
},
);
const handler = getActPostHandler();
const response = createBrowserRouteResponse();
const pending =
handler?.({ params: {}, query: {}, body: { kind: "click", ref: "btn-1" } }, response.res) ??
Promise.resolve();
void pending.catch(() => {});
const completion = (async () => {
await vi.runAllTimersAsync();
await pending;
})();
await expect(completion).rejects.toThrow("blocked new tab");
await expectActionToThrow({ kind: "click", ref: "btn-1" }, "blocked new tab");
expect(chromeMcpMocks.clickChromeMcpElement).toHaveBeenCalledOnce();
});
@@ -338,18 +331,7 @@ describe("existing-session interaction navigation guard", () => {
throw new Error("stale element");
});
const handler = getActPostHandler();
const response = createBrowserRouteResponse();
const pending =
handler?.({ params: {}, query: {}, body: { kind: "click", ref: "btn-1" } }, response.res) ??
Promise.resolve();
void pending.catch(() => {});
const completion = (async () => {
await vi.runAllTimersAsync();
await pending;
})();
await expect(completion).rejects.toThrow("stale element");
await expectActionToThrow({ kind: "click", ref: "btn-1" }, "stale element");
expect(chromeMcpMocks.evaluateChromeMcpScript).toHaveBeenCalled();
expect(navigationGuardMocks.assertBrowserNavigationResultAllowed).toHaveBeenCalled();
});

View File

@@ -25,6 +25,27 @@ function setupEnsureBrowserAvailableHarness() {
return { launchOpenClawChrome, stopOpenClawChrome, isChromeCdpReady, profile, state };
}
function createAttachOnlyLoopbackProfile(cdpUrl: string) {
const state = makeBrowserServerState({
profile: {
name: "manual-cdp",
cdpUrl,
cdpHost: "127.0.0.1",
cdpIsLoopback: true,
cdpPort: 9222,
color: "#00AA00",
driver: "openclaw",
attachOnly: true,
},
resolvedOverrides: {
defaultProfile: "manual-cdp",
ssrfPolicy: {},
},
});
const ctx = createBrowserRouteContext({ getState: () => state });
return { profile: ctx.forProfile("manual-cdp"), state };
}
afterEach(() => {
vi.useRealTimers();
vi.clearAllMocks();
@@ -142,24 +163,7 @@ describe("browser server-context ensureBrowserAvailable", () => {
const isChromeReachable = vi.mocked(chromeModule.isChromeReachable);
const isChromeCdpReady = vi.mocked(chromeModule.isChromeCdpReady);
const state = makeBrowserServerState({
profile: {
name: "manual-cdp",
cdpUrl: "http://127.0.0.1:9222",
cdpHost: "127.0.0.1",
cdpIsLoopback: true,
cdpPort: 9222,
color: "#00AA00",
driver: "openclaw",
attachOnly: true,
},
resolvedOverrides: {
defaultProfile: "manual-cdp",
ssrfPolicy: {},
},
});
const ctx = createBrowserRouteContext({ getState: () => state });
const profile = ctx.forProfile("manual-cdp");
const { profile, state } = createAttachOnlyLoopbackProfile("http://127.0.0.1:9222");
isChromeReachable.mockResolvedValueOnce(true);
isChromeCdpReady.mockResolvedValueOnce(true);
@@ -195,24 +199,7 @@ describe("browser server-context ensureBrowserAvailable", () => {
const isChromeReachable = vi.mocked(chromeModule.isChromeReachable);
const isChromeCdpReady = vi.mocked(chromeModule.isChromeCdpReady);
const state = makeBrowserServerState({
profile: {
name: "manual-cdp",
cdpUrl: "ws://127.0.0.1:9222",
cdpHost: "127.0.0.1",
cdpIsLoopback: true,
cdpPort: 9222,
color: "#00AA00",
driver: "openclaw",
attachOnly: true,
},
resolvedOverrides: {
defaultProfile: "manual-cdp",
ssrfPolicy: {},
},
});
const ctx = createBrowserRouteContext({ getState: () => state });
const profile = ctx.forProfile("manual-cdp");
const { profile, state } = createAttachOnlyLoopbackProfile("ws://127.0.0.1:9222");
isChromeReachable.mockResolvedValueOnce(true);
isChromeCdpReady.mockResolvedValueOnce(true);

View File

@@ -8,6 +8,15 @@ import {
const deps: RemoteProfileTestDeps = await loadRemoteProfileTestDeps();
installRemoteProfileTestLifecycle(deps);
function page(targetId: string, url = `https://${targetId.toLowerCase()}.example`) {
return {
targetId,
title: targetId === "T1" ? "Tab 1" : targetId,
url,
type: "page" as const,
};
}
describe("browser remote profile tab ops via Playwright", () => {
it("uses Playwright tab operations when available", async () => {
const listPagesViaPlaywright = vi.fn(async () => [
@@ -91,10 +100,7 @@ describe("browser remote profile tab ops via Playwright", () => {
});
it("rejects stale targetId for remote profiles even when only one tab remains", async () => {
const responses = [
[{ targetId: "T1", title: "Tab 1", url: "https://example.com", type: "page" }],
[{ targetId: "T1", title: "Tab 1", url: "https://example.com", type: "page" }],
];
const responses = Array.from({ length: 2 }, () => [page("T1", "https://example.com")]);
const listPagesViaPlaywright = vi.fn(deps.createSequentialPageLister(responses));
vi.spyOn(deps.pwAiModule, "getPwAiModule").mockResolvedValue({
@@ -106,16 +112,7 @@ describe("browser remote profile tab ops via Playwright", () => {
});
it("keeps rejecting stale targetId for remote profiles when multiple tabs exist", async () => {
const responses = [
[
{ targetId: "A", title: "A", url: "https://a.example", type: "page" },
{ targetId: "B", title: "B", url: "https://b.example", type: "page" },
],
[
{ targetId: "A", title: "A", url: "https://a.example", type: "page" },
{ targetId: "B", title: "B", url: "https://b.example", type: "page" },
],
];
const responses = Array.from({ length: 2 }, () => [page("A"), page("B")]);
const listPagesViaPlaywright = vi.fn(deps.createSequentialPageLister(responses));
vi.spyOn(deps.pwAiModule, "getPwAiModule").mockResolvedValue({