diff --git a/src/browser/CdpCaptureDocuments.baseUrl.test.ts b/src/browser/CdpCaptureDocuments.baseUrl.test.ts index 7b3dc1868b..c2c9378b89 100644 --- a/src/browser/CdpCaptureDocuments.baseUrl.test.ts +++ b/src/browser/CdpCaptureDocuments.baseUrl.test.ts @@ -115,29 +115,6 @@ describe("DOM metadata document base URLs", () => { }); describe("empty form destinations with a document base URL", () => { - it.each([ - { nodeName: "FORM", attribute: "action" }, - { nodeName: "BUTTON", attribute: "formaction" }, - { nodeName: "INPUT", attribute: "formaction" }, - ])( - "resolves an empty $nodeName $attribute to the document", - ({ nodeName, attribute }) => { - const pageUrl = `${documentUrl}?selected=fixture#section`; - const baseUrl = `${origin}/assets/`; - const value = snapshot(baseUrl, "/assets/", pageUrl, ""); - value.strings[6] = nodeName; - value.strings[14] = attribute; - value.strings[15] = ""; - const result = capture(value); - expect(result.urls[3]).toMatchObject({ - attribute, - url: pageUrl, - destination_scope: "approved", - }); - expect(result.urls[1]?.url).toBe(baseUrl); - }, - ); - it("keeps an empty form action approved when the document base is foreign", () => { const foreign = "https://cdn.example.test/assets/"; const value = snapshot(foreign, foreign); diff --git a/tests/boundary/browser/cdpBrowserProvider.redirectBodyShapeLedger.test.ts b/src/browser/CdpCaptureEvents.redirectBodyShape.test.ts similarity index 91% rename from tests/boundary/browser/cdpBrowserProvider.redirectBodyShapeLedger.test.ts rename to src/browser/CdpCaptureEvents.redirectBodyShape.test.ts index 0636fc01bd..fb2da70a37 100644 --- a/tests/boundary/browser/cdpBrowserProvider.redirectBodyShapeLedger.test.ts +++ b/src/browser/CdpCaptureEvents.redirectBodyShape.test.ts @@ -1,7 +1,7 @@ import { expect, it } from "vitest"; -import { CdpCaptureEvents } from "../../../src/browser/CdpCaptureEvents.js"; -import { inspectWebPageInputSchema } from "../../../src/domain/browserObservation.js"; +import { CdpCaptureEvents } from "./CdpCaptureEvents.js"; +import { inspectWebPageInputSchema } from "../domain/browserObservation.js"; it("preserves an already captured response shape without marking it unavailable", () => { const origin = "http://127.0.0.1:43127"; diff --git a/tests/boundary/browser/cdpSafeMetadata.test.ts b/src/browser/CdpSafeMetadata.test.ts similarity index 99% rename from tests/boundary/browser/cdpSafeMetadata.test.ts rename to src/browser/CdpSafeMetadata.test.ts index 56df3421ea..0cff4e0944 100644 --- a/tests/boundary/browser/cdpSafeMetadata.test.ts +++ b/src/browser/CdpSafeMetadata.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { safeResponseMetadata } from "../../../src/browser/CdpSafeMetadata.js"; +import { safeResponseMetadata } from "./CdpSafeMetadata.js"; const origin = "https://app.example.test"; diff --git a/src/browser/PlaywrightScenarioActions.test.ts b/src/browser/PlaywrightScenarioActions.test.ts deleted file mode 100644 index 53ffd685d5..0000000000 --- a/src/browser/PlaywrightScenarioActions.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { expect, it } from "vitest"; -import type { Page } from "playwright-core"; - -import { BrowserScenarioSecrets } from "./BrowserScenarioSecrets.js"; -import { performPlaywrightScenarioAction } from "./PlaywrightScenarioActions.js"; -import { browserScenarioSchema } from "../domain/browserScenario.js"; - -it("uses exact accessible-name matching without a caller confirmation field", async () => { - const scenario = browserScenarioSchema.parse({ - browser: { mode: "launch", executable_path: "/opt/chromium/chrome" }, - start_url: { url: "https://app.example.test/" }, - actions: [ - { - step_id: "submit", - action: "click", - locator: { kind: "role", role: "button", name: "Submit" }, - }, - ], - }); - const calls: unknown[][] = []; - const page = { - getByRole: (...args: unknown[]) => { - calls.push(args); - return { click: async () => undefined }; - }, - } as unknown as Page; - const secrets = BrowserScenarioSecrets.resolve(scenario, {}); - if (secrets === undefined) throw new Error("No secret declarations expected"); - const action = scenario.actions.at(0); - if (action === undefined) throw new Error("Expected one scenario action"); - - await performPlaywrightScenarioAction({ - page, - action, - secrets, - }); - - expect(calls).toEqual([["button", { name: "Submit", exact: true }]]); -}); diff --git a/tests/boundary/browser/browserObservationService.test.ts b/tests/boundary/browser/browserObservationService.test.ts deleted file mode 100644 index 9dd1c6328d..0000000000 --- a/tests/boundary/browser/browserObservationService.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { listBrowserTargetsInputSchema } from "../../../src/domain/browserObservation.js"; -import { browserCaptureComparisonInputSchema } from "../../../src/domain/browserCaptureComparison.js"; -import { compareWebScreenshotsInputSchema } from "../../../src/domain/webScreenshot.js"; -import { BROWSER_TOOL_CONTRACTS } from "../../../src/contracts/browserToolContracts.js"; -import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js"; -import { - compareWebCaptureEvidence, - compareWebScreenshotEvidence, - listBrowserTargets, -} from "../../../src/application/BrowserObservationService.js"; - -describe("browser observation service prerequisites", () => { - it("reports a missing provider without requiring a permission grant", async () => { - const endpoint = "http://127.0.0.1:9222"; - const origin = "http://[::1]:3000"; - const result = await listBrowserTargets( - undefined, - listBrowserTargetsInputSchema.parse({ - cdp_endpoint: endpoint, - allowed_origins: [origin], - }), - ); - expect(result).toMatchObject({ - ok: false, - error: { - _tag: "AnalysisCapabilityUnavailableError", - }, - }); - }); - it("compares supplied scenarios and PNG artifacts without a CDP endpoint", async () => { - const browser = new CdpBrowserProvider(); - const captures = await compareWebCaptureEvidence( - browser, - browserCaptureComparisonInputSchema.parse( - example("compare_web_captures"), - ), - ); - const screenshots = await compareWebScreenshotEvidence( - browser, - compareWebScreenshotsInputSchema.parse( - example("compare_web_screenshots"), - ), - ); - for (const result of [captures, screenshots]) { - expect(result.ok).toBe(true); - if (!result.ok) throw result.error; - expect(result.value.parameters).not.toHaveProperty("cdp_endpoint"); - } - }); -}); - -const example = (name: string) => { - const input = BROWSER_TOOL_CONTRACTS.find((tool) => tool.name === name) - ?.examples[0]?.input; - if (input === undefined) throw new Error(`Missing example for ${name}`); - return input; -}; diff --git a/tests/boundary/browser/cdpBrowserProvider.discovery_authorization.test.ts b/tests/boundary/browser/cdpBrowserProvider.discovery_authorization.test.ts index 2cdc06561b..053cd673ea 100644 --- a/tests/boundary/browser/cdpBrowserProvider.discovery_authorization.test.ts +++ b/tests/boundary/browser/cdpBrowserProvider.discovery_authorization.test.ts @@ -1,4 +1,4 @@ -import { expect, it } from "vitest"; +import { describe, expect, it } from "vitest"; import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js"; import { @@ -6,9 +6,9 @@ import { listBrowserTargetsInputSchema, } from "../../../src/domain/browserObservation.js"; import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js"; -import { describeBrowser, trackBrowser } from "./cdpBrowserProvider.support.js"; +import { trackBrowser } from "./cdpBrowserProvider.support.js"; -describeBrowser("CdpBrowserProvider: discovery authorization 1", () => { +describe("CdpBrowserProvider: discovery authorization 1", () => { it("discovers pages and captures a selected target without an origin list", async () => { const browser = await startFakeCdpBrowser(); trackBrowser(browser); @@ -158,7 +158,7 @@ describeBrowser("CdpBrowserProvider: discovery authorization 1", () => { }); }); -describeBrowser("CdpBrowserProvider: discovery authorization 2", () => { +describe("CdpBrowserProvider: discovery authorization 2", () => { it("rejects empty browser attachment session identifiers", async () => { const browser = await startFakeCdpBrowser({ invalidAttachedSession: true }); trackBrowser(browser); diff --git a/tests/boundary/browser/cdpBrowserProvider.document_script.test.ts b/tests/boundary/browser/cdpBrowserProvider.document_script.test.ts index e770000b17..6911860494 100644 --- a/tests/boundary/browser/cdpBrowserProvider.document_script.test.ts +++ b/tests/boundary/browser/cdpBrowserProvider.document_script.test.ts @@ -1,4 +1,4 @@ -import { expect, it } from "vitest"; +import { describe, expect, it } from "vitest"; import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js"; import { inspectWebPageInputSchema } from "../../../src/domain/browserObservation.js"; @@ -6,20 +6,9 @@ import { analyzeWebBundleInputSchema } from "../../../src/domain/webBundleAnalys import { discoverWebMcpToolsInputSchema } from "../../../src/domain/webMcpDiscovery.js"; import { captureWebScreenshotInputSchema } from "../../../src/domain/webScreenshot.js"; import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js"; -import { describeBrowser, trackBrowser } from "./cdpBrowserProvider.support.js"; - -describeBrowser("CdpBrowserProvider: document script 1", () => { - it("rejects unknown WebMCP discovery fields", () => { - expect( - discoverWebMcpToolsInputSchema.safeParse({ - cdp_endpoint: "http://127.0.0.1:9222", - allowed_origins: ["https://example.test"], - target_id: "target", - unknown_field: true, - }).success, - ).toBe(false); - }); +import { trackBrowser } from "./cdpBrowserProvider.support.js"; +describe("CdpBrowserProvider: approved accessibility and source capture", () => { it("captures approved accessibility text inline", async () => { const browser = await startFakeCdpBrowser(); trackBrowser(browser); @@ -120,7 +109,7 @@ describeBrowser("CdpBrowserProvider: document script 1", () => { }); }); -describeBrowser("CdpBrowserProvider: transient WebMCP frames", () => { +describe("CdpBrowserProvider: transient WebMCP frames", () => { it("drops stale child tools and reports partial coverage when a blank commit remains", async () => { const browser = await startFakeCdpBrowser({ webMcpTools: true, @@ -205,7 +194,7 @@ describeBrowser("CdpBrowserProvider: transient WebMCP frames", () => { }); }); -describeBrowser("CdpBrowserProvider: document script 2", () => { +describe("CdpBrowserProvider: WebMCP discovery and completeness", () => { it("discovers untrusted WebMCP declarations without registering or invoking them", async () => { const browser = await startFakeCdpBrowser({ webMcpTools: true }); trackBrowser(browser); @@ -360,7 +349,7 @@ describeBrowser("CdpBrowserProvider: document script 2", () => { }); }); -describeBrowser("CdpBrowserProvider WebMCP inventory completeness", () => { +describe("CdpBrowserProvider WebMCP inventory completeness", () => { it("retains every field in a large declared WebMCP input schema", async () => { const propertyCount = 5_001; const browser = await startFakeCdpBrowser({ @@ -392,7 +381,7 @@ describeBrowser("CdpBrowserProvider WebMCP inventory completeness", () => { }); }); -describeBrowser("CdpBrowserProvider: document script 3", () => { +describe("CdpBrowserProvider: frame scope and screenshots", () => { it("removes WebMCP declarations after their child frame leaves scope", async () => { const browser = await startFakeCdpBrowser({ webMcpTools: true, @@ -474,7 +463,7 @@ describeBrowser("CdpBrowserProvider: document script 3", () => { }); }); -describeBrowser("CdpBrowserProvider: document script 4", () => { +describe("CdpBrowserProvider: complete passive inventories", () => { it("returns every frame, resource, worker, accessibility, and storage inventory item", async () => { const browser = await startFakeCdpBrowser({ extraCollections: true }); trackBrowser(browser); @@ -501,7 +490,7 @@ describeBrowser("CdpBrowserProvider: document script 4", () => { }); }); -describeBrowser("CdpBrowserProvider: WebMCP registration owners", () => { +describe("CdpBrowserProvider: WebMCP registration owners", () => { it.each(["retain", "remove-second"] as const)( "keeps same-URL frame owners separate: %s", async (mode) => { diff --git a/tests/boundary/browser/cdpBrowserProvider.lifecycle_failures.test.ts b/tests/boundary/browser/cdpBrowserProvider.lifecycle_failures.test.ts index d559b90887..3d5fc7a6f8 100644 --- a/tests/boundary/browser/cdpBrowserProvider.lifecycle_failures.test.ts +++ b/tests/boundary/browser/cdpBrowserProvider.lifecycle_failures.test.ts @@ -1,4 +1,4 @@ -import { expect, it } from "vitest"; +import { describe, expect, it } from "vitest"; import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js"; import { @@ -11,9 +11,9 @@ import { startFakeCdpBrowser, type FakeCdpBrowser, } from "../../fixtures/fakeCdpBrowser.js"; -import { describeBrowser, trackBrowser } from "./cdpBrowserProvider.support.js"; +import { trackBrowser } from "./cdpBrowserProvider.support.js"; -describeBrowser("CdpBrowserProvider: lifecycle failures 1", () => { +describe("CdpBrowserProvider: cancellation and session events", () => { it("projects direct-session cancellation onto the requested operation", async () => { const browser = await startFakeCdpBrowser({ pageScopedVersionWebSocket: true, @@ -132,7 +132,7 @@ describeBrowser("CdpBrowserProvider: lifecycle failures 1", () => { }); }); -describeBrowser("CdpBrowserProvider: lifecycle failures 2", () => { +describe("CdpBrowserProvider: session termination and scope changes", () => { it("ends a browser session when its flat target session detaches", async () => { const browser = await startFakeCdpBrowser({ sessionTimeline: "target_detached", @@ -243,7 +243,7 @@ describeBrowser("CdpBrowserProvider: lifecycle failures 2", () => { }); }); -describeBrowser("CdpBrowserProvider: lifecycle failures 3", () => { +describe("CdpBrowserProvider: protocol failures and cancellation cleanup", () => { it("degrades optional domains but propagates protocol and payload failures", async () => { const optional = await startFakeCdpBrowser({ unsupportedMethods: ["Accessibility.getFullAXTree"], @@ -373,7 +373,7 @@ describeBrowser("CdpBrowserProvider: lifecycle failures 3", () => { }); }); -describeBrowser("CdpBrowserProvider: lifecycle failures 4", () => { +describe("CdpBrowserProvider: navigation commit and target identity", () => { it("waits for navigation commit and rechecks the attached main-frame origin", async () => { const transitioning = await startFakeCdpBrowser({ transitionalFrameReads: 2, diff --git a/tests/boundary/browser/cdpBrowserProvider.network.test.ts b/tests/boundary/browser/cdpBrowserProvider.network.test.ts index dccd541bab..152b2ef14b 100644 --- a/tests/boundary/browser/cdpBrowserProvider.network.test.ts +++ b/tests/boundary/browser/cdpBrowserProvider.network.test.ts @@ -1,11 +1,11 @@ -import { expect, it } from "vitest"; +import { describe, expect, it } from "vitest"; import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js"; import { inspectWebPageInputSchema } from "../../../src/domain/browserObservation.js"; import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js"; -import { describeBrowser, trackBrowser } from "./cdpBrowserProvider.support.js"; +import { trackBrowser } from "./cdpBrowserProvider.support.js"; -describeBrowser("CdpBrowserProvider: network 1", () => { +describe("CdpBrowserProvider: network 1", () => { it("retains same-origin redirect hops on the one final request", async () => { const browser = await startFakeCdpBrowser({ redirectWithinOrigin: true }); trackBrowser(browser); diff --git a/tests/boundary/browser/cdpBrowserProvider.redirectErrors.test.ts b/tests/boundary/browser/cdpBrowserProvider.redirectErrors.test.ts index 60a89610e8..acfbe4b070 100644 --- a/tests/boundary/browser/cdpBrowserProvider.redirectErrors.test.ts +++ b/tests/boundary/browser/cdpBrowserProvider.redirectErrors.test.ts @@ -1,10 +1,10 @@ -import { expect, it } from "vitest"; +import { describe, expect, it } from "vitest"; import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js"; import { inspectWebPageInputSchema } from "../../../src/domain/browserObservation.js"; import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js"; import type { FakeOptions } from "../../fixtures/fakeCdpBrowserTypes.js"; -import { describeBrowser, trackBrowser } from "./cdpBrowserProvider.support.js"; +import { trackBrowser } from "./cdpBrowserProvider.support.js"; const inspectNetwork = async (options: FakeOptions) => { const browser = await startFakeCdpBrowser(options); @@ -22,7 +22,7 @@ const inspectNetwork = async (options: FakeOptions) => { return { browser, inspection: result.value }; }; -describeBrowser("CdpBrowserProvider: redirect errors", () => { +describe("CdpBrowserProvider: redirect errors", () => { it.each([ { label: "missing", url: undefined, reason: "invalid_protocol_value" }, { label: "malformed", url: "http://%", reason: "invalid_protocol_value" }, diff --git a/tests/boundary/browser/cdpBrowserProvider.sensitive_data.test.ts b/tests/boundary/browser/cdpBrowserProvider.sensitive_data.test.ts index 20bcabaac2..d477643814 100644 --- a/tests/boundary/browser/cdpBrowserProvider.sensitive_data.test.ts +++ b/tests/boundary/browser/cdpBrowserProvider.sensitive_data.test.ts @@ -1,4 +1,4 @@ -import { expect, it } from "vitest"; +import { describe, expect, it } from "vitest"; import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js"; import { @@ -9,9 +9,9 @@ import { startFakeCdpBrowser, type FakeCdpBrowser, } from "../../fixtures/fakeCdpBrowser.js"; -import { describeBrowser, trackBrowser } from "./cdpBrowserProvider.support.js"; +import { trackBrowser } from "./cdpBrowserProvider.support.js"; -describeBrowser("CdpBrowserProvider: sensitive data 1", () => { +describe("CdpBrowserProvider: passive evidence redaction", () => { it("captures bounded passive evidence without retaining sensitive values", async () => { const browser = await startFakeCdpBrowser({ binaryWebSocketEvent: true, @@ -158,7 +158,7 @@ describeBrowser("CdpBrowserProvider: sensitive data 1", () => { }); }); -describeBrowser("CdpBrowserProvider: page cookie scope", () => { +describe("CdpBrowserProvider: page cookie scope", () => { it("uses the attached page URL for cookies and its origin for DOM storage", async () => { const browser = await startFakeCdpBrowser(); trackBrowser(browser); @@ -190,7 +190,7 @@ describeBrowser("CdpBrowserProvider: page cookie scope", () => { }); }); -describeBrowser("CdpBrowserProvider: complete storage fingerprints", () => { +describe("CdpBrowserProvider: complete storage fingerprints", () => { it("fingerprints cache bodies above the former byte ceiling", async () => { const body = "x".repeat(64 * 1_024 + 1); const browser = await startFakeCdpBrowser({ cachedResponseBody: body }); @@ -217,7 +217,7 @@ describeBrowser("CdpBrowserProvider: complete storage fingerprints", () => { }); }); -describeBrowser("CdpBrowserProvider: sensitive data 2", () => { +describe("CdpBrowserProvider: explicitly requested text and payload shapes", () => { it("captures requested console text verbatim and value-free payload shapes", async () => { const browser = await startFakeCdpBrowser({ sensitiveShapes: true, @@ -309,45 +309,7 @@ describeBrowser("CdpBrowserProvider: sensitive data 2", () => { }); }); -describeBrowser("CdpBrowserProvider: complete sensitive data", () => { - it("returns complete requested text and payload shapes verbatim", async () => { - const browser = await startFakeCdpBrowser({ sensitiveShapes: true }); - trackBrowser(browser); - const request = inspectWebPageInputSchema.parse({ - cdp_endpoint: browser.endpoint, - allowed_origins: [browser.allowedOrigin], - target_id: "allowed-page", - observation_ms: 0, - include_console_text: true, - include_json_body_shapes: true, - include_websocket_shapes: true, - }); - const result = await new CdpBrowserProvider().inspectPage(request); - - if (!result.ok) throw result.error; - expect(result.value.console.events[0]?.text_capture).toEqual({ - status: "included", - values: [ - { - argument_index: 0, - type: "string", - text: "authorization=Bearer console-secret", - }, - { argument_index: 1, type: "number", text: "42" }, - ], - retained_bytes: - Buffer.byteLength("authorization=Bearer console-secret") + - Buffer.byteLength("42"), - }); - expect(result.value.network.requests[0]?.body_shapes.status).toBe( - "included", - ); - expect(result.value.network.websocket_events[0]?.payload_shape).toEqual({ - format: "json", - json_shape: expect.objectContaining({ root_type: "object" }), - }); - }); - +describe("CdpBrowserProvider: malformed approved payloads", () => { it("fails closed on malformed approved response and binary payload encodings", async () => { const browser = await startFakeCdpBrowser({ invalidResponseBodyBase64: true, diff --git a/tests/boundary/browser/cdpBrowserProvider.support.ts b/tests/boundary/browser/cdpBrowserProvider.support.ts index 1afe9e0747..8e59d2d031 100644 --- a/tests/boundary/browser/cdpBrowserProvider.support.ts +++ b/tests/boundary/browser/cdpBrowserProvider.support.ts @@ -1,4 +1,4 @@ -import { afterEach, describe } from "vitest"; +import { afterEach } from "vitest"; import type { FakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js"; @@ -11,5 +11,3 @@ afterEach(async () => { export const trackBrowser = (browser: FakeCdpBrowser): void => { browsers.push(browser); }; - -export const describeBrowser = describe; diff --git a/tests/boundary/browser/cdpBrowserProvider.timelineEvents.test.ts b/tests/boundary/browser/cdpBrowserProvider.timelineEvents.test.ts index c77af5985a..e9f1f9a60f 100644 --- a/tests/boundary/browser/cdpBrowserProvider.timelineEvents.test.ts +++ b/tests/boundary/browser/cdpBrowserProvider.timelineEvents.test.ts @@ -1,29 +1,11 @@ -import { expect, it } from "vitest"; +import { describe, expect, it } from "vitest"; import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js"; import { observeWebSessionInputSchema } from "../../../src/domain/browserSession.js"; import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js"; -import { describeBrowser, trackBrowser } from "./cdpBrowserProvider.support.js"; - -describeBrowser("CdpBrowserProvider navigation timeline", () => { - it("accepts caller-selected windows and rejects the removed event-count option", () => { - const accepted = observeWebSessionInputSchema.safeParse({ - cdp_endpoint: "http://127.0.0.1:9222", - allowed_origins: ["https://app.example.test"], - target_id: "page-1", - observation_ms: 120_000, - }); - expect(accepted.success).toBe(true); - expect( - observeWebSessionInputSchema.safeParse({ - cdp_endpoint: "http://127.0.0.1:9222", - allowed_origins: ["https://app.example.test"], - target_id: "page-1", - max_timeline_events: 1, - }).success, - ).toBe(false); - }); +import { trackBrowser } from "./cdpBrowserProvider.support.js"; +describe("CdpBrowserProvider navigation timeline", () => { it("returns every event beyond the former default collection cap", async () => { const additionalEvents = 2_001; const browser = await startFakeCdpBrowser({ diff --git a/tests/boundary/browser/cdpCaptureDocuments.test.ts b/tests/boundary/browser/cdpCaptureDocuments.test.ts index 92612d9be9..9a11c45102 100644 --- a/tests/boundary/browser/cdpCaptureDocuments.test.ts +++ b/tests/boundary/browser/cdpCaptureDocuments.test.ts @@ -1,52 +1,61 @@ import { expect, it } from "vitest"; -import { CdpCaptureCompleteness } from "../../../src/browser/CdpCaptureCompleteness.js"; -import { - captureFrames, - captureResources, -} from "../../../src/browser/CdpCaptureDocuments.js"; +import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js"; +import { inspectWebPageInputSchema } from "../../../src/domain/browserObservation.js"; +import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js"; +import { trackBrowser } from "./cdpBrowserProvider.support.js"; -it("reports malformed child frame entries instead of silently completing inventory", () => { - const completeness = new CdpCaptureCompleteness(); - const captured = captureFrames( - { - frameTree: { - frame: { - id: "main", - url: "https://example.test/", - }, - childFrames: [ - null, - { frame: { id: "child", url: "https://example.test/child" } }, - ], - }, - }, - new Set(["https://example.test"]), - undefined, - completeness, +it("reports malformed child frame entries through the CDP boundary", async () => { + const browser = await startFakeCdpBrowser({ + commandResult: (command, origin) => + command.method === "Page.getFrameTree" + ? { + frameTree: { + frame: { id: "main", url: `${origin}/` }, + childFrames: [ + null, + { frame: { id: "child", url: `${origin}/child` } }, + ], + }, + } + : undefined, + }); + trackBrowser(browser); + + const result = await new CdpBrowserProvider().inspectPage( + inspectWebPageInputSchema.parse({ + cdp_endpoint: browser.endpoint, + allowed_origins: [browser.allowedOrigin], + target_id: "allowed-page", + observation_ms: 0, + }), ); - expect(captured.items.map(({ frame_id }) => frame_id)).toEqual([ + if (!result.ok) throw result.error; + expect(result.value.frames.map(({ frame_id }) => frame_id)).toEqual([ "main", "child", ]); - expect(completeness.snapshot().unavailable_sections).toContain("frames"); + expect(result.value.completeness.unavailable_sections).toContain("frames"); }); -it("reports missing top-level frame trees for frame and resource inventories", () => { - const completeness = new CdpCaptureCompleteness(); - expect( - captureFrames( - {}, - new Set(["https://example.test"]), - undefined, - completeness, - ).items, - ).toEqual([]); - expect( - captureResources({}, new Set(["https://example.test"]), completeness).items, - ).toEqual([]); - expect(completeness.snapshot().unavailable_sections).toEqual( - expect.arrayContaining(["frames", "resources"]), +it("marks a missing resource frame tree incomplete through CDP", async () => { + const browser = await startFakeCdpBrowser({ + commandResult: (command) => + command.method === "Page.getResourceTree" ? {} : undefined, + }); + trackBrowser(browser); + + const result = await new CdpBrowserProvider().inspectPage( + inspectWebPageInputSchema.parse({ + cdp_endpoint: browser.endpoint, + allowed_origins: [browser.allowedOrigin], + target_id: "allowed-page", + observation_ms: 0, + }), ); + + if (!result.ok) throw result.error; + expect(result.value.resources).toEqual([]); + expect(result.value.completeness.unavailable_sections).toContain("resources"); }); diff --git a/tests/boundary/browser/cdpCaptureNavigation.test.ts b/tests/boundary/browser/cdpCaptureNavigation.test.ts index 070d6b1122..4716076406 100644 --- a/tests/boundary/browser/cdpCaptureNavigation.test.ts +++ b/tests/boundary/browser/cdpCaptureNavigation.test.ts @@ -1,65 +1,8 @@ import { expect, it, onTestFinished } from "vitest"; -import { CdpCaptureEvents } from "../../../src/browser/CdpCaptureEvents.js"; import { inspectWebPageInputSchema } from "../../../src/domain/browserObservation.js"; import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js"; import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js"; -import type { CdpEvent } from "../../../src/browser/CdpConnection.js"; - -const events = (): CdpCaptureEvents => - new CdpCaptureEvents( - inspectWebPageInputSchema.parse({ - cdp_endpoint: "http://127.0.0.1:9222", - allowed_origins: ["https://example.test"], - target_id: "page-1", - }), - new Set(["https://example.test"]), - ); - -const frameNavigated = (url: string, loaderId: string): CdpEvent => ({ - method: "Page.frameNavigated", - params: { - frame: { id: "main", url, loaderId }, - }, -}); - -it("ignores a repeated frameNavigated for the document already committed", () => { - const state = events(); - state.beginAuthorizedFrame("main"); - state.ingest(frameNavigated("https://example.test/page", "loader-1")); - expect(state.navigationDuringCapture).toBe(true); - state.navigationDuringCapture = false; - - // A re-fire with the same URL and loader is not a navigation. - state.ingest(frameNavigated("https://example.test/page", "loader-1")); - expect(state.navigationDuringCapture).toBe(false); -}); - -it("still reports a genuine change of document", () => { - const state = events(); - state.beginAuthorizedFrame("main"); - state.ingest(frameNavigated("https://example.test/page", "loader-1")); - state.navigationDuringCapture = false; - state.ingest(frameNavigated("https://example.test/other", "loader-2")); - expect(state.navigationDuringCapture).toBe(true); -}); - -it("does not forget a navigation that happened during the observation window", () => { - const state = events(); - state.beginAuthorizedFrame("main"); - state.ingest(frameNavigated("https://example.test/page", "loader-1")); - // The final capture re-attaches to the resolved frame; clearing the flag here - // used to report a clean capture while carrying pre-navigation state. - state.beginFinalCapture("main"); - expect(state.navigationDuringCapture).toBe(true); -}); - -it("keeps a settled observation window clean", () => { - const state = events(); - state.beginAuthorizedFrame("main"); - state.beginFinalCapture("main"); - expect(state.navigationDuringCapture).toBe(false); -}); it("captures a page without aborting on a duplicate frame event", async () => { const browser = await startFakeCdpBrowser({ sessionTimeline: "same_origin" }); diff --git a/tests/boundary/browser/cdpElectronProvider.test.ts b/tests/boundary/browser/cdpElectronProvider.test.ts index b15ffaa439..3a8cdffdb4 100644 --- a/tests/boundary/browser/cdpElectronProvider.test.ts +++ b/tests/boundary/browser/cdpElectronProvider.test.ts @@ -199,25 +199,6 @@ describe("CdpElectronProvider target selection", () => { }); }); -describe("Electron request schemas", () => { - it("rejects unknown request fields", () => { - expect( - listElectronTargetsInputSchema.safeParse({ - cdp_endpoint: "http://127.0.0.1:9223", - unknown_field: true, - }).success, - ).toBe(false); - expect( - inspectElectronPageInputSchema.safeParse({ - cdp_endpoint: "http://127.0.0.1:9223", - target_id: "page-1", - observation_ms: 0, - unknown_field: true, - }).success, - ).toBe(false); - }); -}); - describe("CdpElectronProvider capture", () => { it("captures Electron script source only after separate approval", async () => { const root = await electronFixture(); diff --git a/tests/boundary/browser/electronActiveHook.test.ts b/tests/boundary/browser/electronActiveHook.test.ts index 75ad44f839..2afaebc7ad 100644 --- a/tests/boundary/browser/electronActiveHook.test.ts +++ b/tests/boundary/browser/electronActiveHook.test.ts @@ -122,8 +122,11 @@ it("retains active hook lifecycle and IPC evidence without a count ceiling", asy const script = join(root, "exercise.cjs"); await writeFile(script, activeCaptureSource); const hook = join(process.cwd(), "scripts/electron-active-hook.cjs"); - const output = await runNode(hook, script, root, root); - const result = JSON.parse(output.trim().split("\n").at(-1) ?? "null") as { + const execution = await runNode(hook, script, root, root); + expect(execution.code).toBe(0); + const result = JSON.parse( + execution.stdout.trim().split("\n").at(-1) ?? "null", + ) as { readonly default_popup_decision: { readonly action: string }; readonly popup_decision: { readonly action: string }; readonly snapshot: { @@ -181,7 +184,7 @@ it("preserves fatal process termination after recording uncaught exceptions", as const script = join(root, "fatal.cjs"); await writeFile(script, 'throw new Error("fatal-hook-secret");\n'); const hook = join(process.cwd(), "scripts/electron-active-hook.cjs"); - const result = await runNodeStatus(hook, script, root, root); + const result = await runNode(hook, script, root, root); expect(result.code).not.toBe(0); expect(result.stderr).toContain("fatal-hook-secret"); @@ -192,7 +195,11 @@ const runNode = ( script: string, moduleRoot: string, cwd: string, -): Promise => +): Promise<{ + readonly code: number | null; + readonly stdout: string; + readonly stderr: string; +}> => new Promise((resolve, reject) => { const child = spawn(process.execPath, ["-r", hook, script], { cwd, @@ -207,44 +214,11 @@ const runNode = ( child.stdout.on("data", (chunk: Buffer) => stdout.push(chunk)); child.stderr.on("data", (chunk: Buffer) => stderr.push(chunk)); child.once("error", reject); - child.once("close", (code) => { - if (code === 0) { - const text = Buffer.concat(stdout).toString("utf8"); - if (text.length > 0) resolve(text); - else - reject( - new Error( - `hook fixture produced no output: ${Buffer.concat(stderr).toString("utf8")}`, - ), - ); - } else - reject( - new Error( - `hook fixture exited with ${String(code)}: ${Buffer.concat(stderr).toString("utf8")}`, - ), - ); - }); - }); - -const runNodeStatus = ( - hook: string, - script: string, - moduleRoot: string, - cwd: string, -): Promise<{ readonly code: number | null; readonly stderr: string }> => - new Promise((resolve, reject) => { - const child = spawn(process.execPath, ["-r", hook, script], { - cwd, - env: { - ...process.env, - NODE_PATH: moduleRoot, - }, - stdio: ["ignore", "pipe", "pipe"], - }); - const stderr: Buffer[] = []; - child.stderr.on("data", (chunk: Buffer) => stderr.push(chunk)); - child.once("error", reject); child.once("close", (code) => - resolve({ code, stderr: Buffer.concat(stderr).toString("utf8") }), + resolve({ + code, + stdout: Buffer.concat(stdout).toString("utf8"), + stderr: Buffer.concat(stderr).toString("utf8"), + }), ); }); diff --git a/tests/boundary/browser/webCaptureDiff.test.ts b/tests/boundary/browser/webCaptureDiff.test.ts index e9289256ff..935bc7ae5a 100644 --- a/tests/boundary/browser/webCaptureDiff.test.ts +++ b/tests/boundary/browser/webCaptureDiff.test.ts @@ -22,16 +22,6 @@ afterEach(async () => { }); describe("web capture diff", () => { - it("rejects caller limits that would omit difference records", () => { - expect( - compareWebCapturesInputSchema.safeParse({ - before: {}, - after: {}, - max_changes: 1, - }).success, - ).toBe(false); - }); - it("reports stable observed changes while preserving unknown dimensions", async () => { const browser = await startFakeCdpBrowser(); browsers.push(browser);