MessageWeave/web/config.test.mjs

138 lines
4.6 KiB
JavaScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import vm from "node:vm";
const html = readFileSync(new URL("./index.html", import.meta.url), "utf8");
const source = readFileSync(new URL("./config.js", import.meta.url), "utf8");
class FakeElement {
constructor(hidden = false) {
this.hidden = hidden;
this.disabled = false;
this.required = false;
this.checked = false;
this.value = "";
this.textContent = "";
this.listeners = new Map();
this.attributes = new Map();
this.classList = {
toggle() {},
add() {},
remove() {},
};
this.children = new Map();
}
addEventListener(type, callback) {
this.listeners.set(type, callback);
}
setAttribute(name, value) {
this.attributes.set(name, value);
}
querySelector(selector) {
if (!this.children.has(selector)) this.children.set(selector, new FakeElement());
return this.children.get(selector);
}
querySelectorAll() { return []; }
focus() {}
reset() {}
setCustomValidity() {}
reportValidity() { return true; }
}
async function startPage(statusResponse) {
const elements = new Map();
for (const match of html.matchAll(/<[^>]*\bid="([^"]+)"[^>]*>/gs)) {
const [, id] = match;
const hidden = /\shidden(?:\s|=|>)/.test(match[0]);
elements.set(id, new FakeElement(hidden));
}
const document = {
querySelector(selector) {
const match = selector.match(/^#(.+)$/);
if (!match || !elements.has(match[1])) throw new Error(`Missing selector: ${selector}`);
return elements.get(match[1]);
},
getElementById(id) {
if (!elements.has(id)) throw new Error(`Missing element: ${id}`);
return elements.get(id);
},
querySelectorAll() { return []; },
};
const window = {
addEventListener() {},
setTimeout() { return 1; },
clearTimeout() {},
};
const fetchCalls = [];
const fetch = async (path, options) => {
fetchCalls.push({ path, options });
if (path !== "/api/status") throw new Error(`Unexpected request: ${path}`);
if (statusResponse instanceof Error) throw statusResponse;
const httpStatus = statusResponse.httpStatus ?? 200;
return {
ok: httpStatus >= 200 && httpStatus < 300,
status: httpStatus,
text: async () => JSON.stringify(statusResponse.body),
};
};
vm.runInNewContext(source, { document, window, fetch, Headers });
await new Promise((resolve) => setImmediate(resolve));
await Promise.resolve();
return { elements, fetchCalls };
}
test("configuration-setup hides all authorization UI and lists only missing variable names", async () => {
const { elements, fetchCalls } = await startPage({
body: {
ready: false,
mode: "configuration-setup",
missing: ["REDIS_URL", "CONFIG_ENCRYPTION_KEY", "secret-value-must-not-display"],
},
});
assert.equal(elements.get("setup-card").hidden, false);
assert.equal(elements.get("intro").hidden, true);
assert.equal(elements.get("auth-card").hidden, true);
assert.equal(elements.get("config-panel").hidden, true);
assert.equal(elements.get("notice").hidden, true);
assert.equal(elements.get("privacy-note").hidden, true);
assert.equal(elements.get("setup-missing").textContent, "REDIS_URL、CONFIG_ENCRYPTION_KEY");
assert.equal(elements.get("setup-message").textContent.includes("配置引导模式"), true);
assert.equal(fetchCalls.length, 1);
assert.equal(fetchCalls[0].path, "/api/status");
assert.equal(fetchCalls[0].options.credentials, "omit");
assert.equal(fetchCalls[0].options.cache, "no-store");
assert.equal(fetchCalls[0].options.headers.has("authorization"), false);
});
test("ready status preserves the existing admin-session entry and configuration page", async () => {
const { elements } = await startPage({
body: { ready: true, mode: "configured", missing: [] },
});
assert.equal(elements.get("setup-card").hidden, true);
assert.equal(elements.get("intro").hidden, false);
assert.equal(elements.get("auth-card").hidden, false);
assert.equal(elements.get("config-panel").hidden, true);
assert.equal(elements.get("privacy-note").hidden, false);
assert.equal(elements.get("notice").hidden, false);
});
test("status errors keep the authorization form hidden until readiness is confirmed", async () => {
const { elements } = await startPage({
httpStatus: 503,
body: {},
});
assert.equal(elements.get("setup-card").hidden, false);
assert.equal(elements.get("auth-card").hidden, true);
assert.equal(elements.get("config-panel").hidden, true);
assert.match(elements.get("setup-message").textContent, /授权入口保持隐藏/);
});