feat(film): capture manifest + validation

This commit is contained in:
Satyam Rastogi
2026-05-30 01:56:57 +05:30
parent b48caaa591
commit dc15c45402
3 changed files with 72 additions and 0 deletions
@@ -0,0 +1,24 @@
{
"viewport": { "width": 1920, "height": 1080, "deviceScaleFactor": 2 },
"theme": "dark",
"baseUrl": "http://localhost:5173",
"captures": [
{ "id": "storm-intel", "role": "owner", "route": "/owner/storm-intel", "waitMs": 2000 },
{ "id": "territory-map", "role": "owner", "route": "/owner/maps", "waitMs": 2500 },
{ "id": "pro-canvas", "role": "owner", "route": "/owner/pro-canvas", "waitMs": 1800 },
{ "id": "lead-verification", "role": "owner", "route": "/lead-verification", "waitMs": 1800 },
{ "id": "dispatch", "role": "owner", "route": "/owner/dispatch", "waitMs": 2500 },
{ "id": "people-skills", "role": "owner", "route": "/owner/people", "waitMs": 1800 },
{ "id": "kanban", "role": "owner", "route": "/owner/kanban", "waitMs": 1800 },
{ "id": "settings-access", "role": "owner", "route": "/owner/settings", "waitMs": 1800 },
{ "id": "projects", "role": "owner", "route": "/owner/projects", "waitMs": 1800 },
{ "id": "estimates", "role": "owner", "route": "/owner/estimates", "waitMs": 1800 },
{ "id": "sub-tasks-owner", "role": "owner", "route": "/owner/subcontractor-tasks", "waitMs": 1800 },
{ "id": "owner-snapshot", "role": "owner", "route": "/owner/snapshot", "waitMs": 2000 },
{ "id": "leaderboard", "role": "owner", "route": "/admin/leaderboard", "waitMs": 1800 },
{ "id": "rep-leads", "role": "agentCody", "route": "/emp/fa/leads", "waitMs": 1800 },
{ "id": "rep-dashboard", "role": "agentCody", "route": "/emp/fa/dashboard", "waitMs": 1800 },
{ "id": "sub-dashboard", "role": "subCarlos", "route": "/subcontractor/dashboard", "waitMs": 1800 },
{ "id": "sub-projects", "role": "subCarlos", "route": "/subcontractor/projects", "waitMs": 1800 }
]
}
@@ -0,0 +1,16 @@
import { describe, it, expect } from "vitest";
import manifest from "./captures.manifest.json";
import { validateManifest, type Manifest } from "./captures";
describe("captures manifest", () => {
it("passes validation", () => {
expect(validateManifest(manifest as Manifest)).toEqual([]);
});
it("has unique ids", () => {
const ids = (manifest as Manifest).captures.map((c) => c.id);
expect(new Set(ids).size).toBe(ids.length);
});
it("captures in dark theme", () => {
expect((manifest as Manifest).theme).toBe("dark");
});
});
+32
View File
@@ -0,0 +1,32 @@
export type CaptureRole = "owner" | "agentCody" | "subCarlos";
export interface Capture {
id: string;
role: CaptureRole;
route: string;
waitMs?: number;
}
export interface Manifest {
viewport: { width: number; height: number; deviceScaleFactor: number };
theme: "dark" | "light";
baseUrl: string;
captures: Capture[];
}
const VALID_ROLES: CaptureRole[] = ["owner", "agentCody", "subCarlos"];
export function validateManifest(m: Manifest): string[] {
const errors: string[] = [];
const seen = new Set<string>();
if (!m.baseUrl?.startsWith("http")) errors.push("baseUrl must be an http(s) URL");
if (m.theme !== "dark" && m.theme !== "light") errors.push("theme must be dark|light");
for (const c of m.captures) {
if (!c.id) errors.push("capture missing id");
if (seen.has(c.id)) errors.push(`duplicate id: ${c.id}`);
seen.add(c.id);
if (!c.route?.startsWith("/")) errors.push(`${c.id}: route must start with "/"`);
if (!VALID_ROLES.includes(c.role)) errors.push(`${c.id}: unknown role "${c.role}"`);
}
return errors;
}