import type { SemanticNode } from "@reactive-resume/resume/stylesheet/types"; import type { ResumeData } from "@reactive-resume/schema/resume/data"; import type { Template } from "@reactive-resume/schema/templates"; import type { TemplateSemanticManifest } from "./template-manifest"; import { describe, expect, it } from "vitest"; import { TEMPLATE_PART_CHILD_KINDS_V1 } from "@reactive-resume/resume/stylesheet"; import { defaultResumeData } from "@reactive-resume/schema/resume/default"; import { templateSchema } from "@reactive-resume/schema/templates"; import { createBindingInventory } from "./binding-inventory"; import { getTemplateSemanticBindingRegistry, getTemplateSemanticManifest, getTemplateSemanticRegistryFingerprintInput, validateTemplateSemanticManifest, } from "./template-manifest"; import { buildSemanticTree } from "./tree"; const EXPECTED_PARTS = { azurill: { "timeline-line": { key: "timeline-line", owner: { kind: "section-items", key: "section-items", placement: "main", columns: 1 }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "start" }, }, "timeline-dot": { key: "timeline-dot", owner: { kind: "item", key: "item", placement: "main", columns: 1 }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "timeline-marker", at: "start" }, }, "timeline-marker": { key: "timeline-marker", owner: { kind: "item", key: "item", placement: "main", columns: 1 }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "start" }, }, "timeline-content": { key: "timeline-content", owner: { kind: "item", key: "item", placement: "main", columns: 1 }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "end", take: "all" }, }, }, bronzor: { "interleaved-section-row": { key: "interleaved-section-row", owner: { kind: "section", key: "section", placement: "main" }, binding: { type: "alias", canonicalKind: "section", token: "interleaved-section-row" }, }, }, chikorita: { "contact-row-primary": { key: "contact-row-primary", owner: { kind: "contact-list", key: "contact-list" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "start", take: [ { kind: "contact-item", name: "email" }, { kind: "contact-item", name: "phone" }, { kind: "contact-item", name: "location" }, ], }, }, "contact-row-secondary": { key: "contact-row-secondary", owner: { kind: "contact-list", key: "contact-list" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "end", take: [ { kind: "contact-item", name: "website" }, { kind: "contact-item", name: "custom" }, ], }, }, }, ditgar: { "featured-summary": { key: "featured-summary", owner: { kind: "region", key: "featured" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "start", take: "all" }, }, "sidebar-background": { key: "sidebar-background", owner: { kind: "region", key: "sidebar" }, binding: { type: "alias", canonicalKind: "region", token: "sidebar-background" }, }, "item-header-border": { key: "item-header-border", owner: { kind: "item-header", key: "item-header", sectionTypes: [ "profiles", "experience", "education", "projects", "skills", "languages", "interests", "awards", "certifications", "publications", "volunteer", "references", ], }, binding: { type: "alias", canonicalKind: "item-header", token: "item-header-border" }, }, }, ditto: { "header-band": { key: "header-band", owner: { kind: "header", key: "header" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "start", take: [{ kind: "picture" }, { kind: "name" }, { kind: "headline" }], }, }, "picture-anchor": { key: "picture-anchor", owner: { kind: "header", key: "header" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "header-band", at: "start", take: [{ kind: "picture" }] }, }, "contact-offset": { key: "contact-offset", owner: { kind: "header", key: "header" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: { before: { kind: "contact-list" } } }, }, }, gengar: { "featured-summary": { key: "featured-summary", owner: { kind: "region", key: "featured" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "start", take: "all" }, }, "sidebar-background": { key: "sidebar-background", owner: { kind: "region", key: "sidebar" }, binding: { type: "alias", canonicalKind: "region", token: "sidebar-background" }, }, }, glalie: { "sidebar-background": { key: "sidebar-background", owner: { kind: "region", key: "sidebar" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "start" }, }, }, kakuna: {}, lapras: {}, leafish: { "header-intro": { key: "header-intro", owner: { kind: "header", key: "header" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "start", take: [{ kind: "picture" }, { kind: "name" }, { kind: "headline" }, { kind: "section", name: "summary" }], }, }, "header-body": { key: "header-body", owner: { kind: "header", key: "header" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "header-intro", at: "start", take: "all" }, }, "header-contact-band": { key: "header-contact-band", owner: { kind: "header", key: "header" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "end", take: [{ kind: "contact-list" }] }, }, }, meowth: { "inline-item-header-leading": { key: "inline-item-header-leading", owner: { kind: "item-header", key: "item-header", sectionTypes: ["experience", "education", "volunteer"], }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "start", take: [ { kind: "field", name: "position", sectionTypes: ["experience", "volunteer"] }, { kind: "field", name: "location", sectionTypes: ["experience", "volunteer"] }, { kind: "field", name: "area", sectionTypes: ["education"] }, { kind: "field", name: "degree", sectionTypes: ["education"] }, ], }, }, "inline-item-header-middle": { key: "inline-item-header-middle", owner: { kind: "item-header", key: "item-header", sectionTypes: ["experience", "education", "volunteer"], }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "start", take: [ { kind: "field", name: "company" }, { kind: "field", name: "school" }, { kind: "field", name: "organization" }, { kind: "link" }, ], }, }, "inline-item-header-trailing": { key: "inline-item-header-trailing", owner: { kind: "item-header", key: "item-header", sectionTypes: ["experience", "education", "volunteer"], }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "start", take: [{ kind: "field", name: "period" }] }, }, "education-grade-row": { key: "education-grade-row", owner: { kind: "item", key: "item" }, binding: { type: "primitive", primitive: "Text", source: "existing" }, route: { parent: "owner", at: { after: { kind: "item-header" } }, take: [ { kind: "field", name: "grade", sectionTypes: ["education"] }, { kind: "field", name: "location", sectionTypes: ["education"] }, ], takeFrom: "item-header", }, }, }, onyx: {}, pikachu: { "header-divider": { key: "header-divider", owner: { kind: "header", key: "header" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "start", take: [{ kind: "name" }, { kind: "headline" }] }, }, }, rhyhorn: { "contact-item-content": { key: "contact-item-content", owner: { kind: "contact-item", key: "contact-item" }, binding: { type: "primitive", primitive: { ownerRole: "structured-link", present: "Link", absent: "View" }, source: "existing", }, route: { parent: "owner", at: "start", take: "all" }, }, "contact-item-last": { key: "contact-item-last", owner: { kind: "contact-item", key: "contact-item", position: "last" }, binding: { type: "alias", canonicalKind: "contact-item", token: "contact-item-last" }, }, }, scizor: { "header-name-rule": { key: "header-name-rule", owner: { kind: "header", key: "header" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: { before: { kind: "headline" } } }, }, }, } as const satisfies Readonly>>>; const EXPECTED_LAYOUT = { azurill: { regions: [ { name: "header", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["main"] }, { name: "sidebar", placement: "sidebar", origins: ["sidebar"] }, ], header: { region: "header", placement: "main" }, specialSummary: null, }, bronzor: { regions: [ { name: "header", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["sidebar", "main"], flow: "interleaved" }, ], header: { region: "header", placement: "main" }, specialSummary: null, }, chikorita: { regions: [ { name: "header", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["main"] }, { name: "sidebar", placement: "sidebar", origins: ["sidebar"] }, ], header: { region: "header", placement: "main" }, specialSummary: null, }, ditgar: { regions: [ { name: "header", placement: "sidebar", origins: [] }, { name: "sidebar", placement: "sidebar", origins: ["sidebar"] }, { name: "featured", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["main"] }, ], header: { region: "header", placement: "sidebar" }, specialSummary: { region: "featured", placement: "main", source: "main-with-header" }, }, ditto: { regions: [ { name: "header", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["main"] }, { name: "sidebar", placement: "sidebar", origins: ["sidebar"] }, ], header: { region: "header", placement: "main" }, specialSummary: null, }, gengar: { regions: [ { name: "header", placement: "sidebar", origins: [] }, { name: "sidebar", placement: "sidebar", origins: ["sidebar"] }, { name: "featured", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["main"] }, ], header: { region: "header", placement: "sidebar" }, specialSummary: { region: "featured", placement: "main", source: "main-with-header" }, }, glalie: { regions: [ { name: "header", placement: "sidebar", origins: [] }, { name: "sidebar", placement: "sidebar", origins: ["sidebar"] }, { name: "main", placement: "main", origins: ["main"] }, ], header: { region: "header", placement: "sidebar" }, specialSummary: null, }, kakuna: { regions: [ { name: "header", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["main"] }, { name: "sidebar", placement: "sidebar", origins: ["sidebar"] }, ], header: { region: "header", placement: "main" }, specialSummary: null, }, lapras: { regions: [ { name: "header", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["main"] }, { name: "sidebar", placement: "sidebar", origins: ["sidebar"] }, ], header: { region: "header", placement: "main" }, specialSummary: null, }, leafish: { regions: [ { name: "header", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["main"] }, { name: "sidebar", placement: "sidebar", origins: ["sidebar"] }, ], header: { region: "header", placement: "main" }, specialSummary: { region: "header", placement: "main", source: "always" }, }, meowth: { regions: [ { name: "header", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["main"] }, { name: "sidebar", placement: "sidebar", origins: ["sidebar"] }, ], header: { region: "header", placement: "main" }, specialSummary: null, }, onyx: { regions: [ { name: "header", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["main"] }, { name: "sidebar", placement: "sidebar", origins: ["sidebar"] }, ], header: { region: "header", placement: "main" }, specialSummary: null, }, pikachu: { regions: [ { name: "header", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["main"] }, { name: "sidebar", placement: "sidebar", origins: ["sidebar"] }, ], header: { region: "header", placement: "main" }, specialSummary: null, }, rhyhorn: { regions: [ { name: "header", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["main"] }, { name: "sidebar", placement: "sidebar", origins: ["sidebar"] }, ], header: { region: "header", placement: "main" }, specialSummary: null, }, scizor: { regions: [ { name: "header", placement: "main", origins: [] }, { name: "main", placement: "main", origins: ["main", "sidebar"] }, ], header: { region: "header", placement: "main" }, specialSummary: null, }, } as const satisfies Readonly>>; const flattenTree = (node: SemanticNode): SemanticNode[] => [node, ...node.children.flatMap(flattenTree)]; const flattenValues = (value: unknown): unknown[] => typeof value === "object" && value !== null ? [value, ...Object.values(value).flatMap(flattenValues)] : [value]; const findNodes = (node: SemanticNode, predicate: (candidate: SemanticNode) => boolean): SemanticNode[] => flattenTree(node).filter(predicate); const findPart = (node: SemanticNode, name: string): SemanticNode | undefined => findNodes(node, (candidate) => candidate.kind === "template-part" && candidate.attributes.name === name)[0]; const childLabels = (node: SemanticNode): string[] => node.children.map((child) => (child.kind === "template-part" ? (child.attributes.name ?? "") : child.kind)); const buildFixture = (): ResumeData => { const data = structuredClone(defaultResumeData); data.picture.url = "/uploads/ada.png"; data.basics = { name: "Ada Lovelace", headline: "Engineer", email: "ada@example.com", phone: "", location: "", website: { url: "", label: "" }, customFields: [], }; data.summary.content = "

Summary

"; data.sections.experience.columns = 1; data.sections.experience.items = [ { id: "experience/1", hidden: false, company: "Analytical Engines", position: "Engineer", location: "London", period: "1842", website: { url: "", label: "", inlineLink: false }, description: "

Built algorithms.

", roles: [], }, ]; data.sections.skills.items = [ { id: "skills/1", hidden: false, icon: "code", iconColor: "", name: "TypeScript", proficiency: "Expert", level: 4, keywords: ["PDF"], }, ]; return data; }; const buildFixtureTree = (template: Template, showHeader = true): SemanticNode => { const data = buildFixture(); const page = { fullWidth: false, main: ["summary", "experience"], sidebar: ["skills"] }; return buildSemanticTree({ data, template, page, pageNumber: 1, showHeader }); }; describe("template semantic manifests", () => { it.each(templateSchema.options)("%s publishes a semantic manifest", (template) => { expect(getTemplateSemanticManifest(template)).toBeDefined(); }); it("registers child kinds for every primitive template part", () => { for (const manifest of Object.values(getTemplateSemanticRegistryFingerprintInput())) { for (const part of manifest.parts) { if (part.binding.type === "alias") continue; expect(TEMPLATE_PART_CHILD_KINDS_V1, `${manifest.template}:${part.name}`).toHaveProperty(part.name); } } }); it.each(templateSchema.options)("%s declares only its exact existing parts and owner bindings", (template) => { const parts = Object.fromEntries( getTemplateSemanticManifest(template).parts.map(({ name, ...part }) => [name, part]), ); expect(parts).toEqual(EXPECTED_PARTS[template]); }); it("declares Rhyhorn's outer contact primitive override and no unrelated canonical overrides", () => { expect(getTemplateSemanticManifest("rhyhorn").canonicalBindings).toEqual([ { kind: "contact-item", binding: { type: "primitive", primitive: "View", source: "existing" } }, ]); expect( templateSchema.options .filter((template) => template !== "rhyhorn") .every((template) => getTemplateSemanticManifest(template).canonicalBindings === undefined), ).toBe(true); }); it.each(templateSchema.options)( "%s declares its exact regions, header, and special-summary placement", (template) => { const { regions, header, specialSummary } = getTemplateSemanticManifest(template); expect({ regions, header, specialSummary }).toEqual(EXPECTED_LAYOUT[template]); }, ); it("rejects duplicate part names and keys", () => { const duplicateName = structuredClone(getTemplateSemanticManifest("ditto")) as TemplateSemanticManifest; const duplicateKey = structuredClone(getTemplateSemanticManifest("ditto")) as TemplateSemanticManifest; const first = duplicateName.parts[0]; if (!first) throw new Error("Missing Ditto part fixture"); (duplicateName.parts as TemplateSemanticManifest["parts"][number][]).push({ ...first, key: "another-key", }); (duplicateKey.parts as TemplateSemanticManifest["parts"][number][]).push({ ...first, name: "another-name", }); expect(() => validateTemplateSemanticManifest(duplicateName)).toThrow(/duplicate part name/); expect(() => validateTemplateSemanticManifest(duplicateKey)).toThrow(/duplicate part key/); }); it("rejects unknown placements, owner lies, synthetic wrappers, and missing or invented chrome", () => { const unknownRegionPlacement = structuredClone( getTemplateSemanticManifest("chikorita"), ) as TemplateSemanticManifest; const unknownHeaderPlacement = structuredClone( getTemplateSemanticManifest("chikorita"), ) as TemplateSemanticManifest; const ownerLie = structuredClone(getTemplateSemanticManifest("bronzor")) as TemplateSemanticManifest; const synthetic = structuredClone(getTemplateSemanticManifest("ditto")) as TemplateSemanticManifest; const missing = structuredClone(getTemplateSemanticManifest("azurill")) as TemplateSemanticManifest; const invented = structuredClone(getTemplateSemanticManifest("chikorita")) as TemplateSemanticManifest; (unknownRegionPlacement.regions[0] as { placement: string }).placement = "footer"; (unknownHeaderPlacement.header as { placement: string }).placement = "footer"; const alias = ownerLie.parts[0]?.binding; if (alias?.type !== "alias") throw new Error("Missing Bronzor alias fixture"); (alias as { canonicalKind: string }).canonicalKind = "item"; const primitive = synthetic.parts[0]?.binding; if (primitive?.type !== "primitive") throw new Error("Missing Ditto primitive fixture"); (primitive as unknown as { source: string }).source = "synthetic"; (missing.parts as TemplateSemanticManifest["parts"][number][]).pop(); (invented.parts as TemplateSemanticManifest["parts"][number][]).push({ name: "invented-wrapper", key: "invented-wrapper", owner: { kind: "header", key: "header" }, binding: { type: "primitive", primitive: "View", source: "existing" }, route: { parent: "owner", at: "start" }, }); expect(() => validateTemplateSemanticManifest(unknownRegionPlacement)).toThrow(/unknown region placement/); expect(() => validateTemplateSemanticManifest(unknownHeaderPlacement)).toThrow(/unknown header placement/); expect(() => validateTemplateSemanticManifest(ownerLie)).toThrow(/aliases a non-owner primitive/); expect(() => validateTemplateSemanticManifest(synthetic)).toThrow(/synthetic wrapper/); expect(() => validateTemplateSemanticManifest(missing)).toThrow(/frozen renderer contract/); expect(() => validateTemplateSemanticManifest(invented)).toThrow(/frozen renderer contract/); }); it("rejects exact renderer-contract mutations across layout, routing, bindings, conditions, and chrome", () => { const removedMain = structuredClone(getTemplateSemanticManifest("chikorita")) as TemplateSemanticManifest; const wrongOrigin = structuredClone(getTemplateSemanticManifest("chikorita")) as TemplateSemanticManifest; const wrongFlow = structuredClone(getTemplateSemanticManifest("bronzor")) as TemplateSemanticManifest; const wrongPrimitive = structuredClone(getTemplateSemanticManifest("ditto")) as TemplateSemanticManifest; const primitiveToAlias = structuredClone(getTemplateSemanticManifest("rhyhorn")) as TemplateSemanticManifest; const aliasToPrimitive = structuredClone(getTemplateSemanticManifest("bronzor")) as TemplateSemanticManifest; const wrongSummary = structuredClone(getTemplateSemanticManifest("leafish")) as TemplateSemanticManifest; const unknownOwner = structuredClone(getTemplateSemanticManifest("bronzor")) as TemplateSemanticManifest; const changedCondition = structuredClone(getTemplateSemanticManifest("azurill")) as TemplateSemanticManifest; const changedSelectorCondition = structuredClone(getTemplateSemanticManifest("meowth")) as TemplateSemanticManifest; const missingChrome = structuredClone(getTemplateSemanticManifest("ditto")) as TemplateSemanticManifest; const extraChrome = structuredClone(getTemplateSemanticManifest("ditto")) as TemplateSemanticManifest; (removedMain.regions as TemplateSemanticManifest["regions"][number][]).splice(1, 1); (wrongOrigin.regions[1]?.origins as string[])[0] = "sidebar"; (wrongFlow.regions[1] as { flow: string }).flow = "sequential"; const headerBand = wrongPrimitive.parts.find((part) => part.name === "header-band"); if (headerBand?.binding.type !== "primitive") throw new Error("Missing Ditto header band"); (headerBand.binding as { primitive: string }).primitive = "Text"; const content = primitiveToAlias.parts.find((part) => part.name === "contact-item-content"); if (!content) throw new Error("Missing Rhyhorn content"); (content as unknown as { binding: object; route?: object }).binding = { type: "alias", canonicalKind: "contact-item", token: "contact-item-content", }; delete (content as unknown as { route?: object }).route; const row = aliasToPrimitive.parts[0]; if (!row) throw new Error("Missing Bronzor row"); (row as unknown as { binding: object; route?: object }).binding = { type: "primitive", primitive: "View", source: "existing", }; (row as unknown as { route?: object }).route = { parent: "owner", at: "start" }; if (!wrongSummary.specialSummary) throw new Error("Missing Leafish summary"); (wrongSummary.specialSummary as { source: string }).source = "main-with-header"; (unknownOwner.parts[0]?.owner as unknown as { key: string }).key = "unknown"; const line = changedCondition.parts.find((part) => part.name === "timeline-line"); if (!line || !("columns" in line.owner)) throw new Error("Missing Azurill timeline line"); delete (line.owner as { columns?: number }).columns; const leading = changedSelectorCondition.parts.find((part) => part.name === "inline-item-header-leading"); if (leading?.binding.type !== "primitive" || !leading.route || !Array.isArray(leading.route.take)) { throw new Error("Missing Meowth leading selectors"); } (leading.route.take[0]?.sectionTypes as string[])[0] = "education"; (missingChrome.parts as TemplateSemanticManifest["parts"][number][]).pop(); const extra = structuredClone(extraChrome.parts[0]); if (!extra) throw new Error("Missing Ditto chrome"); (extra as { name: string; key: string }).name = "extra"; (extra as { name: string; key: string }).key = "extra"; (extraChrome.parts as TemplateSemanticManifest["parts"][number][]).push(extra); for (const mutation of [ removedMain, wrongOrigin, wrongFlow, wrongPrimitive, primitiveToAlias, aliasToPrimitive, wrongSummary, unknownOwner, changedCondition, changedSelectorCondition, missingChrome, extraChrome, ]) { expect(() => validateTemplateSemanticManifest(mutation)).toThrow(); } }); it("rejects empty and unknown selector section conditions before exact-contract comparison", () => { const empty = structuredClone(getTemplateSemanticManifest("meowth")) as TemplateSemanticManifest; const unknown = structuredClone(getTemplateSemanticManifest("meowth")) as TemplateSemanticManifest; const getFirstSelector = (manifest: TemplateSemanticManifest) => { const leading = manifest.parts.find((part) => part.name === "inline-item-header-leading"); if ( leading?.binding.type !== "primitive" || !leading.route || !Array.isArray(leading.route.take) || !leading.route.take[0] ) { throw new Error("Missing Meowth leading selector"); } return leading.route.take[0]; }; (getFirstSelector(empty) as { sectionTypes: string[] }).sectionTypes = []; (getFirstSelector(unknown) as { sectionTypes: string[] }).sectionTypes = ["unknown"]; expect(() => validateTemplateSemanticManifest(empty)).toThrow(/selector section types must not be empty/); expect(() => validateTemplateSemanticManifest(unknown)).toThrow(/selector has an unknown section type/); }); it("rejects cyclic and unsupported-depth primitive routing before exact-contract comparison", () => { const cycle = structuredClone(getTemplateSemanticManifest("azurill")) as TemplateSemanticManifest; const tooDeep = structuredClone(getTemplateSemanticManifest("azurill")) as TemplateSemanticManifest; const marker = cycle.parts.find((part) => part.name === "timeline-marker"); if (marker?.binding.type !== "primitive") throw new Error("Missing Azurill marker"); (marker.route as { parent: string }).parent = "timeline-dot"; const content = tooDeep.parts.find((part) => part.name === "timeline-content"); if (content?.binding.type !== "primitive") throw new Error("Missing Azurill content"); (content.route as { parent: string }).parent = "timeline-dot"; expect(() => validateTemplateSemanticManifest(cycle)).toThrow(/cyclic part routing/); expect(() => validateTemplateSemanticManifest(tooDeep)).toThrow(/unsupported part routing depth/); }); it.each(templateSchema.options)("%s builds its manifest-backed tree without key collisions", (template) => { const tree = buildFixtureTree(template); const nodes = flattenTree(tree); const partNames = new Set( findNodes(tree, (node) => node.kind === "template-part").map((node) => node.attributes.name), ); const expectedPrimitiveParts = new Set( getTemplateSemanticManifest(template).parts.flatMap((part) => part.binding.type === "primitive" ? [part.name] : [], ), ); expect(partNames).toEqual(expectedPrimitiveParts); expect(new Set(nodes.map((node) => node.key)).size).toBe(nodes.length); }); it("routes every registered primitive part through exactly the child kinds used by all template trees", () => { const data = buildFixture(); data.basics.phone = "+44 123"; data.basics.location = "London"; data.basics.website = { url: "https://example.com", label: "Website" }; data.sections.education.hidden = false; data.sections.volunteer.hidden = false; data.sections.experience.items = [ { id: "experience/inline", hidden: false, company: "Analytical Engines", position: "Engineer", location: "London", period: "1842", website: { url: "https://inline.example.com", label: "Inline", inlineLink: true }, description: "

Built algorithms.

", roles: [], }, { id: "experience/nested", hidden: false, company: "Difference Engines", position: "Programmer", location: "London", period: "1843", website: { url: "https://outside.example.com", label: "Outside", inlineLink: false }, description: "", roles: [ { id: "role/1", position: "Programmer", period: "1843", description: "

Published programs.

", }, ], }, ]; data.sections.education.items = [ { id: "education/coverage", hidden: false, school: "University", area: "Mathematics", degree: "BSc", grade: "First", location: "Oxford", period: "1835", website: { url: "", label: "", inlineLink: false }, description: "", }, ]; data.sections.volunteer.items = [ { id: "volunteer/coverage", hidden: false, organization: "Volunteer Guild", location: "Paris", period: "1840", website: { url: "", label: "", inlineLink: false }, description: "", }, ]; const page = { fullWidth: false, main: ["summary", "experience", "education", "volunteer", "skills"], sidebar: [], }; const partNames = new Set(); const childPairs = new Set(); for (const template of templateSchema.options) { const tree = buildSemanticTree({ data, template, page, pageNumber: 1, showHeader: true }); for (const part of findNodes(tree, (node) => node.kind === "template-part")) { const name = part.attributes.name; if (!name) throw new Error("Template part is missing its name"); partNames.add(name); for (const child of part.children) { childPairs.add(`${name}:${child.kind}`); } } } expect([...partNames].sort()).toEqual( [ "contact-item-content", "contact-offset", "contact-row-primary", "contact-row-secondary", "education-grade-row", "featured-summary", "header-band", "header-body", "header-contact-band", "header-divider", "header-intro", "header-name-rule", "inline-item-header-leading", "inline-item-header-middle", "inline-item-header-trailing", "picture-anchor", "sidebar-background", "timeline-content", "timeline-dot", "timeline-line", "timeline-marker", ].sort(), ); expect([...childPairs].sort()).toEqual( [ "contact-item-content:field", "contact-item-content:icon", "contact-item-content:link", "contact-row-primary:contact-item", "contact-row-secondary:contact-item", "education-grade-row:combined-text", "featured-summary:section", "header-band:headline", "header-band:name", "header-band:template-part", "header-body:headline", "header-body:name", "header-body:picture", "header-body:section", "header-contact-band:contact-list", "header-divider:headline", "header-divider:name", "header-intro:template-part", "inline-item-header-leading:combined-text", "inline-item-header-leading:field", "inline-item-header-middle:field", "inline-item-header-middle:link", "inline-item-header-trailing:field", "picture-anchor:picture", "timeline-content:field", "timeline-content:item", "timeline-content:item-header", "timeline-content:level", "timeline-content:link", "timeline-marker:template-part", ].sort(), ); }); it("nests Azurill's dot under its existing marker primitive", () => { const tree = buildFixtureTree("azurill"); const marker = findNodes( tree, (node) => node.kind === "template-part" && node.attributes.name === "timeline-marker", )[0]; expect(marker).toBeDefined(); expect( marker && findNodes(marker, (node) => node.kind === "template-part" && node.attributes.name === "timeline-dot"), ).toHaveLength(1); }); it("routes Azurill's canonical items through its exact timeline primitive hierarchy", () => { const tree = buildFixtureTree("azurill"); const experience = findNodes(tree, (node) => node.kind === "section" && node.id === "experience")[0]; const sectionItems = experience && findNodes(experience, (node) => node.kind === "section-items")[0]; const item = sectionItems?.children.find((node) => node.kind === "item"); const marker = item && findPart(item, "timeline-marker"); const content = item && findPart(item, "timeline-content"); expect(sectionItems && childLabels(sectionItems)).toEqual(["timeline-line", "item"]); expect(item && childLabels(item)).toEqual(["timeline-marker", "timeline-content"]); expect(marker && childLabels(marker)).toEqual(["timeline-dot"]); expect(content?.children.some((node) => node.kind === "item-header")).toBe(true); }); it("routes Leafish's header content through intro, body, and contact-band primitives", () => { const tree = buildFixtureTree("leafish"); const header = findNodes(tree, (node) => node.kind === "header")[0]; const intro = header && findPart(header, "header-intro"); const body = intro && findPart(intro, "header-body"); const contactBand = header && findPart(header, "header-contact-band"); expect(header && childLabels(header)).toEqual(["header-intro", "header-contact-band"]); expect(intro && childLabels(intro)).toEqual(["header-body"]); expect(body && childLabels(body)).toEqual(["picture", "name", "headline", "section"]); expect(contactBand && childLabels(contactBand)).toEqual(["contact-list"]); }); it("routes Ditto's header content through its exact primitive hierarchy and order", () => { const tree = buildFixtureTree("ditto"); const header = findNodes(tree, (node) => node.kind === "header")[0]; const band = header && findPart(header, "header-band"); const anchor = band && findPart(band, "picture-anchor"); expect(header && childLabels(header)).toEqual(["header-band", "contact-offset", "contact-list"]); expect(band && childLabels(band)).toEqual(["picture-anchor", "name", "headline"]); expect(anchor && childLabels(anchor)).toEqual(["picture"]); }); it.each([ ["bronzor", "section", "interleaved-section-row"], ["ditgar", "region", "sidebar-background"], ["gengar", "region", "sidebar-background"], ] as const)("%s applies %s to its canonical %s owner without an alias node", (template, kind, token) => { const tree = buildFixtureTree(template); const owner = findNodes( tree, (node) => node.kind === kind && node.attributes.part?.split(" ").includes(token) === true, )[0]; expect(owner).toBeDefined(); expect(findPart(tree, token)).toBeUndefined(); }); it("counts template aliases on their canonical owners in the binding inventory", () => { const tree = buildFixtureTree("bronzor"); const inventory = createBindingInventory(tree, getTemplateSemanticBindingRegistry("bronzor")); const aliasedSections = findNodes( tree, (node) => node.kind === "section" && node.attributes.part === "interleaved-section-row", ); expect(aliasedSections.length).toBeGreaterThan(0); expect(aliasedSections.map((node) => inventory.aliasTokensByNodeKey[node.key])).toEqual( aliasedSections.map(() => ["interleaved-section-row"]), ); expect( Object.values(inventory.bindings).some( (binding) => binding.type === "alias" && binding.token === "interleaved-section-row", ), ).toBe(false); }); it("binds Rhyhorn's outer contacts to View, nests their real content primitive, and aliases the rendered last owner", () => { const data = buildFixture(); data.basics.location = "London"; const tree = buildSemanticTree({ data, template: "rhyhorn", page: { fullWidth: true, main: [], sidebar: [] }, pageNumber: 1, showHeader: true, }); const contacts = findNodes(tree, (node) => node.kind === "contact-item"); const inventory = createBindingInventory(tree, getTemplateSemanticBindingRegistry("rhyhorn")); expect(contacts).toHaveLength(2); expect(contacts.map((node) => childLabels(node))).toEqual([["contact-item-content"], ["contact-item-content"]]); expect(contacts.map((node) => inventory.bindings[node.key])).toEqual([ { type: "primitive", primitive: "View", source: "existing" }, { type: "primitive", primitive: "View", source: "existing" }, ]); expect( contacts.map((node) => { const content = findPart(node, "contact-item-content"); return content && inventory.bindings[content.key]; }), ).toEqual([ { type: "primitive", primitive: "Link", source: "existing" }, { type: "primitive", primitive: "View", source: "existing" }, ]); const semanticLink = findNodes(contacts[0] ?? tree, (node) => node.kind === "link")[0]; const linkedContent = findPart(contacts[0] ?? tree, "contact-item-content"); expect(semanticLink && inventory.bindings[semanticLink.key]).toEqual({ type: "alias", canonicalKind: "template-part", canonicalNodeKey: linkedContent?.key, token: "structured-link", }); expect( [contacts[0]?.key, linkedContent?.key, semanticLink?.key].filter( (key): key is string => key !== undefined && inventory.bindings[key]?.type === "primitive", ), ).toEqual([contacts[0]?.key, linkedContent?.key]); expect(contacts[0]?.attributes.part).toBeUndefined(); expect(contacts[1]?.attributes.part).toBe("contact-item-last"); expect(inventory.aliasTokensByNodeKey[contacts[1]?.key ?? ""]).toEqual(["contact-item-last"]); expect(findPart(tree, "contact-item-last")).toBeUndefined(); }); it("does not invent Rhyhorn last-contact chrome when no contact is rendered or the header is hidden", () => { const data = buildFixture(); data.basics.email = ""; data.basics.customFields = []; const input = { data, template: "rhyhorn" as const, page: { fullWidth: true, main: [], sidebar: [] }, pageNumber: 1, }; const empty = buildSemanticTree({ ...input, showHeader: true }); const hidden = buildSemanticTree({ ...input, showHeader: false }); expect(findNodes(empty, (node) => node.kind === "contact-item")).toHaveLength(0); expect(findNodes(empty, (node) => node.attributes.part === "contact-item-last")).toHaveLength(0); expect(findNodes(hidden, (node) => node.kind === "header")).toHaveLength(0); }); it.each(["bronzor", "scizor"] as const)( "%s preserves layout origin separately from physical placement", (template) => { const tree = buildFixtureTree(template); const sidebarOrigin = findNodes(tree, (node) => node.kind === "section" && node.attributes.origin === "sidebar"); expect(sidebarOrigin.length).toBeGreaterThan(0); expect(sidebarOrigin.every((node) => node.attributes.placement === "main")).toBe(true); }, ); it.each([ ["ditgar", "sidebar"], ["gengar", "sidebar"], ["glalie", "sidebar"], ["azurill", "main"], ["leafish", "main"], ["pikachu", "main"], ] as const)("%s places its header in the renderer-owned %s column", (template, placement) => { const headerRegion = findNodes( buildFixtureTree(template), (node) => node.kind === "region" && node.attributes.region === "header", )[0]; expect(headerRegion?.attributes.placement).toBe(placement); }); it.each(["ditgar", "gengar"] as const)("%s moves the first-page summary into its featured region", (template) => { const tree = buildFixtureTree(template); const featured = findNodes(tree, (node) => node.kind === "region" && node.attributes.region === "featured")[0]; const summaries = findNodes(tree, (node) => node.kind === "section" && node.id === "summary"); expect(featured && findNodes(featured, (node) => node.kind === "section" && node.id === "summary")).toHaveLength(1); expect(summaries).toHaveLength(1); expect(summaries[0]?.roles).toContain("featured-summary"); }); it("places Leafish summary in the header only while the renderer shows that header", () => { const withHeader = buildFixtureTree("leafish", true); const withoutHeader = buildFixtureTree("leafish", false); const headerRegion = findNodes( withHeader, (node) => node.kind === "region" && node.attributes.region === "header", )[0]; expect( headerRegion && findNodes(headerRegion, (node) => node.kind === "section" && node.id === "summary"), ).toHaveLength(1); expect(findNodes(withHeader, (node) => node.kind === "section" && node.id === "summary")).toHaveLength(1); expect(findNodes(withoutHeader, (node) => node.kind === "section" && node.id === "summary")).toHaveLength(0); }); it("omits Leafish's header summary when the shared Summary renderer is not visible", () => { const data = buildFixture(); data.summary.content = ""; const tree = buildSemanticTree({ data, template: "leafish", page: { fullWidth: true, main: ["summary", "experience"], sidebar: [] }, pageNumber: 1, showHeader: true, }); expect(findNodes(tree, (node) => node.kind === "section" && node.id === "summary")).toHaveLength(0); }); it.each(["ditgar", "gengar"] as const)("%s leaves summary in main when featured chrome is absent", (template) => { const tree = buildFixtureTree(template, false); const summary = findNodes(tree, (node) => node.kind === "section" && node.id === "summary"); expect(findNodes(tree, (node) => node.kind === "region" && node.attributes.region === "featured")).toHaveLength(0); expect(summary).toHaveLength(1); expect(summary[0]?.attributes).toMatchObject({ origin: "main", placement: "main" }); expect(summary[0]?.roles).not.toContain("featured-summary"); }); it("emits Ditgar's existing item-header border owner for language and reference items", () => { const data = buildFixture(); data.sections.languages.items = [ { id: "language/1", hidden: false, language: "English", fluency: "Native", level: 4 }, ]; data.sections.references.items = [ { id: "reference/1", hidden: false, name: "Charles Babbage", position: "Inventor", phone: "+44 123", website: { url: "", label: "", inlineLink: false }, description: "", }, ]; const tree = buildSemanticTree({ data, template: "ditgar", page: { fullWidth: true, main: ["languages", "references"], sidebar: [] }, pageNumber: 1, showHeader: false, }); for (const itemId of ["language/1", "reference/1"]) { const item = findNodes(tree, (node) => node.kind === "item" && node.id === itemId)[0]; const header = item && findNodes(item, (node) => node.kind === "item-header")[0]; expect(header).toBeDefined(); expect(header?.attributes.part).toBe("item-header-border"); expect(header && findPart(header, "item-header-border")).toBeUndefined(); } }); it("keeps Ditgar's item-header border owner when optional language and reference header values are empty", () => { const data = buildFixture(); data.sections.languages.hidden = false; data.sections.references.hidden = false; data.sections.languages.items = [ { id: "language/empty", hidden: false, language: "English", fluency: "", level: 4 }, ]; data.sections.references.items = [ { id: "reference/empty", hidden: false, name: "Charles Babbage", position: "", phone: "", website: { url: "", label: "", inlineLink: false }, description: "

Reference body

", }, ]; const tree = buildSemanticTree({ data, template: "ditgar", page: { fullWidth: true, main: ["languages", "references"], sidebar: [] }, pageNumber: 1, showHeader: false, }); for (const itemId of ["language/empty", "reference/empty"]) { const item = findNodes(tree, (node) => node.kind === "item" && node.id === itemId)[0]; const headers = item ? item.children.filter((node) => node.kind === "item-header") : []; expect(item).toBeDefined(); expect(headers).toHaveLength(1); expect(headers[0]?.attributes.part).toBe("item-header-border"); } }); it("keeps Meowth's three inline header primitives when optional header slots are empty", () => { const data = buildFixture(); data.sections.experience.hidden = false; data.sections.education.hidden = false; data.sections.volunteer.hidden = false; data.sections.experience.items = [ { id: "experience/empty", hidden: false, company: "Analytical Engines", position: "", location: "", period: "", website: { url: "", label: "", inlineLink: false }, description: "

Experience body

", roles: [], }, ]; data.sections.education.items = [ { id: "education/empty", hidden: false, school: "University", area: "", degree: "", grade: "", location: "", period: "", website: { url: "", label: "", inlineLink: false }, description: "

Education body

", }, ]; data.sections.volunteer.items = [ { id: "volunteer/empty", hidden: false, organization: "Volunteer Guild", location: "", period: "", website: { url: "", label: "", inlineLink: false }, description: "

Volunteer body

", }, ]; const tree = buildSemanticTree({ data, template: "meowth", page: { fullWidth: true, main: ["experience", "education", "volunteer"], sidebar: [] }, pageNumber: 1, showHeader: false, }); for (const itemId of ["experience/empty", "education/empty", "volunteer/empty"]) { const item = findNodes(tree, (node) => node.kind === "item" && node.id === itemId)[0]; const header = item?.children.find((node) => node.kind === "item-header"); expect(item).toBeDefined(); expect(header && childLabels(header)).toEqual([ "inline-item-header-leading", "inline-item-header-middle", "inline-item-header-trailing", ]); expect(header?.children[0]?.children).toEqual([]); expect(header?.children[1]?.children).toHaveLength(1); expect(header?.children[2]?.children).toEqual([]); } }); it("routes Meowth header fields by section while leaving education grade and location after the inline parts", () => { const data = buildFixture(); data.sections.experience.hidden = false; data.sections.education.hidden = false; data.sections.volunteer.hidden = false; data.sections.experience.items = [ { id: "experience/routing", hidden: false, company: "Analytical Engines", position: "Engineer", location: "London", period: "1842", website: { url: "", label: "", inlineLink: false }, description: "", roles: [], }, ]; data.sections.education.items = [ { id: "education/routing", hidden: false, school: "University", area: "Mathematics", degree: "BSc", grade: "First", location: "Oxford", period: "1835", website: { url: "", label: "", inlineLink: false }, description: "", }, ]; data.sections.volunteer.items = [ { id: "volunteer/routing", hidden: false, organization: "Volunteer Guild", location: "Paris", period: "1840", website: { url: "", label: "", inlineLink: false }, description: "", }, ]; const tree = buildSemanticTree({ data, template: "meowth", page: { fullWidth: true, main: ["experience", "education", "volunteer"], sidebar: [] }, pageNumber: 1, showHeader: false, }); const itemFor = (itemId: string) => findNodes(tree, (node) => node.kind === "item" && node.id === itemId)[0]; const itemHeader = (itemId: string) => itemFor(itemId)?.children.find((node) => node.kind === "item-header"); const partFields = (header: SemanticNode | undefined, name: string) => (header && findPart(header, name)?.children.flatMap((node) => node.kind === "combined-text" ? node.children.map((child) => child.attributes.name) : [node.attributes.name], )) ?? []; const experience = itemHeader("experience/routing"); const education = itemHeader("education/routing"); const volunteer = itemHeader("volunteer/routing"); expect(partFields(experience, "inline-item-header-leading")).toEqual(["position", "location"]); expect(partFields(volunteer, "inline-item-header-leading")).toEqual(["location"]); expect(partFields(education, "inline-item-header-leading")).toEqual(["area", "degree"]); expect(education && childLabels(education)).toEqual([ "inline-item-header-leading", "inline-item-header-middle", "inline-item-header-trailing", ]); const educationItem = itemFor("education/routing"); const gradeRow = educationItem && findPart(educationItem, "education-grade-row"); expect(educationItem && childLabels(educationItem).slice(0, 2)).toEqual(["item-header", "education-grade-row"]); expect(gradeRow?.children).toHaveLength(1); expect(gradeRow?.children[0]?.kind).toBe("combined-text"); expect(gradeRow?.children[0]?.children.map((node) => node.attributes.name)).toEqual(["grade", "location"]); }); it.each(["bronzor", "scizor"] as const)( "%s keeps duplicate flattened origins and repeated authored pages collision-safe", (template) => { const data = buildFixture(); const page = { fullWidth: false, main: ["experience"], sidebar: ["experience"] }; const first = buildSemanticTree({ data, template, page, pageNumber: 1, showHeader: false }); const second = buildSemanticTree({ data, template, page, pageNumber: 2, showHeader: false }); const fullWidth = buildSemanticTree({ data, template, page: { ...page, fullWidth: true }, pageNumber: 1, showHeader: false, }); const firstSections = findNodes(first, (node) => node.kind === "section" && node.id === "experience"); expect(firstSections).toHaveLength(2); expect(new Set(firstSections.map((node) => node.key)).size).toBe(2); expect(findNodes(second, (node) => node.kind === "section").every((node) => node.key.startsWith("page-2/"))).toBe( true, ); expect(findNodes(fullWidth, (node) => node.kind === "section" && node.id === "experience")).toHaveLength(1); }, ); it("publishes stable, deterministic, deeply frozen fingerprint input without functions", () => { const first = getTemplateSemanticRegistryFingerprintInput(); const second = getTemplateSemanticRegistryFingerprintInput(); const serialized = JSON.stringify(first); expect(second).toBe(first); expect(JSON.stringify(second)).toBe(serialized); expect(Object.isFrozen(first)).toBe(true); expect(Object.isFrozen(first.azurill.parts)).toBe(true); expect(flattenValues(first).every((value) => typeof value !== "function")).toBe(true); expect(() => { (first.azurill.parts as unknown as object[]).pop(); }).toThrow(); expect(JSON.stringify(getTemplateSemanticRegistryFingerprintInput())).toBe(serialized); }); it.each(templateSchema.options)( "%s binds every manifest node to existing chrome without synthetic wrappers", (template) => { const tree = buildFixtureTree(template); const inventory = createBindingInventory(tree, getTemplateSemanticBindingRegistry(template)); const nodeCount = flattenTree(tree).length; expect(inventory.unboundNodeKeys).toEqual([]); expect(inventory.syntheticWrapperCount).toBe(0); expect(Object.keys(inventory.bindings)).toHaveLength(nodeCount); for (const partNode of findNodes(tree, (node) => node.kind === "template-part")) { const binding = inventory.bindings[partNode.key]; const declaration = getTemplateSemanticManifest(template).parts.find( (part) => part.name === partNode.attributes.name, ); expect(declaration).toBeDefined(); expect(declaration?.binding.type).toBe("primitive"); if (declaration?.binding.type === "primitive" && typeof declaration.binding.primitive === "string") { expect(binding).toEqual(declaration.binding); } else if (declaration?.binding.type === "primitive") { const primitive = declaration.binding.primitive; if (typeof primitive === "string") throw new Error("Expected conditional primitive"); expect(binding).toMatchObject({ type: "primitive", source: "existing", }); expect([primitive.present, primitive.absent]).toContain( binding?.type === "primitive" ? binding.primitive : undefined, ); } } }, ); });