From 0010b246c01899915b48861741bb9a660e8f161a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 20 Apr 2026 16:29:20 +0100 Subject: [PATCH] test: share browser tab route fixtures --- .../browser/src/browser/routes/tabs.test.ts | 226 ++++++------------ 1 file changed, 78 insertions(+), 148 deletions(-) diff --git a/extensions/browser/src/browser/routes/tabs.test.ts b/extensions/browser/src/browser/routes/tabs.test.ts index bfc118579c3..be3eaacd495 100644 --- a/extensions/browser/src/browser/routes/tabs.test.ts +++ b/extensions/browser/src/browser/routes/tabs.test.ts @@ -13,6 +13,51 @@ vi.mock("../navigation-guard.js", () => navigationGuardMocks); const { registerBrowserTabRoutes } = await import("./tabs.js"); +type ProfileContext = ReturnType; +type TabFixture = { + targetId: string; + title: string; + url: string; + type: "page"; +}; + +const publicTab = (overrides: Partial = {}): TabFixture => ({ + targetId: "T1", + title: "Public", + url: "https://example.com", + type: "page", + ...overrides, +}); + +const internalTab = (overrides: Partial = {}): 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>) { return { ...baseProfileContext(), @@ -55,10 +100,7 @@ function baseProfileContext() { }; } -function createRouteContext( - profileCtx: ReturnType, - 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; - profileCtx: ReturnType; +async function callTabsRoute(params: { + method: "get" | "post"; + path: "/tabs" | "/tabs/action" | "/tabs/focus"; + body?: Record; + 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; +async function callTabsAction(params: { + body: Record; + 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; + profileCtx: ProfileContext; body: Record; 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", }, ], });