114 lines
4.0 KiB
TypeScript
114 lines
4.0 KiB
TypeScript
import { assertEquals, assertRejects, assertThrows } from "@std/assert";
|
|
import { join } from "@std/path";
|
|
import { cleanup } from "../src/lifecycle.ts";
|
|
import {
|
|
interpolateEnvironment,
|
|
loadScenario,
|
|
resolveScenarioPath,
|
|
validateBaseUrl,
|
|
} from "../src/scenario.ts";
|
|
|
|
function minimalScenario(extra = ""): string {
|
|
return `{
|
|
"schemaVersion": 1,
|
|
"id": "test-screen",
|
|
"title": "Test screen",
|
|
"baseUrl": "http://127.0.0.1:5173",
|
|
"personas": [{"id":"anonymous","label":"Anonymous","auth":{"kind":"anonymous"}}],
|
|
"viewports": [{"label":"desktop","width":1000,"height":800}],
|
|
"routes": [{
|
|
"id":"home","label":"Home","path":"/","goal":"Inspect home",
|
|
"dataState":"Fixture data","ready":{"kind":"selector","selector":"main"},
|
|
"capturePoints":[{"id":"initial","label":"Initial"}]
|
|
}]${extra}
|
|
}`;
|
|
}
|
|
|
|
Deno.test("scenario parser preserves explicit visual review context", async () => {
|
|
const directory = await Deno.makeTempDir();
|
|
try {
|
|
const path = join(directory, "scenario.json");
|
|
await Deno.writeTextFile(path, minimalScenario());
|
|
const scenario = await loadScenario(path);
|
|
assertEquals(scenario.personas[0].auth, { kind: "anonymous" });
|
|
assertEquals(scenario.routes[0].goal, "Inspect home");
|
|
assertEquals(scenario.routes[0].dataState, "Fixture data");
|
|
assertEquals(scenario.routes[0].ready, {
|
|
kind: "selector",
|
|
selector: "main",
|
|
timeoutMs: undefined,
|
|
});
|
|
assertEquals(scenario.reducedMotion, "reduce");
|
|
} finally {
|
|
await Deno.remove(directory, { recursive: true });
|
|
}
|
|
});
|
|
|
|
Deno.test("scenario parser rejects duplicate persona identity", async () => {
|
|
const directory = await Deno.makeTempDir();
|
|
try {
|
|
const path = join(directory, "scenario.json");
|
|
await Deno.writeTextFile(
|
|
path,
|
|
minimalScenario().replace(
|
|
'[{"id":"anonymous","label":"Anonymous","auth":{"kind":"anonymous"}}]',
|
|
'[{"id":"same","label":"First","auth":{"kind":"anonymous"}},{"id":"same","label":"Second","auth":{"kind":"anonymous"}}]',
|
|
),
|
|
);
|
|
await assertRejects(() => loadScenario(path), Error, "duplicate id: same");
|
|
} finally {
|
|
await Deno.remove(directory, { recursive: true });
|
|
}
|
|
});
|
|
|
|
Deno.test("base URL rejects embedded credentials and non-http schemes", () => {
|
|
assertThrows(
|
|
() => validateBaseUrl("https://user:secret@example.test"),
|
|
Error,
|
|
"must not contain credentials",
|
|
);
|
|
assertThrows(() => validateBaseUrl("file:///tmp/index.html"), Error, "must use http or https");
|
|
});
|
|
|
|
Deno.test("environment interpolation fails closed", () => {
|
|
assertEquals(
|
|
interpolateEnvironment("/w/${WORKSPACE_ID}", { WORKSPACE_ID: "W-test" }),
|
|
"/w/W-test",
|
|
);
|
|
assertThrows(
|
|
() => interpolateEnvironment("${MISSING}", {}),
|
|
Error,
|
|
"required environment variable is missing",
|
|
);
|
|
});
|
|
|
|
Deno.test("committed auth profiles resolve outside the repository", async () => {
|
|
const source = "scenarios/workspace-control-plane.json";
|
|
const scenario = await loadScenario(source);
|
|
for (const persona of scenario.personas) {
|
|
if (persona.auth.kind !== "storage-state") continue;
|
|
const statePath = resolveScenarioPath(source, persona.auth.path);
|
|
assertEquals(statePath.startsWith(Deno.cwd()), false);
|
|
}
|
|
});
|
|
|
|
Deno.test("cleanup removes only complete review bundles beyond retention", async () => {
|
|
const directory = await Deno.makeTempDir();
|
|
try {
|
|
for (const name of ["one", "two", "three"]) {
|
|
const run = join(directory, name);
|
|
await Deno.mkdir(run);
|
|
await Deno.writeTextFile(join(run, "review-context.json"), "{}");
|
|
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
}
|
|
const unrelated = join(directory, "auth");
|
|
await Deno.mkdir(unrelated);
|
|
await Deno.writeTextFile(join(unrelated, "state.json"), "secret");
|
|
const removed = await cleanup({ outputDirectory: directory, keep: 1 });
|
|
assertEquals(removed.length, 2);
|
|
assertEquals(await Deno.readTextFile(join(unrelated, "state.json")), "secret");
|
|
} finally {
|
|
await Deno.remove(directory, { recursive: true });
|
|
}
|
|
});
|