mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-09-14 12:24:33 +08:00
195 lines
4.7 KiB
JavaScript
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",
|
|
]);
|
|
});
|