feat: replace CJS require with static imports, add comprehensive layout system tests
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user