feat: add display kiosk host
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const readFileMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("node:fs/promises", () => ({ readFile: readFileMock }));
|
||||
vi.mock("@ekklesia/shell/display-assets", () => ({
|
||||
displayHostAssetUrls: Object.freeze({
|
||||
html: "file:///surface/browser/display/index.html",
|
||||
script: "file:///surface/browser/display/app.js",
|
||||
}),
|
||||
}));
|
||||
|
||||
import {
|
||||
createDisplayHostAssets,
|
||||
DisplayHostAssetError,
|
||||
loadDisplayHostAssets,
|
||||
} from "../src/display-host-assets.js";
|
||||
|
||||
describe("display host asset loading", () => {
|
||||
beforeEach(() => readFileMock.mockReset());
|
||||
|
||||
it("reads only the two declared file URLs in order and freezes one consistent snapshot", async () => {
|
||||
const html = '<!doctype html><div id="ekklesia-display"></div>';
|
||||
const script = "export const displayHost = true;\n";
|
||||
readFileMock.mockResolvedValueOnce(html).mockResolvedValueOnce(script);
|
||||
|
||||
const assets = await loadDisplayHostAssets();
|
||||
|
||||
expect(readFileMock.mock.calls).toEqual([
|
||||
[new URL("file:///surface/browser/display/index.html"), "utf8"],
|
||||
[new URL("file:///surface/browser/display/app.js"), "utf8"],
|
||||
]);
|
||||
expect(assets).toEqual({
|
||||
html,
|
||||
script,
|
||||
scriptEtag: `"${createHash("sha256").update(script).digest("base64url")}"`,
|
||||
});
|
||||
expect(Object.isFrozen(assets)).toBe(true);
|
||||
});
|
||||
|
||||
it("fails deterministically without attempting a neighboring file or exposing the read error", async () => {
|
||||
readFileMock.mockRejectedValueOnce(
|
||||
new Error("ENOENT /home/operator/private/surface-build secret-token"),
|
||||
);
|
||||
|
||||
const failure = await loadDisplayHostAssets().catch((error: unknown) => error);
|
||||
|
||||
expect(readFileMock).toHaveBeenCalledTimes(1);
|
||||
expect(failure).toBeInstanceOf(DisplayHostAssetError);
|
||||
expect(failure).toMatchObject({
|
||||
code: "DISPLAY_HOST_ASSET_UNAVAILABLE",
|
||||
message: "The display host browser assets are unavailable.",
|
||||
name: "DisplayHostAssetError",
|
||||
});
|
||||
expect((failure as Error).stack).toBe(
|
||||
"DisplayHostAssetError: The display host browser assets are unavailable.",
|
||||
);
|
||||
const publicText = `${String(failure)}\n${JSON.stringify(failure)}\n${(failure as Error).stack}`;
|
||||
for (const secret of ["/home/", "operator", "ENOENT", "secret-token"]) {
|
||||
expect(publicText).not.toContain(secret);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects an empty page or script as an unavailable host build", () => {
|
||||
for (const [html, script] of [
|
||||
["", "export {};"],
|
||||
["<html></html>", " \n"],
|
||||
] as const) {
|
||||
expect(() => createDisplayHostAssets(html, script)).toThrow(DisplayHostAssetError);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user