mirror of
https://github.com/Quad4-Software/MeshChatX.git
synced 2026-08-18 09:49:09 -04:00
89 lines
3.9 KiB
JavaScript
89 lines
3.9 KiB
JavaScript
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
import { existsSync, readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
|
|
const ROOT = resolve(import.meta.dirname, "../..");
|
|
const INDEX_HTML = resolve(ROOT, "meshchatx/src/frontend/index.html");
|
|
const BOOT_THEME_JS = resolve(ROOT, "meshchatx/src/frontend/public/boot-theme.js");
|
|
const PUBLIC_DIR = resolve(ROOT, "meshchatx/src/frontend/public");
|
|
|
|
const INLINE_SCRIPT_RE = /<script(?![^>]*\bsrc\s*=)[^>]*>/gi;
|
|
const SCRIPT_SRC_RE = /<script\b[^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/gi;
|
|
|
|
describe("main app CSP and boot theme", () => {
|
|
beforeEach(() => {
|
|
document.documentElement.className = "";
|
|
delete document.documentElement.dataset.bootTheme;
|
|
document.documentElement.style.colorScheme = "";
|
|
window.localStorage.clear();
|
|
delete window.MeshChatXAndroid;
|
|
});
|
|
|
|
afterEach(() => {
|
|
document.documentElement.className = "";
|
|
delete document.documentElement.dataset.bootTheme;
|
|
document.documentElement.style.colorScheme = "";
|
|
window.localStorage.clear();
|
|
delete window.MeshChatXAndroid;
|
|
});
|
|
|
|
it("index.html has no inline scripts (main CSP forbids unsafe-inline)", () => {
|
|
const html = readFileSync(INDEX_HTML, "utf8");
|
|
const inline = [...html.matchAll(INLINE_SCRIPT_RE)].map((m) => m[0]);
|
|
expect(inline).toEqual([]);
|
|
});
|
|
|
|
it("index.html loads boot-theme.js from public before the app module", () => {
|
|
const html = readFileSync(INDEX_HTML, "utf8");
|
|
expect(existsSync(BOOT_THEME_JS)).toBe(true);
|
|
expect(html).toContain('src="/boot-theme.js"');
|
|
const bootIdx = html.indexOf('src="/boot-theme.js"');
|
|
const mainIdx = html.indexOf('src="main.js"');
|
|
expect(bootIdx).toBeGreaterThan(-1);
|
|
expect(mainIdx).toBeGreaterThan(bootIdx);
|
|
});
|
|
|
|
it("every classic script src in index.html resolves under public or is the vite entry", () => {
|
|
const html = readFileSync(INDEX_HTML, "utf8");
|
|
const srcs = [...html.matchAll(SCRIPT_SRC_RE)].map((m) => m[1]);
|
|
expect(srcs.length).toBeGreaterThan(0);
|
|
for (const src of srcs) {
|
|
if (src === "main.js" || src.endsWith("/main.js")) {
|
|
continue;
|
|
}
|
|
const rel = src.replace(/^\//, "");
|
|
expect(existsSync(resolve(PUBLIC_DIR, rel)), `missing public asset for ${src}`).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("boot-theme.js applies dark theme by default", () => {
|
|
const code = readFileSync(BOOT_THEME_JS, "utf8");
|
|
// eslint-disable-next-line no-new-func
|
|
Function(code)();
|
|
expect(document.documentElement.classList.contains("dark")).toBe(true);
|
|
expect(document.documentElement.dataset.bootTheme).toBe("dark");
|
|
expect(document.documentElement.style.colorScheme).toBe("dark");
|
|
});
|
|
|
|
it("boot-theme.js respects localStorage light theme", () => {
|
|
window.localStorage.setItem("meshchatx_ui_theme", "light");
|
|
const code = readFileSync(BOOT_THEME_JS, "utf8");
|
|
// eslint-disable-next-line no-new-func
|
|
Function(code)();
|
|
expect(document.documentElement.classList.contains("dark")).toBe(false);
|
|
expect(document.documentElement.dataset.bootTheme).toBe("light");
|
|
expect(document.documentElement.style.colorScheme).toBe("light");
|
|
});
|
|
|
|
it("boot-theme.js prefers Android bridge theme over localStorage", () => {
|
|
window.localStorage.setItem("meshchatx_ui_theme", "dark");
|
|
window.MeshChatXAndroid = {
|
|
getPreferredUiTheme: () => "light",
|
|
};
|
|
const code = readFileSync(BOOT_THEME_JS, "utf8");
|
|
// eslint-disable-next-line no-new-func
|
|
Function(code)();
|
|
expect(document.documentElement.dataset.bootTheme).toBe("light");
|
|
expect(document.documentElement.classList.contains("dark")).toBe(false);
|
|
});
|
|
});
|