mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 10:20:42 +00:00
test: share browser tab route fixtures
This commit is contained in:
@@ -13,6 +13,51 @@ vi.mock("../navigation-guard.js", () => navigationGuardMocks);
|
||||
|
||||
const { registerBrowserTabRoutes } = await import("./tabs.js");
|
||||
|
||||
type ProfileContext = ReturnType<typeof createProfileContext>;
|
||||
type TabFixture = {
|
||||
targetId: string;
|
||||
title: string;
|
||||
url: string;
|
||||
type: "page";
|
||||
};
|
||||
|
||||
const publicTab = (overrides: Partial<TabFixture> = {}): TabFixture => ({
|
||||
targetId: "T1",
|
||||
title: "Public",
|
||||
url: "https://example.com",
|
||||
type: "page",
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const internalTab = (overrides: Partial<TabFixture> = {}): TabFixture => ({
|
||||
targetId: "T2",
|
||||
title: "Internal",
|
||||
url: "http://169.254.169.254/latest/meta-data/",
|
||||
type: "page",
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const createProfileWithTabs = (tabs: TabFixture[]) =>
|
||||
createProfileContext({
|
||||
listTabs: vi.fn(async () => tabs),
|
||||
});
|
||||
|
||||
async function expectBrowserNotRunningAction(action: "close" | "select") {
|
||||
const profileCtx = createProfileContext({
|
||||
isReachable: vi.fn(async () => false),
|
||||
});
|
||||
|
||||
const response = await callTabsAction({
|
||||
body: { action, index: 0 },
|
||||
profileCtx,
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(409);
|
||||
expect(response.body).toEqual({ error: "browser not running" });
|
||||
expect(profileCtx.listTabs).not.toHaveBeenCalled();
|
||||
expect(action === "close" ? profileCtx.closeTab : profileCtx.focusTab).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
function createProfileContext(overrides?: Partial<ReturnType<typeof baseProfileContext>>) {
|
||||
return {
|
||||
...baseProfileContext(),
|
||||
@@ -55,10 +100,7 @@ function baseProfileContext() {
|
||||
};
|
||||
}
|
||||
|
||||
function createRouteContext(
|
||||
profileCtx: ReturnType<typeof createProfileContext>,
|
||||
options?: { ssrfPolicy?: unknown },
|
||||
) {
|
||||
function createRouteContext(profileCtx: ProfileContext, options?: { ssrfPolicy?: unknown }) {
|
||||
return {
|
||||
state: () => ({ resolved: { ssrfPolicy: options?.ssrfPolicy } }),
|
||||
forProfile: () => profileCtx,
|
||||
@@ -83,57 +125,45 @@ function createRouteContext(
|
||||
};
|
||||
}
|
||||
|
||||
async function callTabsAction(params: {
|
||||
body: Record<string, unknown>;
|
||||
profileCtx: ReturnType<typeof createProfileContext>;
|
||||
async function callTabsRoute(params: {
|
||||
method: "get" | "post";
|
||||
path: "/tabs" | "/tabs/action" | "/tabs/focus";
|
||||
body?: Record<string, unknown>;
|
||||
profileCtx: ProfileContext;
|
||||
ssrfPolicy?: unknown;
|
||||
}) {
|
||||
const { app, postHandlers } = createBrowserRouteApp();
|
||||
const { app, getHandlers, postHandlers } = createBrowserRouteApp();
|
||||
registerBrowserTabRoutes(
|
||||
app,
|
||||
createRouteContext(params.profileCtx, { ssrfPolicy: params.ssrfPolicy }) as never,
|
||||
);
|
||||
const handler = postHandlers.get("/tabs/action");
|
||||
const handler =
|
||||
params.method === "get" ? getHandlers.get(params.path) : postHandlers.get(params.path);
|
||||
expect(handler).toBeTypeOf("function");
|
||||
|
||||
const response = createBrowserRouteResponse();
|
||||
await handler?.({ params: {}, query: {}, body: params.body }, response.res);
|
||||
await handler?.({ params: {}, query: {}, body: params.body ?? {} }, response.res);
|
||||
return response;
|
||||
}
|
||||
|
||||
async function callTabsList(params: {
|
||||
profileCtx: ReturnType<typeof createProfileContext>;
|
||||
async function callTabsAction(params: {
|
||||
body: Record<string, unknown>;
|
||||
profileCtx: ProfileContext;
|
||||
ssrfPolicy?: unknown;
|
||||
}) {
|
||||
const { app, getHandlers } = createBrowserRouteApp();
|
||||
registerBrowserTabRoutes(
|
||||
app,
|
||||
createRouteContext(params.profileCtx, { ssrfPolicy: params.ssrfPolicy }) as never,
|
||||
);
|
||||
const handler = getHandlers.get("/tabs");
|
||||
expect(handler).toBeTypeOf("function");
|
||||
return await callTabsRoute({ ...params, method: "post", path: "/tabs/action" });
|
||||
}
|
||||
|
||||
const response = createBrowserRouteResponse();
|
||||
await handler?.({ params: {}, query: {}, body: {} }, response.res);
|
||||
return response;
|
||||
async function callTabsList(params: { profileCtx: ProfileContext; ssrfPolicy?: unknown }) {
|
||||
return await callTabsRoute({ ...params, method: "get", path: "/tabs" });
|
||||
}
|
||||
|
||||
async function callTabsFocus(params: {
|
||||
profileCtx: ReturnType<typeof createProfileContext>;
|
||||
profileCtx: ProfileContext;
|
||||
body: Record<string, unknown>;
|
||||
ssrfPolicy?: unknown;
|
||||
}) {
|
||||
const { app, postHandlers } = createBrowserRouteApp();
|
||||
registerBrowserTabRoutes(
|
||||
app,
|
||||
createRouteContext(params.profileCtx, { ssrfPolicy: params.ssrfPolicy }) as never,
|
||||
);
|
||||
const handler = postHandlers.get("/tabs/focus");
|
||||
expect(handler).toBeTypeOf("function");
|
||||
|
||||
const response = createBrowserRouteResponse();
|
||||
await handler?.({ params: {}, query: {}, body: params.body }, response.res);
|
||||
return response;
|
||||
return await callTabsRoute({ ...params, method: "post", path: "/tabs/focus" });
|
||||
}
|
||||
|
||||
describe("browser tab routes", () => {
|
||||
@@ -147,35 +177,11 @@ describe("browser tab routes", () => {
|
||||
});
|
||||
|
||||
it("returns browser-not-running for close when the browser is not reachable", async () => {
|
||||
const profileCtx = createProfileContext({
|
||||
isReachable: vi.fn(async () => false),
|
||||
});
|
||||
|
||||
const response = await callTabsAction({
|
||||
body: { action: "close", index: 0 },
|
||||
profileCtx,
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(409);
|
||||
expect(response.body).toEqual({ error: "browser not running" });
|
||||
expect(profileCtx.listTabs).not.toHaveBeenCalled();
|
||||
expect(profileCtx.closeTab).not.toHaveBeenCalled();
|
||||
await expectBrowserNotRunningAction("close");
|
||||
});
|
||||
|
||||
it("returns browser-not-running for select when the browser is not reachable", async () => {
|
||||
const profileCtx = createProfileContext({
|
||||
isReachable: vi.fn(async () => false),
|
||||
});
|
||||
|
||||
const response = await callTabsAction({
|
||||
body: { action: "select", index: 0 },
|
||||
profileCtx,
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(409);
|
||||
expect(response.body).toEqual({ error: "browser not running" });
|
||||
expect(profileCtx.listTabs).not.toHaveBeenCalled();
|
||||
expect(profileCtx.focusTab).not.toHaveBeenCalled();
|
||||
await expectBrowserNotRunningAction("select");
|
||||
});
|
||||
|
||||
it("redacts blocked tab URLs from GET /tabs", async () => {
|
||||
@@ -187,22 +193,7 @@ describe("browser tab routes", () => {
|
||||
}
|
||||
},
|
||||
);
|
||||
const profileCtx = createProfileContext({
|
||||
listTabs: vi.fn(async () => [
|
||||
{
|
||||
targetId: "T1",
|
||||
title: "Public",
|
||||
url: "https://example.com",
|
||||
type: "page",
|
||||
},
|
||||
{
|
||||
targetId: "T2",
|
||||
title: "Internal",
|
||||
url: "http://169.254.169.254/latest/meta-data/",
|
||||
type: "page",
|
||||
},
|
||||
]),
|
||||
});
|
||||
const profileCtx = createProfileWithTabs([publicTab(), internalTab()]);
|
||||
|
||||
const response = await callTabsList({
|
||||
profileCtx,
|
||||
@@ -214,16 +205,11 @@ describe("browser tab routes", () => {
|
||||
running: true,
|
||||
tabs: [
|
||||
{
|
||||
targetId: "T1",
|
||||
title: "Public",
|
||||
url: "https://example.com",
|
||||
type: "page",
|
||||
...publicTab(),
|
||||
},
|
||||
{
|
||||
targetId: "T2",
|
||||
title: "Internal",
|
||||
...internalTab(),
|
||||
url: "",
|
||||
type: "page",
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -234,16 +220,7 @@ describe("browser tab routes", () => {
|
||||
navigationGuardMocks.assertBrowserNavigationResultAllowed.mockRejectedValueOnce(
|
||||
new Error("blocked"),
|
||||
);
|
||||
const profileCtx = createProfileContext({
|
||||
listTabs: vi.fn(async () => [
|
||||
{
|
||||
targetId: "T2",
|
||||
title: "Internal",
|
||||
url: "http://169.254.169.254/latest/meta-data/",
|
||||
type: "page",
|
||||
},
|
||||
]),
|
||||
});
|
||||
const profileCtx = createProfileWithTabs([internalTab()]);
|
||||
|
||||
const response = await callTabsFocus({
|
||||
profileCtx,
|
||||
@@ -303,22 +280,7 @@ describe("browser tab routes", () => {
|
||||
navigationGuardMocks.assertBrowserNavigationResultAllowed.mockRejectedValueOnce(
|
||||
new Error("blocked"),
|
||||
);
|
||||
const profileCtx = createProfileContext({
|
||||
listTabs: vi.fn(async () => [
|
||||
{
|
||||
targetId: "T1",
|
||||
title: "Public",
|
||||
url: "https://example.com",
|
||||
type: "page",
|
||||
},
|
||||
{
|
||||
targetId: "T2",
|
||||
title: "Internal",
|
||||
url: "http://169.254.169.254/latest/meta-data/",
|
||||
type: "page",
|
||||
},
|
||||
]),
|
||||
});
|
||||
const profileCtx = createProfileWithTabs([publicTab(), internalTab()]);
|
||||
|
||||
const response = await callTabsAction({
|
||||
body: { action: "select", index: 1 },
|
||||
@@ -332,14 +294,7 @@ describe("browser tab routes", () => {
|
||||
|
||||
it("does not run SSRF result validation for /tabs/focus when policy is not configured", async () => {
|
||||
const profileCtx = createProfileContext({
|
||||
listTabs: vi.fn(async () => [
|
||||
{
|
||||
targetId: "T2",
|
||||
title: "Internal",
|
||||
url: "http://169.254.169.254/latest/meta-data/",
|
||||
type: "page",
|
||||
},
|
||||
]),
|
||||
listTabs: vi.fn(async () => [internalTab()]),
|
||||
});
|
||||
|
||||
const response = await callTabsFocus({
|
||||
@@ -356,20 +311,7 @@ describe("browser tab routes", () => {
|
||||
|
||||
it("does not run SSRF result validation for /tabs/action select when policy is not configured", async () => {
|
||||
const profileCtx = createProfileContext({
|
||||
listTabs: vi.fn(async () => [
|
||||
{
|
||||
targetId: "T1",
|
||||
title: "Public",
|
||||
url: "https://example.com",
|
||||
type: "page",
|
||||
},
|
||||
{
|
||||
targetId: "T2",
|
||||
title: "Internal",
|
||||
url: "http://169.254.169.254/latest/meta-data/",
|
||||
type: "page",
|
||||
},
|
||||
]),
|
||||
listTabs: vi.fn(async () => [publicTab(), internalTab()]),
|
||||
});
|
||||
|
||||
const response = await callTabsAction({
|
||||
@@ -394,18 +336,11 @@ describe("browser tab routes", () => {
|
||||
);
|
||||
const profileCtx = createProfileContext({
|
||||
listTabs: vi.fn(async () => [
|
||||
{
|
||||
targetId: "T1",
|
||||
title: "Public",
|
||||
url: "https://example.com",
|
||||
type: "page",
|
||||
},
|
||||
{
|
||||
targetId: "T2",
|
||||
publicTab(),
|
||||
internalTab({
|
||||
title: "Private Admin",
|
||||
url: "http://10.0.0.5/admin",
|
||||
type: "page",
|
||||
},
|
||||
}),
|
||||
]),
|
||||
});
|
||||
|
||||
@@ -420,16 +355,11 @@ describe("browser tab routes", () => {
|
||||
ok: true,
|
||||
tabs: [
|
||||
{
|
||||
targetId: "T1",
|
||||
title: "Public",
|
||||
url: "https://example.com",
|
||||
type: "page",
|
||||
...publicTab(),
|
||||
},
|
||||
{
|
||||
targetId: "T2",
|
||||
title: "Private Admin",
|
||||
...internalTab({ title: "Private Admin" }),
|
||||
url: "",
|
||||
type: "page",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user