mirror of
https://github.com/morluto/rea.git
synced 2026-10-31 03:45:39 +00:00
Read screenshot identity from the selected frame-tree root instead of a filtered inventory that could substitute a child. Reuse the frame record parser for initial and event identities, rejecting malformed roots before capture while preserving known origin refusals. Share explicit origin-scope diagnostics across CDP workflows without changing policy or conflating startup uncertainty, document replacement, worker protection, and Inspector restrictions. Document the recovery and make screenshot navigation fixture ordering explicit. Validation: 577 focused checks passed across 13 files; one platform-specific invalid-byte-filename case skipped. Build, typecheck, lint, formatting, generated documentation checks, README conformance, and discovery passed. No full suite or live browser/provider execution.
116 lines
4.3 KiB
TypeScript
116 lines
4.3 KiB
TypeScript
import { expect, it } from "vitest";
|
|
import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js";
|
|
import { inspectWebPageInputSchema } from "../../../src/domain/browserObservation.js";
|
|
import { projectAnalysisError } from "../../../src/domain/analysisErrorProjection.js";
|
|
import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js";
|
|
import { frameNavigated, trackBrowser } from "./cdpBrowserProvider.support.js";
|
|
|
|
it.each([
|
|
{ method: "Network.enable", sourceFails: false },
|
|
{ method: "DOMSnapshot.captureSnapshot", sourceFails: false },
|
|
{ method: "DOMSnapshot.captureSnapshot", sourceFails: true },
|
|
])(
|
|
"explains observed origin refusal during $method with source failure $sourceFails",
|
|
async ({ method: stage, sourceFails }) => {
|
|
const browser = await startFakeCdpBrowser({
|
|
eventsBeforeReply: [stage],
|
|
commandEvents: (command) =>
|
|
command.method === stage
|
|
? [
|
|
frameNavigated(command, {
|
|
id: "frame-main",
|
|
loaderId: "outside-document",
|
|
url: "https://outside.example/page",
|
|
}),
|
|
]
|
|
: undefined,
|
|
commandError: ({ method }) =>
|
|
sourceFails && method === "Debugger.getScriptSource"
|
|
? { code: -32000, message: "No script for id" }
|
|
: undefined,
|
|
});
|
|
trackBrowser(browser);
|
|
const result = await new CdpBrowserProvider().inspectPage(
|
|
inspectWebPageInputSchema.parse({
|
|
cdp_endpoint: browser.endpoint,
|
|
target_id: "allowed-page",
|
|
observation_ms: 0,
|
|
include_script_sources: true,
|
|
}),
|
|
);
|
|
if (result.ok) throw new Error("Expected observed origin refusal");
|
|
expect(projectAnalysisError(result.error)).toMatchObject({
|
|
code: "target_unavailable",
|
|
message: expect.stringContaining("permitted HTTP(S) origin scope"),
|
|
remediation: { action: expect.stringContaining("allowed_origins") },
|
|
details: {
|
|
operation: "inspect_web_page",
|
|
reason: "target_not_allowed",
|
|
},
|
|
});
|
|
if (sourceFails)
|
|
expect(result.error.cause).toMatchObject({ reason: "protocol_error" });
|
|
expect(browser.commands.map(({ method }) => method)).toContain(
|
|
"Target.detachFromTarget",
|
|
);
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
{ changed: true, sourceFails: true, reason: "target_changed" },
|
|
{ changed: false, sourceFails: true, reason: "protocol_error" },
|
|
{ changed: false, sourceFails: false, reason: null },
|
|
])(
|
|
"preserves observed navigation before a stale source-read failure: $changed/$sourceFails",
|
|
async ({ changed, sourceFails, reason }) => {
|
|
const browser = await startFakeCdpBrowser({
|
|
eventsBeforeReply: ["DOMSnapshot.captureSnapshot"],
|
|
commandEvents: (command, origin) =>
|
|
changed && command.method === "DOMSnapshot.captureSnapshot"
|
|
? [
|
|
frameNavigated(command, {
|
|
id: "frame-main",
|
|
loaderId: "reloaded-document",
|
|
url: origin + "/reloaded",
|
|
}),
|
|
]
|
|
: undefined,
|
|
commandError: ({ method }) =>
|
|
sourceFails && method === "Debugger.getScriptSource"
|
|
? { code: -32000, message: "No script for id" }
|
|
: undefined,
|
|
});
|
|
trackBrowser(browser);
|
|
const result = await new CdpBrowserProvider().inspectPage(
|
|
inspectWebPageInputSchema.parse({
|
|
cdp_endpoint: browser.endpoint,
|
|
target_id: "allowed-page",
|
|
observation_ms: 0,
|
|
include_script_sources: true,
|
|
}),
|
|
);
|
|
if (reason === null) {
|
|
if (!result.ok) throw result.error;
|
|
expect(
|
|
result.value.scripts.items.some(({ source }) => source.included),
|
|
).toBe(true);
|
|
return;
|
|
}
|
|
expect(result).toMatchObject({ ok: false, error: { reason } });
|
|
if (result.ok) throw new Error("Expected a capture refusal");
|
|
if (changed) {
|
|
expect(projectAnalysisError(result.error)).toMatchObject({
|
|
code: "target_unavailable",
|
|
message: expect.stringContaining("target changed"),
|
|
remediation: {
|
|
action: expect.stringContaining("Retry after navigation settles"),
|
|
},
|
|
});
|
|
expect(result.error.cause).toMatchObject({ reason: "protocol_error" });
|
|
}
|
|
expect(browser.commands.map(({ method }) => method)).toContain(
|
|
"Target.detachFromTarget",
|
|
);
|
|
},
|
|
);
|