Files
rea/tests/acceptance/webSessionLifecycleWindow.test.ts

140 lines
4.7 KiB
TypeScript
Raw Permalink Normal View History

import { expect, onTestFinished } from "vitest";
import { z } from "zod";
import { webObservationSessionSchema } from "../../src/domain/browserSession.js";
import { toolContract } from "../../src/contracts/toolContracts.js";
import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js";
import { startFakeCdpBrowser } from "../fixtures/fakeCdpBrowser.js";
import { cliTest } from "../support/cli/cliFixture.js";
cliTest.for(["CLI", "stdio MCP"] as const)(
"filters replayed lifecycle milestones before the observation window through %s",
async (surface, { cli }) => {
const browser = await startFakeCdpBrowser({
eventsBeforeReply: [
"Performance.getMetrics",
"Page.setLifecycleEventsEnabled",
],
commandResult: (command) => {
if (command.method === "Performance.enable") return {};
if (command.method === "Performance.getMetrics")
return { metrics: [{ name: "Timestamp", value: 20 }] };
return undefined;
},
commandEvents: (command, origin) => {
if (command.method === "Performance.getMetrics")
return [
{
method: "Page.lifecycleEvent",
sessionId: command.sessionId,
params: {
frameId: "frame-main",
loaderId: "loader-before-window",
name: "load",
timestamp: 19,
},
},
{
method: "Page.navigatedWithinDocument",
sessionId: command.sessionId,
params: {
frameId: "frame-main",
url: `${origin}#during-clock-response`,
navigationType: "historyApi",
},
},
];
if (command.method === "Page.setLifecycleEventsEnabled")
return [
{
method: "Page.lifecycleEvent",
sessionId: command.sessionId,
params: {
frameId: "frame-main",
loaderId: "loader-before-window-replayed-after-arm",
name: "load",
timestamp: 19,
},
},
{
method: "Page.lifecycleEvent",
sessionId: command.sessionId,
params: {
frameId: "frame-main",
loaderId: "loader-current-window",
name: "load",
timestamp: 21,
},
},
];
return undefined;
},
});
onTestFinished(() => browser.close());
let evidence: unknown;
if (surface === "CLI") {
const response = await cli.run({
arguments: [
"observe-web-session",
browser.endpoint,
"allowed-page",
"--allowed-origins",
browser.allowedOrigin,
"--observation-ms",
"5",
"--json",
],
});
expect(response.exitCode, response.stderr).toBe(0);
evidence = response.json;
} else {
const { call } = await connectLocalToolsMcp();
const response = await call("observe_web_session", {
cdp_endpoint: browser.endpoint,
allowed_origins: [browser.allowedOrigin],
target_id: "allowed-page",
observation_ms: 5,
});
expect(response.isError, JSON.stringify(response)).not.toBe(true);
evidence = response.structuredContent;
}
const contract = toolContract("observe_web_session");
const sourceEvidence = contract.outputSchema.parse(evidence);
const normalized = z
.object({ normalized_result: webObservationSessionSchema })
.parse(sourceEvidence).normalized_result;
expect(normalized.timeline).toContainEqual(
expect.objectContaining({
type: "lifecycle",
loader_id: "loader-current-window",
timestamp: 21,
}),
);
expect(normalized.timeline).not.toContainEqual(
expect.objectContaining({ loader_id: "loader-before-window" }),
);
expect(normalized.timeline).not.toContainEqual(
expect.objectContaining({
loader_id: "loader-before-window-replayed-after-arm",
}),
);
expect(normalized.timeline).toContainEqual(
expect.objectContaining({
type: "same_document_navigation",
url: `${browser.allowedOrigin}#during-clock-response`,
timestamp: null,
}),
);
expect(normalized.limitations).toContain(
"Timeline rows without a comparable producer timestamp have a null timestamp. Buffered events with unknown timing are retained as context only; they do not establish activity inside the selected observation window.",
);
expect(browser.commands).toContainEqual(
expect.objectContaining({
method: "Performance.enable",
params: { timeDomain: "timeTicks" },
}),
);
},
);