Files
reverse-engineering-anything/tests/boundary/cli/browserCli.test.ts
T
morlutoandCursor Agent 1ef5918179 fix(browser): preserve source-map declaration uncertainty (#7945)
* fix(browser): preserve source-map declaration uncertainty

* style(tests): fix CI formatting

* fix(browser): retain known source map differences

* fix(web): bind map comparisons to script metadata

* fix(browser): match source maps with partial script metadata

Keep each script's observed hash and declaration constraints instead of
dropping a metadata field for the whole URL group.

* test(flutter): assert ABI selector refusals as structured issues

MCP input validation reports the ABI path and the schema message separately.
The old concatenated "abi: ..." sentence is no longer the delivered error.

* fix(web): prove large source-map changes without factorial search

Groups larger than eight scripts were reported as unexplained, so nine
x.map declarations plus one omission compared as unknown against ten
y.map declarations. Declaration counts reject impossible placements, and
augmenting paths assign the rest without retrying identical scripts.

* test(mcp): assert bundle path issues by field

compare_bundles reports each relative path as an invalid_format issue
instead of one joined diagnostic sentence.

* fix(web): match repeated source-map scripts by count

Identical declarations share one group, and a shared omission pool is
counted once. A 2000-script self-compare no longer builds a dense
assignment graph.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-10-29 18:50:26 +08:00

1049 lines
32 KiB
TypeScript

import { afterEach, describe, expect } from "vitest";
import { z } from "zod";
import { cliTest, type TestCli } from "../../support/cli/cliFixture.js";
import { DEFAULT_BROWSER_SCENARIO_CAPTURE_LIMITS } from "../../../src/domain/browserScenarioCaptureLimits.js";
import { BROWSER_STORAGE_FINGERPRINT_VERSION } from "../../../src/domain/browserScenarioCaptureValues.js";
import { browserScenarioDiffSchema } from "../../../src/domain/browserScenarioDiffValues.js";
import {
startFakeCdpBrowser,
type FakeCdpBrowser,
type FakeOptions,
} from "../../fixtures/fakeCdpBrowser.js";
const browsers: FakeCdpBrowser[] = [];
const INTEGRATION_TEST_TIMEOUT_MS = 60_000;
const partialScriptEvents: FakeOptions["commandEvents"] = (
{ method },
origin,
) =>
method === "Debugger.enable"
? [
{
method: "Debugger.scriptParsed",
sessionId: "session-1",
params: {
scriptId: "partial-script",
url: `${origin}/app.js`,
executionContextId: 1,
},
},
]
: undefined;
afterEach(async () => {
await Promise.all(browsers.splice(0).map(async (browser) => browser.close()));
});
cliTest(
"compares source-map declaration omissions through the public CLI",
async ({ cli }) => {
const cdn = "https://cdn.example.test";
const browser = await startFakeCdpBrowser({
commandEvents: ({ method }, origin) =>
method === "Debugger.enable"
? [
{
method: "Debugger.scriptParsed",
sessionId: "session-1",
params: {
scriptId: "map-script",
url: `${origin}/app.js`,
hash: "a".repeat(64),
length: 10,
isModule: false,
scriptLanguage: "JavaScript",
sourceMapURL: `${cdn}/app.map`,
},
},
]
: undefined,
});
browsers.push(browser);
const inspect = async (broad: boolean) => {
const result = await cli.run({
arguments: [
"inspect-web-page",
browser.endpoint,
"allowed-page",
"--observation-ms",
"0",
"--allowed-origins",
browser.allowedOrigin,
...(broad ? ["--allowed-origins", cdn] : []),
"--json",
],
environment: process.env,
});
expect(result.exitCode, result.stderr).toBe(0);
return normalizedResult(result.json);
};
const known = await inspect(true);
const omitted = await inspect(false);
expect(known).toMatchObject({
scripts: {
items: [expect.objectContaining({ source_map_url: `${cdn}/app.map` })],
},
});
expect(omitted).toMatchObject({
scripts: { items: [expect.objectContaining({ source_map_url: null })] },
completeness: {
excluded: expect.arrayContaining([
expect.objectContaining({
section: "source_maps",
reason: "disallowed_origin",
}),
]),
},
});
for (const [before, after] of [
[known, omitted],
[omitted, known],
[omitted, omitted],
] as const) {
const result = await cli.run({
arguments: [
"compare-web-captures",
JSON.stringify({ inspection: before }),
JSON.stringify({ inspection: after }),
"--json",
],
});
expect(result.exitCode, result.stderr).toBe(0);
expect(result.json).toMatchObject({
normalized_result: {
dimensions: { scripts: { status: "unknown", total_changes: 0 } },
},
});
}
},
INTEGRATION_TEST_TIMEOUT_MS,
);
cliTest(
"browser CLI parity returns the same Evidence discovery and inspection contracts",
async ({ cli }) => {
const browser = await startFakeCdpBrowser({
sessionTimeline: "same_origin",
webMcpTools: true,
sensitiveShapes: true,
commandEvents: partialScriptEvents,
eventsBeforeReply: ["Network.enable"],
});
browsers.push(browser);
const listedInvocation = await cli.run({
arguments: ["list-browser-targets", browser.endpoint, "--json"],
environment: process.env,
});
expect(listedInvocation.exitCode, listedInvocation.stderr).toBe(0);
expect(listedInvocation.signal).toBeNull();
const listed = listedInvocation.json;
expect(listed).toMatchObject({
operation: "list_browser_targets",
provider: { id: "rea-cdp-browser" },
normalized_result: {
targets: expect.arrayContaining([
expect.objectContaining({ target_id: "allowed-page" }),
expect.objectContaining({ target_id: "disallowed-page" }),
]),
},
});
const inspectedInvocation = await cli.run({
arguments: [
"inspect-web-page",
browser.endpoint,
"allowed-page",
"--observation-ms",
"0",
"--include-console-text",
"--include-json-body-shapes",
"--include-websocket-shapes",
"--json",
],
environment: process.env,
});
expect(inspectedInvocation.exitCode, inspectedInvocation.stderr).toBe(0);
expect(inspectedInvocation.signal).toBeNull();
const inspected = inspectedInvocation.json;
expect(inspected).toMatchObject({
operation: "inspect_web_page",
provider: { id: "rea-cdp-browser" },
normalized_result: {
target: { target_id: "allowed-page" },
scripts: {
items: [{ cdp_hash: null, length: null, is_module: null }],
},
network: {
prior_activity_available: false,
requests: [
expect.objectContaining({
body_shapes: expect.objectContaining({ status: "included" }),
}),
],
},
},
});
const inspection = normalizedResult(inspected);
const comparedInvocation = await cli.run({
arguments: [
"compare-web-captures",
JSON.stringify({ inspection, scenario: { caller_metadata: true } }),
JSON.stringify({ inspection }),
"--json",
],
environment: process.env,
});
expect(comparedInvocation.exitCode, comparedInvocation.stderr).toBe(0);
expect(comparedInvocation.signal).toBeNull();
const compared = comparedInvocation.json;
expect(compared).toMatchObject({
operation: "compare_web_captures",
normalized_result: {
overall_status: "unknown",
dimensions: {
network: { status: "unknown", total_changes: 0 },
},
},
});
await assertScenarioComparisonPolicy(cli);
},
INTEGRATION_TEST_TIMEOUT_MS,
);
cliTest(
"compares redirect metadata uncertainty through the public CLI",
async ({ cli }) => {
const browser = await startFakeCdpBrowser();
browsers.push(browser);
const inspectedInvocation = await cli.run({
arguments: [
"inspect-web-page",
browser.endpoint,
"allowed-page",
"--observation-ms",
"0",
"--json",
],
environment: process.env,
});
expect(inspectedInvocation.exitCode, inspectedInvocation.stderr).toBe(0);
expect(inspectedInvocation.signal).toBeNull();
const inspected = inspectedInvocation.json;
const inspection = normalizedResult(inspected);
const missingVsKnownInvocation = await compareCapturesCli(
cli,
redirectCapture(inspection, browser.allowedOrigin, null),
redirectCapture(inspection, browser.allowedOrigin, 302),
);
expect(
missingVsKnownInvocation.exitCode,
missingVsKnownInvocation.stderr,
).toBe(0);
expect(missingVsKnownInvocation.signal).toBeNull();
const missingVsKnown = missingVsKnownInvocation.json;
expect(missingVsKnown).toMatchObject({
normalized_result: {
dimensions: {
network: {
status: "unknown",
total_changes: 0,
reason: expect.stringContaining("redirect"),
},
},
},
});
const knownDifferenceInvocation = await compareCapturesCli(
cli,
redirectCapture(inspection, browser.allowedOrigin, 302),
redirectCapture(inspection, browser.allowedOrigin, 307),
);
expect(
knownDifferenceInvocation.exitCode,
knownDifferenceInvocation.stderr,
).toBe(0);
expect(knownDifferenceInvocation.signal).toBeNull();
const knownDifference = knownDifferenceInvocation.json;
expect(knownDifference).toMatchObject({
normalized_result: {
dimensions: { network: { status: "unknown", total_changes: 0 } },
},
});
// A closed imported request inventory supplies the missing directional
// proof; two attachment-limited samples cannot pair repeated requests.
const closed = redirectCapture(inspection, browser.allowedOrigin, 302);
if (!isRecord(closed.inspection.completeness))
throw new Error("Missing capture completeness");
const limited = closed.inspection.completeness.attach_limited_sections;
if (!Array.isArray(limited)) throw new Error("Missing attachment coverage");
closed.inspection.completeness.attach_limited_sections = limited.filter(
(section) => section !== "network_requests",
);
const closedDifferenceInvocation = await compareCapturesCli(
cli,
closed,
redirectCapture(inspection, browser.allowedOrigin, 307),
);
expect(
closedDifferenceInvocation.exitCode,
closedDifferenceInvocation.stderr,
).toBe(0);
expect(closedDifferenceInvocation.signal).toBeNull();
expect(closedDifferenceInvocation.json).toMatchObject({
normalized_result: {
dimensions: { network: { status: "changed", total_changes: 1 } },
},
});
},
INTEGRATION_TEST_TIMEOUT_MS,
);
cliTest(
"browser CLI attaches through a browser socket and records it redacted",
async ({ cli }) => {
const browser = await startFakeCdpBrowser();
browsers.push(browser);
const inspectedInvocation = await cli.run({
arguments: [
"inspect-web-page",
browser.browserWebSocketUrl,
"allowed-page",
"--observation-ms",
"0",
"--json",
],
environment: process.env,
});
expect(inspectedInvocation.exitCode, inspectedInvocation.stderr).toBe(0);
expect(inspectedInvocation.signal).toBeNull();
const inspected = inspectedInvocation.json;
expect(inspected).toMatchObject({
operation: "inspect_web_page",
parameters: {
cdp_endpoint: browser.browserWebSocketUrl.replace(
/fake$/u,
"[REDACTED]",
),
},
normalized_result: { target: { target_id: "allowed-page" } },
});
expect(JSON.stringify(inspected)).not.toContain("/devtools/browser/fake");
const refusedInvocation = await cli.run({
arguments: [
"list-browser-targets",
browser.browserWebSocketUrl.replace(/fake$/u, "not-this-browser"),
"--json",
],
environment: process.env,
});
expect(refusedInvocation.exitCode, refusedInvocation.stderr).toBe(1);
expect(refusedInvocation.signal).toBeNull();
const refused = refusedInvocation.json;
expect(refused).toMatchObject({
code: "provider_unavailable",
details: {
operation: "list_browser_targets",
reason: "endpoint_unreachable",
},
});
expect(JSON.stringify(refused)).not.toContain("not-this-browser");
},
INTEGRATION_TEST_TIMEOUT_MS,
);
describe("scenario comparison CLI diagnostics", () => {
const rule = {
rule_id: "domain",
artifacts: ["url"],
match: "app.example.test",
replacement: "normalized.test",
};
cliTest.for([
{
name: "duplicate rule IDs",
normalization: { rules: [rule, rule] },
issue: {
path: ["normalization", "rules", 1, "rule_id"],
reason: "invalid_value",
message: "Normalization rule IDs must be unique",
},
},
{
name: "duplicate artifact kinds",
normalization: { rules: [{ ...rule, artifacts: ["url", "url"] }] },
issue: {
path: ["normalization", "rules", 0, "artifacts"],
reason: "invalid_value",
message: "Normalization rule artifact kinds must be unique",
},
},
{
name: "structurally invalid rules",
normalization: { rules: "invalid" },
issue: {
path: ["normalization", "rules"],
reason: "invalid_type",
expected: "array",
},
},
])(
"retains scenario normalization diagnostics for $name",
async ({ normalization, issue }, { cli }) => {
const capture = JSON.stringify(completeScenarioCapture());
const resultInvocation = await cli.run({
arguments: [
"compare-web-captures",
capture,
capture,
"--normalization-json",
JSON.stringify(normalization),
"--json",
],
environment: process.env,
});
expect(resultInvocation.exitCode, resultInvocation.stderr).toBe(1);
expect(resultInvocation.signal).toBeNull();
const result = resultInvocation.json;
expect(result).toMatchObject({
code: "invalid_request",
details: { operation: "compare_web_captures", issues: [issue] },
});
},
);
cliTest(
"keeps malformed scenario diagnostics in the selected capture group",
async ({ cli }) => {
const capture = completeScenarioCapture();
const resultInvocation = await cli.run({
arguments: [
"compare-web-captures",
JSON.stringify({ ...capture, duration_ms: "invalid" }),
JSON.stringify(capture),
"--json",
],
environment: process.env,
});
expect(resultInvocation.exitCode, resultInvocation.stderr).toBe(1);
expect(resultInvocation.signal).toBeNull();
const result = resultInvocation.json;
expect(result).toMatchObject({
code: "invalid_request",
details: {
operation: "compare_web_captures",
issues: [
{
path: ["before_scenario", "duration_ms"],
reason: "invalid_type",
expected: "number",
},
],
},
});
},
);
cliTest.for(["omitted", "misspelled"])(
"reports the missing scenario manifest when it is %s on both captures",
async (kind, { cli }) => {
const { scenario, ...capture } = completeScenarioCapture();
const input = JSON.stringify(
kind === "omitted" ? capture : { ...capture, scneario: scenario },
);
const resultInvocation = await cli.run({
arguments: ["compare-web-captures", input, input, "--json"],
environment: process.env,
});
expect(resultInvocation.exitCode, resultInvocation.stderr).toBe(1);
expect(resultInvocation.signal).toBeNull();
const result = resultInvocation.json;
expect(result).toMatchObject({
code: "invalid_request",
details: {
operation: "compare_web_captures",
issues: expect.arrayContaining([
{
path: ["before_scenario", "scenario"],
reason: "missing_argument",
expected: "object",
},
{
path: ["after_scenario", "scenario"],
reason: "missing_argument",
expected: "object",
},
]),
},
});
expect(JSON.stringify(result)).not.toContain('"inspection"');
},
);
});
describe("scenario comparison recovery guidance", () => {
cliTest(
"reports each distinct event coverage instruction once",
async ({ cli }) => {
const before = scenarioCaptureWithConsole("same", {
request_body: false,
response_body: false,
header_values: false,
});
const after = {
...before,
scenario: {
...before.scenario,
event_families: [...before.scenario.event_families, "network"],
network_content: {
...before.scenario.network_content,
response_body: true,
},
},
};
const invocation = await compareCapturesCli(cli, before, after);
expect(invocation.exitCode, invocation.stderr).toBe(0);
const result = z
.object({ normalized_result: browserScenarioDiffSchema })
.parse(invocation.json).normalized_result;
expect(result.overall_status).toBe("unknown");
const diffs = result.steps.flatMap((step) =>
step.artifact_diffs.filter((diff) => diff.artifact === "events"),
);
expect(diffs.length).toBeGreaterThan(0);
for (const diff of diffs) {
expect(
diff.reason?.match(/Event family selections differ/gu),
).toHaveLength(1);
expect(
diff.reason?.match(/Network content selections differ/gu),
).toHaveLength(1);
}
},
);
cliTest(
"reports independent network recovery alongside console differences",
async ({ cli }) => {
const base = completeScenarioCapture();
const matchingSelection = scenarioCaptureWithConsole(
"before",
base.scenario.network_content,
);
const differentSelection = scenarioCaptureWithConsole("after", {
...base.scenario.network_content,
response_body: true,
});
const changedInvocation = await compareCapturesCli(
cli,
matchingSelection,
differentSelection,
);
expect(changedInvocation.exitCode, changedInvocation.stderr).toBe(0);
expect(changedInvocation.signal).toBeNull();
const changed = changedInvocation.json;
expect(changed).toMatchObject({
normalized_result: {
overall_status: "changed",
steps: expect.arrayContaining([
expect.objectContaining({
step_id: "open-settings",
artifact_diffs: expect.arrayContaining([
expect.objectContaining({
artifact: "events",
status: "changed",
reason: expect.stringContaining(
"Network content selections differ",
),
}),
]),
}),
]),
},
});
const quietMismatchInvocation = await compareCapturesCli(
cli,
matchingSelection,
scenarioCaptureWithConsole(
"before",
differentSelection.scenario.network_content,
),
);
expect(
quietMismatchInvocation.exitCode,
quietMismatchInvocation.stderr,
).toBe(0);
expect(quietMismatchInvocation.signal).toBeNull();
const quietMismatch = quietMismatchInvocation.json;
expect(quietMismatch).toMatchObject({
normalized_result: {
steps: expect.arrayContaining([
expect.objectContaining({
step_id: "open-settings",
artifact_diffs: expect.arrayContaining([
expect.objectContaining({
artifact: "events",
status: "unknown",
reason: expect.stringContaining(
"Network content selections differ",
),
}),
]),
}),
]),
},
});
},
);
});
describe("browser CLI capture parity", () => {
cliTest(
"runs analysis, session, WebMCP, screenshot, and comparison from explicit request scope",
async ({ cli }) => {
const browser = await startFakeCdpBrowser({
sessionTimeline: "same_origin",
webMcpTools: true,
sensitiveShapes: true,
});
browsers.push(browser);
const scopeArgs = ["--allowed-origins", browser.allowedOrigin];
const analyzedInvocation = await cli.run({
arguments: [
"analyze-web-bundle",
browser.endpoint,
"allowed-page",
...scopeArgs,
"--observation-ms",
"0",
"--json",
],
environment: process.env,
});
expect(analyzedInvocation.exitCode, analyzedInvocation.stderr).toBe(0);
expect(analyzedInvocation.signal).toBeNull();
const analyzed = analyzedInvocation.json;
expect(analyzed).toMatchObject({
operation: "analyze_web_bundle",
normalized_result: {
capture: { scripts_analyzed: 1 },
observations: { source_maps: { status: "not_requested" } },
},
});
const observedSessionInvocation = await cli.run({
arguments: [
"observe-web-session",
browser.endpoint,
"allowed-page",
...scopeArgs,
"--observation-ms",
"5",
"--json",
],
environment: process.env,
});
expect(
observedSessionInvocation.exitCode,
observedSessionInvocation.stderr,
).toBe(0);
expect(observedSessionInvocation.signal).toBeNull();
const observedSession = observedSessionInvocation.json;
expect(observedSession).toMatchObject({
operation: "observe_web_session",
normalized_result: {
window: { end_reason: "window_elapsed" },
timeline: expect.arrayContaining([
expect.objectContaining({ type: "same_origin_reload" }),
]),
},
});
const webMcpInvocation = await cli.run({
arguments: [
"discover-webmcp-tools",
browser.endpoint,
"allowed-page",
...scopeArgs,
"--observation-ms",
"0",
"--json",
],
environment: process.env,
});
expect(webMcpInvocation.exitCode, webMcpInvocation.stderr).toBe(0);
expect(webMcpInvocation.signal).toBeNull();
const webMcp = webMcpInvocation.json;
expect(webMcp).toMatchObject({
operation: "discover_webmcp_tools",
normalized_result: {
tools: {
items: [
expect.objectContaining({
name: "search_orders",
annotations: expect.objectContaining({
consequential: true,
}),
}),
],
},
},
});
const screenshotInvocation = await cli.run({
arguments: [
"capture-web-screenshot",
browser.endpoint,
"allowed-page",
...scopeArgs,
"--json",
],
environment: process.env,
});
expect(screenshotInvocation.exitCode, screenshotInvocation.stderr).toBe(
0,
);
expect(screenshotInvocation.signal).toBeNull();
const screenshot = screenshotInvocation.json;
const artifact = screenshotArtifact(screenshot);
expect(artifact).toMatchObject({ media_type: "image/png", bytes: 70 });
const visualInvocation = await cli.run({
arguments: [
"compare-web-screenshots",
JSON.stringify(artifact),
JSON.stringify(artifact),
"--json",
],
environment: process.env,
});
expect(visualInvocation.exitCode, visualInvocation.stderr).toBe(0);
expect(visualInvocation.signal).toBeNull();
const visual = visualInvocation.json;
expect(visual).toMatchObject({
operation: "compare_web_screenshots",
normalized_result: { status: "identical", changed_pixels: 0 },
});
},
INTEGRATION_TEST_TIMEOUT_MS,
);
});
const screenshotArtifact = (value: unknown): Record<string, unknown> => {
if (
!isRecord(value) ||
!isRecord(value.normalized_result) ||
!isRecord(value.normalized_result.artifact)
)
throw new TypeError("Missing CLI screenshot artifact");
return value.normalized_result.artifact;
};
const normalizedResult = (value: unknown): Record<string, unknown> => {
if (!isRecord(value) || !isRecord(value.normalized_result))
throw new TypeError("Missing CLI normalized result");
return value.normalized_result;
};
const redirectCapture = (
inspection: Record<string, unknown>,
origin: string,
status: number | null,
) => {
const copy = structuredClone(inspection);
if (!isRecord(copy.network) || !Array.isArray(copy.network.requests))
throw new Error("Missing network capture");
const request = copy.network.requests[0];
if (!isRecord(request)) throw new Error("Missing network request");
request.redirects = [
{
url: `${origin}/prior`,
response_url: `${origin}/prior`,
method: "GET",
resource_type: "Fetch",
status,
mime_type: "text/plain",
encoded_data_length: 12,
request_timestamp: 1,
redirect_event_timestamp: 2,
},
];
return { inspection: copy };
};
const assertScenarioComparisonPolicy = async (cli: TestCli): Promise<void> => {
const scenarioCapture = completeScenarioCapture();
const scenarioComparedInvocation = await cli.run({
arguments: [
"compare-web-captures",
JSON.stringify(scenarioCapture),
JSON.stringify(scenarioCapture),
"--normalization-json",
'{"rules":[]}',
"--json",
],
environment: process.env,
});
expect(
scenarioComparedInvocation.exitCode,
scenarioComparedInvocation.stderr,
).toBe(0);
expect(scenarioComparedInvocation.signal).toBeNull();
const scenarioCompared = scenarioComparedInvocation.json;
expect(scenarioCompared).toMatchObject({
operation: "compare_web_captures",
normalized_result: {
comparison_kind: "browser_scenario",
overall_status: "unchanged",
alignment: { status: "aligned", aligned_steps: 2 },
},
});
const intentionalPolicyInvocation = await cli.run({
arguments: [
"compare-web-captures",
JSON.stringify(scenarioCapture),
JSON.stringify(scenarioCapture),
"--normalization-json",
JSON.stringify({
rules: [
{
rule_id: "domain",
artifacts: ["url"],
match: "app.example.test",
replacement: "normalized.test",
},
],
}),
"--json",
],
environment: process.env,
});
expect(
intentionalPolicyInvocation.exitCode,
intentionalPolicyInvocation.stderr,
).toBe(0);
expect(intentionalPolicyInvocation.signal).toBeNull();
const intentionalPolicy = intentionalPolicyInvocation.json;
expect(intentionalPolicy).toMatchObject({
normalized_result: {
normalization: { rules: [{ rule_id: "domain" }] },
},
});
const malformedPolicyInvocation = await cli.run({
arguments: [
"compare-web-captures",
JSON.stringify(scenarioCapture),
JSON.stringify(scenarioCapture),
"--normalization-json",
"{invalid-policy",
"--json",
],
environment: process.env,
});
expect(
malformedPolicyInvocation.exitCode,
malformedPolicyInvocation.stderr,
).toBe(1);
expect(malformedPolicyInvocation.signal).toBeNull();
const malformedPolicy = malformedPolicyInvocation.json;
expect(malformedPolicy).toMatchObject({
error: "Browser observation failed",
code: "invalid_request",
details: {
operation: "compare_web_captures",
issues: [
{
path: ["normalization"],
reason: "invalid_format",
expected: "JSON",
},
],
},
});
};
const compareCapturesCli = (cli: TestCli, before: unknown, after: unknown) =>
cli.run({
arguments: [
"compare-web-captures",
JSON.stringify(before),
JSON.stringify(after),
"--json",
],
environment: process.env,
});
const completeScenarioCapture = () => {
const url = {
url: "https://app.example.test/",
origin: "https://app.example.test",
query_parameter_names: [],
redacted: false,
};
const completeness = {
status: "complete",
equality_eligible: true,
missing_sections: [],
truncated_sections: [],
};
const artifacts = {
screenshot: { state: "not_requested" },
dom: { state: "not_requested" },
accessibility: { state: "not_requested" },
url: { state: "captured", value: url },
history: { state: "not_requested" },
storage: { state: "not_requested" },
};
const step = (stepIndex: number, stepId: string, action: string) => ({
step_index: stepIndex,
step_id: stepId,
action,
status: "completed",
elapsed_ms: stepIndex,
before_url: url,
after_url: url,
error: null,
event_sequence_start: 1,
event_sequence_end: 0,
artifacts,
completeness,
});
return {
browser: {
mode: "connect",
process_ownership: "external",
cleanup: "disconnected-external",
product: "Chromium",
version: "149",
},
scenario: {
start_origin: "https://app.example.test",
action_count: 1,
secret_references: [],
network_content: {
request_body: false,
response_body: false,
header_values: false,
},
event_families: [],
limits: DEFAULT_BROWSER_SCENARIO_CAPTURE_LIMITS,
},
duration_ms: 1,
steps: [
step(0, "scenario_start", "scenario_start"),
step(1, "open-settings", "click"),
],
events: {
retained: 0,
dropped: 0,
items: [],
omissions: {
by_kind: [],
network_transactions: [],
network_transaction_range: null,
},
},
completeness,
limitations: [],
};
};
cliTest(
"compares reordered scenario storage through the public CLI and commits the rule",
async ({ cli }) => {
const capture = completeScenarioCapture();
const entries = [
{ name: "theme", value_state: "hashed", value_sha256: "a".repeat(64) },
{ name: "locale", value_state: "hashed", value_sha256: "b".repeat(64) },
];
const withStorage = (values: typeof entries) => ({
...capture,
steps: capture.steps.map((step) => ({
...step,
artifacts: {
...step.artifacts,
storage: {
state: "captured",
value: {
fingerprint_version: BROWSER_STORAGE_FINGERPRINT_VERSION,
cookies: [],
local_storage: values,
session_storage: [],
},
},
},
})),
});
const comparedInvocation = await compareCapturesCli(
cli,
withStorage(entries),
withStorage(entries.toReversed()),
);
expect(comparedInvocation.exitCode, comparedInvocation.stderr).toBe(0);
expect(comparedInvocation.signal).toBeNull();
const compared = comparedInvocation.json;
expect(compared).toMatchObject({
operation: "compare_web_captures",
normalized_result: {
comparison_kind: "browser_scenario",
overall_status: "unchanged",
normalization: {
built_in_rules: expect.arrayContaining([
"compare_storage_entries_as_unordered_collection",
]),
},
},
});
const changedInvocation = await compareCapturesCli(
cli,
withStorage(entries),
withStorage(entries.slice(1)),
);
expect(changedInvocation.exitCode, changedInvocation.stderr).toBe(0);
expect(changedInvocation.signal).toBeNull();
const changed = changedInvocation.json;
expect(changed).toMatchObject({
normalized_result: { overall_status: "changed" },
});
},
INTEGRATION_TEST_TIMEOUT_MS,
);
const scenarioCaptureWithConsole = (
text: string,
networkContent: {
readonly request_body: boolean;
readonly response_body: boolean;
readonly header_values: boolean;
},
) => {
const capture = completeScenarioCapture();
return {
...capture,
scenario: {
...capture.scenario,
network_content: networkContent,
event_families: ["console"],
},
events: {
retained: 1,
dropped: 0,
items: [
{
sequence: 1,
step_index: 1,
kind: "console",
level: "info",
text,
url: null,
},
],
omissions: {
by_kind: [],
network_transactions: [],
network_transaction_range: null,
},
},
};
};
const isRecord = (value: unknown): value is Record<string, unknown> =>
typeof value === "object" && value !== null && !Array.isArray(value);