feat: add display kiosk host
CI / verify (pull_request) Successful in 38s
CI / infrastructure-smoke (pull_request) Successful in 20s
CI / migration-smoke (pull_request) Successful in 59s

This commit is contained in:
ekklesia
2026-08-23 20:50:56 -05:00
parent be5ed7853d
commit 2e0fe059c3
80 changed files with 4868 additions and 397 deletions
+50
View File
@@ -170,3 +170,53 @@ describe("the shell builds no module knowledge into its bundle", () => {
expect(bundle).not.toContain("createObjectURL");
});
});
describe("the display host is a separate generic browser bundle", () => {
let bundle: string;
beforeAll(async () => {
const result = await build({
absWorkingDir: packageDirectory,
bundle: true,
define: {
__EKKLESIA_API_BASE_URL__: '""',
"process.env.NODE_ENV": '"production"',
},
entryPoints: [path.join(sourceDirectory, "display", "main.tsx")],
format: "esm",
jsx: "automatic",
logLevel: "silent",
minify: false,
platform: "browser",
target: "es2022",
write: false,
});
bundle = result.outputFiles.map((file) => file.text).join("\n");
}, 60_000);
it("contains the generic loader's one dynamic import and no statically known module", () => {
expect([...bundle.matchAll(/\bimport\s*\(/gu)]).toHaveLength(1);
expect(bundle).toContain("(url) => import(url)");
expect(bundle).not.toContain("ekklesia.module.json");
expect(bundle).not.toContain("/api/v1/modules");
for (const moduleId of ["announcements", "calendar", "livestream", "signage"]) {
expect(bundle.toLowerCase()).not.toContain(moduleId);
}
});
it("knows only its host route resources, with no Dashboard application or transport coupling", () => {
expect(bundle).toContain('"/api/v1/principal"');
expect(bundle).toContain('"/api/v1/module-displays"');
expect(bundle).not.toContain('"/api/v1/dashboard/navigation"');
expect(bundle).not.toContain("Loading Ekklesia");
expect(bundle).not.toContain("Sign in to Ekklesia");
expect(bundle.toLowerCase()).not.toContain("socket.io");
expect(bundle.toLowerCase()).not.toContain("mqtt");
});
it("uses neither eval nor a blob URL to load display code", () => {
expect(bundle).not.toContain("new Function(");
expect(bundle).not.toMatch(/\beval\s*\(/u);
expect(bundle).not.toContain("createObjectURL");
});
});