301 lines
9.8 KiB
TypeScript
301 lines
9.8 KiB
TypeScript
import test, { describe, afterEach } from "node:test"
|
|
import assert from "node:assert"
|
|
import { buildLayoutForEntries, resolveGroups } from "./config-loader"
|
|
import { componentRegistry } from "../../components/registry"
|
|
import type { QuartzComponent, QuartzComponentConstructor } from "../../components/types"
|
|
import { PluginJsonEntry, LayoutPosition } from "./types"
|
|
|
|
const makeComponent = (name: string): QuartzComponent => {
|
|
const c = (() => null) as unknown as QuartzComponent
|
|
c.displayName = name
|
|
return c
|
|
}
|
|
|
|
const makeConstructor = (name: string): QuartzComponentConstructor => {
|
|
return () => makeComponent(name)
|
|
}
|
|
|
|
function makeEntry(
|
|
source: string,
|
|
layout?: { position: LayoutPosition; priority: number },
|
|
): PluginJsonEntry {
|
|
return {
|
|
source,
|
|
enabled: true,
|
|
options: {},
|
|
...(layout ? { layout: { position: layout.position, priority: layout.priority } } : {}),
|
|
}
|
|
}
|
|
|
|
afterEach(() => {
|
|
componentRegistry.clear()
|
|
})
|
|
|
|
describe("position assignment", () => {
|
|
test("places component in correct position from layout.position", () => {
|
|
const component = makeComponent("MyPlugin")
|
|
componentRegistry.register("my-plugin", component, "test-source")
|
|
|
|
const result = buildLayoutForEntries([makeEntry("my-plugin", { position: "left", priority: 10 })], {})
|
|
assert.deepStrictEqual(result.left, [component])
|
|
})
|
|
|
|
test("places component in footer position", () => {
|
|
const component = makeComponent("FooterComp")
|
|
componentRegistry.register("footer-comp", component, "test-source")
|
|
|
|
const result = buildLayoutForEntries(
|
|
[makeEntry("footer-comp", { position: "footer", priority: 20 })],
|
|
{},
|
|
)
|
|
assert.deepStrictEqual(result.footer, [component])
|
|
})
|
|
|
|
test("places component in header position", () => {
|
|
const component = makeComponent("HeaderComp")
|
|
componentRegistry.register("header-comp", component, "test-source")
|
|
|
|
const result = buildLayoutForEntries(
|
|
[makeEntry("header-comp", { position: "header", priority: 5 })],
|
|
{},
|
|
)
|
|
assert.deepStrictEqual(result.header, [component])
|
|
})
|
|
|
|
test("returns empty arrays when no entries have layout", () => {
|
|
const component = makeComponent("NoLayout")
|
|
componentRegistry.register("no-layout", component, "test-source")
|
|
|
|
const result = buildLayoutForEntries([makeEntry("no-layout")], {})
|
|
assert.deepStrictEqual(result.header, [])
|
|
assert.deepStrictEqual(result.left, [])
|
|
assert.deepStrictEqual(result.right, [])
|
|
assert.deepStrictEqual(result.beforeBody, [])
|
|
assert.deepStrictEqual(result.afterBody, [])
|
|
assert.deepStrictEqual(result.footer, [])
|
|
})
|
|
})
|
|
|
|
describe("defaultPosition fallback", () => {
|
|
test("uses manifest defaultPosition when no explicit layout", () => {
|
|
const component = makeComponent("F")
|
|
componentRegistry.register("f", component, "test-source", {
|
|
name: "f",
|
|
displayName: "F",
|
|
description: "",
|
|
version: "1",
|
|
defaultPosition: "footer",
|
|
defaultPriority: 50,
|
|
})
|
|
|
|
const result = buildLayoutForEntries([makeEntry("f")], {})
|
|
assert.deepStrictEqual(result.footer, [component])
|
|
})
|
|
|
|
test("explicit layout takes precedence over defaultPosition", () => {
|
|
const component = makeComponent("P")
|
|
componentRegistry.register("p", component, "test-source", {
|
|
name: "p",
|
|
displayName: "P",
|
|
description: "",
|
|
version: "1",
|
|
defaultPosition: "right",
|
|
})
|
|
|
|
const result = buildLayoutForEntries(
|
|
[makeEntry("p", { position: "left", priority: 10 })],
|
|
{},
|
|
)
|
|
assert.deepStrictEqual(result.left, [component])
|
|
assert.deepStrictEqual(result.right, [])
|
|
})
|
|
|
|
test("silently skips invalid defaultPosition", () => {
|
|
const component = makeComponent("Bad")
|
|
componentRegistry.register("bad", component, "test-source", {
|
|
name: "bad",
|
|
displayName: "Bad",
|
|
description: "",
|
|
version: "1",
|
|
defaultPosition: "body",
|
|
})
|
|
|
|
const result = buildLayoutForEntries([makeEntry("bad")], {})
|
|
assert.deepStrictEqual(result.header, [])
|
|
assert.deepStrictEqual(result.left, [])
|
|
assert.deepStrictEqual(result.right, [])
|
|
assert.deepStrictEqual(result.beforeBody, [])
|
|
assert.deepStrictEqual(result.afterBody, [])
|
|
assert.deepStrictEqual(result.footer, [])
|
|
})
|
|
})
|
|
|
|
describe("priority sorting", () => {
|
|
test("sorts components within a position by priority", () => {
|
|
const compA = makeComponent("A")
|
|
const compB = makeComponent("B")
|
|
const compC = makeComponent("C")
|
|
componentRegistry.register("a", compA, "test-source")
|
|
componentRegistry.register("b", compB, "test-source")
|
|
componentRegistry.register("c", compC, "test-source")
|
|
|
|
const result = buildLayoutForEntries(
|
|
[
|
|
makeEntry("a", { position: "left", priority: 30 }),
|
|
makeEntry("b", { position: "left", priority: 10 }),
|
|
makeEntry("c", { position: "left", priority: 20 }),
|
|
],
|
|
{},
|
|
)
|
|
|
|
const names = result.left?.map((component) => component.displayName)
|
|
assert.deepStrictEqual(names, ["B", "C", "A"])
|
|
})
|
|
|
|
test("defaults to priority 50 for defaultPosition without defaultPriority", () => {
|
|
const explicit = makeComponent("Explicit")
|
|
const ctor = makeConstructor("Default")
|
|
const defaulted = ctor(undefined)
|
|
|
|
componentRegistry.register("explicit", explicit, "test-source")
|
|
componentRegistry.register("defaulted", defaulted, "test-source", {
|
|
name: "defaulted",
|
|
displayName: "Default",
|
|
description: "",
|
|
version: "1",
|
|
defaultPosition: "left",
|
|
})
|
|
|
|
const result = buildLayoutForEntries(
|
|
[makeEntry("explicit", { position: "left", priority: 40 }), makeEntry("defaulted")],
|
|
{},
|
|
)
|
|
|
|
const names = result.left?.map((component) => component.displayName)
|
|
assert.deepStrictEqual(names, ["Explicit", "Default"])
|
|
})
|
|
})
|
|
|
|
describe("resolveGroups", () => {
|
|
test("returns ungrouped items in priority order", () => {
|
|
const a = makeComponent("A")
|
|
const b = makeComponent("B")
|
|
const c = makeComponent("C")
|
|
const items = [
|
|
{ component: a, priority: 30 },
|
|
{ component: b, priority: 10 },
|
|
{ component: c, priority: 20 },
|
|
]
|
|
const result = resolveGroups(items, {})
|
|
assert.strictEqual(result.length, 3)
|
|
assert.strictEqual(result[0], b)
|
|
assert.strictEqual(result[1], c)
|
|
assert.strictEqual(result[2], a)
|
|
})
|
|
|
|
test("returns ungrouped items unchanged", () => {
|
|
const a = makeComponent("A")
|
|
const items = [{ component: a, priority: 10 }]
|
|
const result = resolveGroups(items, {})
|
|
assert.strictEqual(result.length, 1)
|
|
assert.strictEqual(result[0], a)
|
|
})
|
|
|
|
test("returns empty array for empty input", () => {
|
|
const result = resolveGroups([], {})
|
|
assert.deepStrictEqual(result, [])
|
|
})
|
|
|
|
test("wraps grouped items in a Flex component", () => {
|
|
const a = makeComponent("A")
|
|
const b = makeComponent("B")
|
|
const items = [
|
|
{ component: a, priority: 10, group: "toolbar" },
|
|
{ component: b, priority: 20, group: "toolbar" },
|
|
]
|
|
const result = resolveGroups(items, {})
|
|
assert.strictEqual(result.length, 1)
|
|
assert.notStrictEqual(result[0], a)
|
|
assert.notStrictEqual(result[0], b)
|
|
})
|
|
|
|
test("uses explicit group priority from config", () => {
|
|
const grouped = makeComponent("Grouped")
|
|
const ungrouped = makeComponent("Ungrouped")
|
|
const items = [
|
|
{ component: grouped, priority: 50, group: "nav" },
|
|
{ component: ungrouped, priority: 10 },
|
|
]
|
|
const result = resolveGroups(items, { nav: { priority: 5 } })
|
|
assert.strictEqual(result.length, 2)
|
|
assert.strictEqual(result[1], ungrouped)
|
|
})
|
|
|
|
test("falls back to first member priority when no group config", () => {
|
|
const a = makeComponent("A")
|
|
const b = makeComponent("B")
|
|
const solo = makeComponent("Solo")
|
|
const items = [
|
|
{ component: a, priority: 20, group: "nav" },
|
|
{ component: b, priority: 40, group: "nav" },
|
|
{ component: solo, priority: 30 },
|
|
]
|
|
const result = resolveGroups(items, {})
|
|
assert.strictEqual(result.length, 2)
|
|
assert.strictEqual(result[1], solo)
|
|
})
|
|
|
|
test("single-member group still wraps in Flex", () => {
|
|
const a = makeComponent("A")
|
|
const items = [{ component: a, priority: 10, group: "solo" }]
|
|
const result = resolveGroups(items, {})
|
|
assert.strictEqual(result.length, 1)
|
|
assert.notStrictEqual(result[0], a)
|
|
})
|
|
})
|
|
|
|
describe("buildLayoutForEntries with display wrappers", () => {
|
|
test("applies display wrapper for mobile-only", () => {
|
|
const component = makeComponent("Wrapped")
|
|
componentRegistry.register("wrapped-plugin", component, "test-source")
|
|
|
|
const entry: PluginJsonEntry = {
|
|
source: "wrapped-plugin",
|
|
enabled: true,
|
|
options: {},
|
|
layout: { position: "left" as LayoutPosition, priority: 10, display: "mobile-only" },
|
|
}
|
|
const result = buildLayoutForEntries([entry], {})
|
|
assert.strictEqual(result.left?.length, 1)
|
|
assert.notStrictEqual(result.left?.[0], component)
|
|
})
|
|
})
|
|
|
|
describe("buildLayoutForEntries with constructors", () => {
|
|
test("instantiates constructor components via registry", () => {
|
|
const ctor = makeConstructor("Instantiated")
|
|
componentRegistry.register("ctor-plugin", ctor, "test-source")
|
|
|
|
const result = buildLayoutForEntries(
|
|
[makeEntry("ctor-plugin", { position: "left", priority: 10 })],
|
|
{},
|
|
)
|
|
assert.strictEqual(result.left?.length, 1)
|
|
assert.strictEqual(result.left?.[0].displayName, "Instantiated")
|
|
})
|
|
|
|
test("merges entry options with TS overrides for constructors", () => {
|
|
const ctor = makeConstructor("Merged")
|
|
componentRegistry.register("merge-plugin", ctor, "test-source")
|
|
componentRegistry.setOptionOverrides("merge-plugin", { extra: true })
|
|
|
|
const result = buildLayoutForEntries(
|
|
[{ source: "merge-plugin", enabled: true, options: { base: 1 }, layout: { position: "right" as LayoutPosition, priority: 10 } }],
|
|
{},
|
|
)
|
|
assert.strictEqual(result.right?.length, 1)
|
|
})
|
|
})
|
|
|
|
|