Rename the admin example plugin to auth-admin, and let config/menu.ts hide or rename a profile-menu entry
CI / full-gate (push) Successful in 3m32s
CI / full-gate (push) Successful in 3m32s
This commit is contained in:
@@ -29,6 +29,12 @@ test("findConflicts: duplicate nav id is an error, a shared permission name only
|
||||
p({ id: "b", nav: [{ id: "dup", label: "B" }] }),
|
||||
]);
|
||||
assert.ok(navDup.some((c) => c.kind === "nav-id" && c.level === "error" && c.plugins.includes("a") && c.plugins.includes("b")));
|
||||
// The override targets profile-menu entries by the same ids, so one namespace covers both.
|
||||
const profileDup = findConflicts([
|
||||
p({ id: "a", nav: [{ id: "dup", label: "A" }] }),
|
||||
p({ id: "b", profileMenu: [{ href: "/b", id: "dup", label: "B", session: true }] }),
|
||||
]);
|
||||
assert.ok(profileDup.some((c) => c.kind === "nav-id" && c.level === "error" && c.plugins.includes("a") && c.plugins.includes("b")));
|
||||
|
||||
// Sharing a permission across plugins is legitimate → warn, not error.
|
||||
const permissionDup = findConflicts([
|
||||
|
||||
@@ -34,7 +34,11 @@ export function findConflicts(plugins: Plugin[]): PluginConflict[] {
|
||||
if (owners.length > 1) out.push({ kind: "route", level: "error", message: `${owners.length} routes resolve to "${key}"`, plugins: uniq(owners) });
|
||||
});
|
||||
|
||||
collect(plugins, (plugin, push) => collectNavIds(plugin.nav, push)).forEach((owners, id) => {
|
||||
const overrideTargets = (plugin: Plugin, push: (id: string) => void): void => {
|
||||
collectNavIds(plugin.nav, push);
|
||||
for (const entry of plugin.profileMenu ?? []) if (entry.id != null) push(entry.id);
|
||||
};
|
||||
collect(plugins, overrideTargets).forEach((owners, id) => {
|
||||
if (owners.length > 1) out.push({ kind: "nav-id", level: "error", message: `nav id "${id}" used ${owners.length}×; override targets ids, so they must be unique`, plugins: uniq(owners) });
|
||||
});
|
||||
|
||||
|
||||
@@ -128,11 +128,11 @@ test("a route + nav node may be marked public, or session, and load fine", async
|
||||
assert.equal(sess?.nav?.[0]?.session, true);
|
||||
});
|
||||
|
||||
test("`admin` is not reserved — the admin screens ship as a drop-in plugin mounted at /admin", async (t) => {
|
||||
const dir = scaffold(t, { "admin/plugin.ts": full("admin") });
|
||||
test("`auth-admin` is not reserved — the admin screens ship as a drop-in plugin mounted at /auth-admin", async (t) => {
|
||||
const dir = scaffold(t, { "auth-admin/plugin.ts": full("auth-admin") });
|
||||
const plugins = await discoverPlugins({ dir });
|
||||
assert.equal(plugins.length, 1);
|
||||
assert.equal(plugins[0]?.id, "admin");
|
||||
assert.equal(plugins[0]?.id, "auth-admin");
|
||||
});
|
||||
|
||||
test("a plugin may declare `home` (public /) and `dashboard` (gated /dashboard) handlers", async (t) => {
|
||||
|
||||
@@ -108,8 +108,8 @@ test("RESERVED_PLUGIN_IDS covers every built-in top-level mount; `home` (the / f
|
||||
]);
|
||||
for (const id of builtins) assert.ok(RESERVED_PLUGIN_IDS.has(id), `built-in mount "${id}" must be a reserved plugin id`);
|
||||
// "/" is owned by the `home` manifest field (not a /<id> route), so it cannot be shadowed and is
|
||||
// deliberately not reserved — a plugin folder named "home" is legal. `admin` is likewise free: the
|
||||
// admin screens ship as a drop-in plugin mounted at /admin, not a built-in route.
|
||||
// deliberately not reserved — a plugin folder named "home" is legal. `auth-admin` is likewise free:
|
||||
// the admin screens ship as a drop-in plugin mounted at /auth-admin, not a built-in route.
|
||||
assert.equal(RESERVED_PLUGIN_IDS.has("home"), false);
|
||||
assert.equal(RESERVED_PLUGIN_IDS.has("admin"), false);
|
||||
assert.equal(RESERVED_PLUGIN_IDS.has("auth-admin"), false);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user