Files
github-actions[bot] 70d35bab31
Merge-upstream / merge (push) Canceled after 0s
🗽 Sync 2026-09-13 02:49:28
2026-09-13 02:49:28 +08:00

195 lines
4.7 KiB
JavaScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import test from "node:test";
const source = readFileSync(
resolve(import.meta.dirname, "../src/resource/menu-shadcn.js"),
"utf8",
);
const E = (tagName, attributes, children) => ({
tagName,
attributes,
children,
});
// The module's top level only defines constants and returns the extended
// object, so empty DOM/storage stubs keep the factory inert.
const loadMenuModule = (localStorage = {}) => {
const baseclass = {
extend(module) {
return module;
},
};
const ui = { menu: { getChildren: () => [] } };
const L = {
env: { dispatchpath: [], requestpath: [] },
url: (...segments) => `/${segments.join("/")}`,
};
return new Function(
"baseclass",
"ui",
"E",
"L",
"_",
"document",
"window",
"localStorage",
"sessionStorage",
"navigator",
source,
)(
baseclass,
ui,
E,
L,
(value) => value,
{},
{},
localStorage,
{},
{ platform: "" },
);
};
const RECENTS_KEY = "shadcn.palette.recents";
const fakeStorage = (initial = {}) => {
const map = new Map(Object.entries(initial));
return {
getItem: (key) => (map.has(key) ? map.get(key) : null),
setItem: (key, value) => map.set(key, String(value)),
map,
};
};
const navPage = (path, title, group = "Group") => ({
title,
group,
icon: "activity",
path,
href: `/${path}`,
isLogout: false,
});
const paletteWith = (storage, index) => {
const module = loadMenuModule(storage);
module.palIndex = index;
return module;
};
const browseIndex = () => [
navPage("admin/status/overview", "概览", "状态"),
navPage("admin/status/syslog", "系统日志", "状态"),
navPage("admin/network/iface", "接口", "网络"),
navPage("admin/network/firewall", "防火墙", "网络"),
{
title: "Logout",
group: null,
icon: "logout",
path: "admin/logout",
href: "/admin/logout",
isLogout: true,
},
];
const stored = (recents) => ({ [RECENTS_KEY]: JSON.stringify(recents) });
const paths = (pages) => pages.map((page) => page.path);
test("a recorded pick lands at the head of the stored recents", () => {
const storage = fakeStorage();
const palette = paletteWith(storage, browseIndex());
palette._palRecordRecent("admin/status/syslog");
palette._palRecordRecent("admin/network/iface");
assert.deepEqual(JSON.parse(storage.map.get(RECENTS_KEY)), [
"admin/network/iface",
"admin/status/syslog",
]);
});
test("re-recording a path moves it up instead of duplicating it", () => {
const storage = fakeStorage(
stored(["admin/network/iface", "admin/status/syslog"]),
);
const palette = paletteWith(storage, browseIndex());
palette._palRecordRecent("admin/status/syslog");
assert.deepEqual(JSON.parse(storage.map.get(RECENTS_KEY)), [
"admin/status/syslog",
"admin/network/iface",
]);
});
test("corrupt or foreign stored values read as no history", () => {
for (const value of ["not json", '"just a string"', "[1,2,3]", "{}"]) {
const palette = paletteWith(
fakeStorage({ [RECENTS_KEY]: value }),
browseIndex(),
);
assert.deepEqual(palette._palReadRecents(), [], value);
}
});
test("a throwing storage neither breaks reads nor records", () => {
const hostile = {
getItem: () => {
throw new Error("privacy mode");
},
setItem: () => {
throw new Error("quota");
},
};
const palette = paletteWith(hostile, browseIndex());
assert.deepEqual(palette._palReadRecents(), []);
assert.doesNotThrow(() => palette._palRecordRecent("admin/status/overview"));
});
test("browsing floats visited pages in recency order, menu order behind", () => {
const palette = paletteWith(
fakeStorage(stored(["admin/network/iface", "admin/status/syslog"])),
browseIndex(),
);
assert.deepEqual(paths(palette._palBrowsePages()), [
"admin/network/iface",
"admin/status/syslog",
"admin/status/overview",
"admin/network/firewall",
]);
});
test("vanished paths drop and the rest keep their order", () => {
const palette = paletteWith(
fakeStorage(stored(["admin/removed/page", "admin/network/firewall"])),
browseIndex(),
);
assert.deepEqual(paths(palette._palBrowsePages()), [
"admin/network/firewall",
"admin/status/overview",
"admin/status/syslog",
"admin/network/iface",
]);
});
test("the logout leaf stays out of the browse list, stored or not", () => {
const palette = paletteWith(
fakeStorage(stored(["admin/logout", "admin/status/syslog"])),
browseIndex(),
);
assert.deepEqual(paths(palette._palBrowsePages()), [
"admin/status/syslog",
"admin/status/overview",
"admin/network/iface",
"admin/network/firewall",
]);
});