Scert/frontend/src/App.test.js

136 lines
6.0 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import { useAppShell } from "./App.js";
import { deferred, mountComposable } from "./test-support/mount.js";
function dispatchPageEvent(type, persisted = true) {
const event = new Event(type);
Object.defineProperty(event, "persisted", { value: persisted });
window.dispatchEvent(event);
}
test("历史缓存恢复先隐藏旧控制台,再按真实会话显示登录页", async (t) => {
let active = true;
const sessionOptions = [];
const { state } = await mountComposable(t, useAppShell, async (path, options) => {
if (path.endsWith("/status")) return Response.json({ initialized: true });
sessionOptions.push(options);
return active ? Response.json({ authenticated: true, is_admin: true }) : new Response(null, { status: 401 });
});
assert.equal(state.authenticated.value, true);
dispatchPageEvent("pagehide");
assert.equal(state.authenticated.value, null);
assert.equal(state.bootstrapping.value, true);
active = false;
dispatchPageEvent("pageshow");
await new Promise((resolve) => setImmediate(resolve));
assert.equal(state.authenticated.value, false);
assert.equal(state.isAdmin.value, false);
assert.equal(state.bootstrapping.value, false);
assert.equal(sessionOptions.length, 2);
assert.ok(sessionOptions.every((options) => options.cache === "no-store" && options.credentials === "include"));
});
test("非缓存页面显示不重复检查,卸载后移除恢复监听", async (t) => {
let calls = 0;
const { unmount } = await mountComposable(t, useAppShell, async (path) => {
calls += 1;
return Response.json(path.endsWith("/status") ? { initialized: true } : { authenticated: true });
});
assert.equal(calls, 2);
dispatchPageEvent("pageshow", false);
assert.equal(calls, 2);
unmount();
dispatchPageEvent("pageshow");
assert.equal(calls, 2);
});
test("恢复期间网络失败不恢复旧权限或业务界面", async (t) => {
let failed = false;
const { state } = await mountComposable(t, useAppShell, async (path) => {
if (failed) throw new Error("连接失败");
return Response.json(path.endsWith("/status") ? { initialized: true } : { authenticated: true, is_admin: true });
});
failed = true;
dispatchPageEvent("pageshow");
assert.equal(state.authenticated.value, null);
await new Promise((resolve) => setImmediate(resolve));
assert.equal(state.setupState.value, null);
assert.equal(state.isAdmin.value, null);
assert.equal(state.error.value, "连接失败");
});
test("离开页面前的在途会话响应不能覆盖恢复后的未登录状态", async (t) => {
const pending = deferred();
let sessionCalls = 0;
const { state } = await mountComposable(t, useAppShell, async (path) => {
if (path.endsWith("/status")) return Response.json({ initialized: true });
sessionCalls += 1;
return sessionCalls === 1 ? pending.promise : new Response(null, { status: 401 });
});
dispatchPageEvent("pagehide");
dispatchPageEvent("pageshow");
await new Promise((resolve) => setImmediate(resolve));
assert.equal(state.authenticated.value, false);
pending.resolve(Response.json({ authenticated: true, is_admin: true }));
await new Promise((resolve) => setImmediate(resolve));
assert.equal(state.authenticated.value, false);
assert.equal(state.isAdmin.value, false);
});
test("初始化令牌兑换前清除地址片段,后续请求只用同源会话", async (t) => {
const calls = [];
const { state } = await mountComposable(t, () => {
window.location.hash = "#token=test-bootstrap-token";
window.history.replaceState = (_, __, url) => calls.push({ replace: url });
return useAppShell();
}, async (path, options) => {
calls.push({ path, options });
if (path.endsWith("/status")) return Response.json({ mode: "bootstrap" });
if (path.endsWith("/session")) return Response.json({ authenticated: true });
return Response.json({ mode: "bootstrap", initialized: false });
});
assert.equal(state.error.value, "");
assert.equal(state.setupState.value.mode, "bootstrap");
assert.equal(calls[1].replace, "/");
assert.equal(calls[2].path, "/api/v1/bootstrap/session");
assert.equal(calls[2].options.credentials, "same-origin");
assert.deepEqual(JSON.parse(calls[2].options.body), { token: "test-bootstrap-token" });
assert.equal(calls[3].path, "/api/v1/bootstrap/settings");
});
test("没有令牌和有效会话时不暴露初始化表单", async (t) => {
const { state } = await mountComposable(t, useAppShell, async (path) => path.endsWith("/status")
? Response.json({ mode: "bootstrap" }) : new Response(null, { status: 401 }));
assert.equal(state.setupState.value, null);
assert.match(state.error.value, /完整一次性地址/);
});
test("正式服务回退状态接口并检查登录态", async (t) => {
const calls = [];
const { state } = await mountComposable(t, useAppShell, async (path) => {
calls.push(path);
if (path.includes("bootstrap")) return new Response(null, { status: 404 });
if (path.includes("setup")) return Response.json({ initialized: true });
return new Response(null, { status: 401 });
});
assert.deepEqual(calls, ["/api/v1/bootstrap/status", "/api/v1/setup/status", "/api/v1/auth/session"]);
assert.equal(state.authenticated.value, false);
});
test("完成跳转拒绝畸形地址、非 HTTPS 和带密码地址", async (t) => {
const { state } = await mountComposable(t, () => {
window.location.pathname = "/login";
return useAppShell();
}, () => { throw new Error("不应发送请求"); });
for (const address of ["invalid", "http://example.test", "https://owner:password@example.test"]) {
assert.doesNotThrow(() => state.handleInitialized({ completed: true, management_url: address }));
assert.match(state.error.value, /正式管理地址无效/);
}
let destination;
window.setTimeout = (callback) => callback();
window.location.assign = (url) => { destination = url; };
state.handleInitialized({ completed: true, management_url: "https://127.0.0.1:8443/" });
assert.equal(destination, "https://127.0.0.1:8443/");
});