@warpgogol/werkstatt-shared 0.14.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +26 -16
  2. package/README.uk.md +24 -14
  3. package/package.json +7 -1
  4. package/src/checks/result-helpers.ts +21 -4
  5. package/src/checks/tests/remediation-auto-populate.test.ts +83 -0
  6. package/src/integration/vitest.config.ts +7 -0
  7. package/src/ontology/manifest.ts +19 -0
  8. package/src/ontology/schemas/system/tests/growth.test.ts +22 -0
  9. package/src/ontology/schemas/system/tests/integrations.test.ts +12 -0
  10. package/src/ontology/schemas/system/tests/manifest.test.ts +22 -0
  11. package/src/ontology/schemas/system/tests/page-output.test.ts +13 -0
  12. package/src/ontology/schemas/system/tests/release.test.ts +22 -0
  13. package/src/ontology/schemas/system/tests/text.test.ts +18 -0
  14. package/src/ontology/schemas/system/tests/verification.test.ts +12 -0
  15. package/src/ontology/schemas/tests/biome.test.ts +19 -0
  16. package/src/ontology/schemas/tests/capability.test.ts +22 -0
  17. package/src/ontology/schemas/tests/constellation.test.ts +22 -0
  18. package/src/ontology/schemas/tests/dns-records.test.ts +13 -0
  19. package/src/ontology/schemas/tests/manifest-resolver.test.ts +13 -0
  20. package/src/ontology/schemas/tests/page-entry.test.ts +22 -0
  21. package/src/ontology/schemas/tests/section-archetype.test.ts +12 -0
  22. package/src/ontology/schemas/tests/site-family.test.ts +18 -0
  23. package/src/ontology/schemas/tests/system.test.ts +20 -0
  24. package/src/share/agent/tests/api-catalog.test.ts +8 -0
  25. package/src/share/agent/tests/ard-catalog.test.ts +8 -0
  26. package/src/share/agent/tests/capability.test.ts +14 -0
  27. package/src/share/agent/tests/fleet-catalog.test.ts +15 -0
  28. package/src/share/agent/tests/knowledge.test.ts +20 -0
  29. package/src/share/agent/tests/manifest.test.ts +9 -0
  30. package/src/share/agent/tests/mcp-card.test.ts +8 -0
  31. package/src/share/agent/tests/omit-empty.test.ts +33 -0
  32. package/src/share/agent/tests/openapi.test.ts +8 -0
  33. package/src/share/agent/tests/proof.test.ts +15 -0
  34. package/src/share/knowledge/tests/claim.test.ts +11 -0
  35. package/src/share/knowledge/tests/derivation.test.ts +30 -0
  36. package/src/share/knowledge/tests/freshness.test.ts +18 -0
  37. package/src/share/knowledge/tests/ledger.test.ts +35 -0
  38. package/src/share/knowledge/tests/plan.test.ts +19 -0
  39. package/src/share/knowledge/tests/source.test.ts +19 -0
  40. package/src/share/remediation/remediation-catalog.ts +196 -0
  41. package/src/share/schemas/tests/claim-records.test.ts +52 -0
  42. package/src/share/schemas/tests/claims.test.ts +23 -0
  43. package/src/share/schemas/tests/effects.test.ts +116 -0
  44. package/src/share/schemas/tests/features.test.ts +61 -0
  45. package/src/share/schemas/tests/horizontal-align.test.ts +16 -0
  46. package/src/share/schemas/tests/icon-color.test.ts +16 -0
  47. package/src/share/schemas/tests/live-photo.test.ts +40 -0
  48. package/src/share/schemas/tests/material-credit.test.ts +51 -0
  49. package/src/share/schemas/tests/media.test.ts +26 -0
  50. package/src/share/schemas/tests/navigation.test.ts +58 -0
  51. package/src/share/schemas/tests/page-base.test.ts +19 -0
  52. package/src/share/schemas/tests/participant.test.ts +26 -0
  53. package/src/share/schemas/tests/person.test.ts +12 -0
  54. package/src/share/schemas/tests/print.test.ts +30 -0
  55. package/src/share/schemas/tests/section-background.test.ts +31 -0
  56. package/src/share/schemas/tests/section-body.test.ts +16 -0
  57. package/src/share/schemas/tests/section-cards.test.ts +17 -0
  58. package/src/share/schemas/tests/section-cta.test.ts +41 -0
  59. package/src/share/schemas/tests/section-header.test.ts +31 -0
  60. package/src/share/schemas/tests/section-image.test.ts +24 -0
  61. package/src/share/schemas/tests/section-motion.test.ts +29 -0
  62. package/src/share/schemas/tests/section-shell.test.ts +42 -0
  63. package/src/share/schemas/tests/section-stats.test.ts +17 -0
  64. package/src/share/schemas/tests/site-background.test.ts +33 -0
  65. package/src/share/schemas/tests/standard-list-item.test.ts +16 -0
  66. package/src/share/scripts/tests/external-link-qr.test.ts +8 -0
  67. package/src/share/scripts/tests/external-links.test.ts +25 -0
  68. package/src/share/scripts/tests/gsap-counter.test.ts +8 -0
  69. package/src/share/scripts/tests/gsap-mountain-journey.test.ts +8 -0
  70. package/src/share/scripts/tests/gsap-parallax.test.ts +8 -0
  71. package/src/share/scripts/tests/gsap-reveal.test.ts +8 -0
  72. package/src/share/scripts/tests/gsap-stagger.test.ts +8 -0
  73. package/src/share/scripts/tests/inline-number-animation.test.ts +8 -0
  74. package/src/share/scripts/tests/lenis.test.ts +8 -0
  75. package/src/share/scripts/tests/live-photos.test.ts +8 -0
  76. package/src/share/scripts/tests/lordicon.test.ts +8 -0
  77. package/src/share/scripts/tests/orchestrator.test.ts +8 -0
  78. package/src/share/scripts/tests/scheduler.test.ts +23 -0
  79. package/src/share/semantic/jsonld/tests/article.test.ts +89 -0
  80. package/src/share/semantic/jsonld/tests/breadcrumb.test.ts +62 -0
  81. package/src/share/semantic/jsonld/tests/collection-list.test.ts +39 -0
  82. package/src/share/semantic/jsonld/tests/faq.test.ts +60 -0
  83. package/src/share/semantic/jsonld/tests/initiative.test.ts +56 -0
  84. package/src/share/semantic/jsonld/tests/organization.test.ts +123 -0
  85. package/src/share/semantic/jsonld/tests/person.test.ts +89 -0
  86. package/src/share/semantic/jsonld/tests/service.test.ts +65 -0
  87. package/src/share/semantic/jsonld/tests/shared.test.ts +41 -0
  88. package/src/share/semantic/jsonld/tests/video.test.ts +84 -0
  89. package/src/share/semantic/jsonld/tests/webpage.test.ts +93 -0
  90. package/src/share/semantic/jsonld/tests/website.test.ts +27 -0
  91. package/src/share/semantic/jsonld/types.ts +1 -0
  92. package/src/share/semantic/models.ts +1 -0
  93. package/src/share/semantic/tests/ai.test.ts +55 -0
  94. package/src/share/semantic/tests/block-extraction.test.ts +34 -0
  95. package/src/share/semantic/tests/breadcrumbs.test.ts +127 -0
  96. package/src/share/semantic/tests/build-page.test.ts +122 -0
  97. package/src/share/semantic/tests/business-projection.test.ts +188 -0
  98. package/src/share/semantic/tests/derived-prices-loader.test.ts +46 -0
  99. package/src/share/semantic/tests/extract.test.ts +126 -0
  100. package/src/share/semantic/tests/feed.test.ts +113 -0
  101. package/src/share/semantic/tests/helpers.ts +32 -0
  102. package/src/share/semantic/tests/ids.test.ts +118 -0
  103. package/src/share/semantic/tests/image-sitemap.test.ts +109 -0
  104. package/src/share/semantic/tests/jsonld.test.ts +61 -0
  105. package/src/share/semantic/tests/llms-policy.test.ts +60 -0
  106. package/src/share/semantic/tests/llms.test.ts +118 -0
  107. package/src/share/semantic/tests/markdown-hygiene.test.ts +72 -0
  108. package/src/share/semantic/tests/markdown-page.test.ts +97 -0
  109. package/src/share/semantic/tests/markdown-twin-provenance.test.ts +152 -0
  110. package/src/share/semantic/tests/organization-profile.test.ts +107 -0
  111. package/src/share/semantic/tests/output-projection.test.ts +77 -0
  112. package/src/share/semantic/tests/page-markdown.test.ts +98 -0
  113. package/src/share/semantic/tests/page-utils.test.ts +88 -0
  114. package/src/share/semantic/tests/price-marker-resolver.test.ts +88 -0
  115. package/src/share/semantic/tests/robots.test.ts +70 -0
  116. package/src/share/semantic/tests/update-stamp.test.ts +102 -0
  117. package/src/share/utility-registry.yaml +14 -0
@@ -0,0 +1,116 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ namedEffectTargetSchema,
4
+ effectTargetSchema,
5
+ glassEffectSchema,
6
+ effectSchema,
7
+ EFFECT_KIND_META,
8
+ allowedKindsForTarget,
9
+ resolveEffectsForTarget,
10
+ hasEnabledGlassEffect,
11
+ } from "../effects.ts";
12
+ import type { EffectAssignment } from "../effects.ts";
13
+ import { z } from "zod";
14
+
15
+ type ParsedEffect = z.infer<typeof effectSchema>;
16
+
17
+ describe("namedEffectTargetSchema", () => {
18
+ it("accepts valid targets", () => {
19
+ expect(namedEffectTargetSchema.parse("section")).toBe("section");
20
+ expect(namedEffectTargetSchema.parse("heading")).toBe("heading");
21
+ });
22
+
23
+ it("rejects invalid target", () => {
24
+ expect(() => namedEffectTargetSchema.parse("unknown")).toThrow();
25
+ });
26
+ });
27
+
28
+ describe("effectTargetSchema", () => {
29
+ it("accepts slot targets", () => {
30
+ expect(effectTargetSchema.parse("slot:my-slot")).toBe("slot:my-slot");
31
+ });
32
+
33
+ it("rejects invalid slot format", () => {
34
+ expect(() => effectTargetSchema.parse("slot:")).toThrow();
35
+ });
36
+ });
37
+
38
+ describe("glassEffectSchema", () => {
39
+ it("accepts valid glass effect", () => {
40
+ const result = glassEffectSchema.safeParse({ kind: "glass", enabled: true });
41
+ expect(result.success).toBe(true);
42
+ });
43
+
44
+ it("rejects missing required fields", () => {
45
+ expect(() => glassEffectSchema.parse({ kind: "glass" })).toThrow();
46
+ });
47
+
48
+ it("rejects extra keys (strict)", () => {
49
+ expect(() => glassEffectSchema.parse({ kind: "glass", enabled: true, extra: true })).toThrow();
50
+ });
51
+ });
52
+
53
+ describe("effectSchema (discriminated union)", () => {
54
+ it("accepts glass effect", () => {
55
+ const result = effectSchema.safeParse({ kind: "glass", enabled: true });
56
+ expect(result.success).toBe(true);
57
+ });
58
+
59
+ it("accepts shadow effect", () => {
60
+ const result = effectSchema.safeParse({ kind: "shadow", enabled: true });
61
+ expect(result.success).toBe(true);
62
+ });
63
+
64
+ it("rejects unknown kind", () => {
65
+ expect(() => effectSchema.parse({ kind: "unknown", enabled: true })).toThrow();
66
+ });
67
+ });
68
+
69
+ describe("EFFECT_KIND_META", () => {
70
+ it("has metadata for all effect kinds", () => {
71
+ expect(EFFECT_KIND_META.glass.strategy).toBe("surface");
72
+ expect(EFFECT_KIND_META.shadow.strategy).toBe("text");
73
+ });
74
+ });
75
+
76
+ describe("allowedKindsForTarget", () => {
77
+ it("returns text kinds for heading", () => {
78
+ const kinds = allowedKindsForTarget("heading");
79
+ expect(kinds).toContain("shadow");
80
+ expect(kinds).not.toContain("glass");
81
+ });
82
+
83
+ it("returns glass-only default for body", () => {
84
+ const kinds = allowedKindsForTarget("body");
85
+ expect(kinds).toEqual(["glass"]);
86
+ });
87
+ });
88
+
89
+ describe("resolveEffectsForTarget", () => {
90
+ it("returns empty array when no assignments", () => {
91
+ expect(resolveEffectsForTarget(undefined, "section")).toEqual([]);
92
+ });
93
+
94
+ it("returns effects for matching target", () => {
95
+ const glassEffect: ParsedEffect = effectSchema.parse({ kind: "glass", enabled: true });
96
+ const assignments: EffectAssignment[] = [{ target: "section", stack: [glassEffect] }];
97
+ const effects = resolveEffectsForTarget(assignments, "section");
98
+ expect(effects).toHaveLength(1);
99
+ });
100
+ });
101
+
102
+ describe("hasEnabledGlassEffect", () => {
103
+ it("returns true when glass effect is enabled", () => {
104
+ const e: ParsedEffect = effectSchema.parse({ kind: "glass", enabled: true });
105
+ expect(hasEnabledGlassEffect([e])).toBe(true);
106
+ });
107
+
108
+ it("returns false when glass effect is disabled", () => {
109
+ const e: ParsedEffect = effectSchema.parse({ kind: "glass", enabled: false });
110
+ expect(hasEnabledGlassEffect([e])).toBe(false);
111
+ });
112
+
113
+ it("returns false when no effects", () => {
114
+ expect(hasEnabledGlassEffect(undefined)).toBe(false);
115
+ });
116
+ });
@@ -0,0 +1,61 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ featurePolicyVisibilitySchema,
4
+ featurePolicyBehaviorValueSchema,
5
+ featurePolicySchema,
6
+ } from "../features.ts";
7
+
8
+ describe("featurePolicyVisibilitySchema", () => {
9
+ it("accepts valid visibility values", () => {
10
+ for (const v of ["enabled", "disabled", "hidden", "draft"]) {
11
+ expect(featurePolicyVisibilitySchema.parse(v)).toBe(v);
12
+ }
13
+ });
14
+
15
+ it("rejects invalid values", () => {
16
+ expect(() => featurePolicyVisibilitySchema.parse("visible")).toThrow();
17
+ });
18
+ });
19
+
20
+ describe("featurePolicyBehaviorValueSchema", () => {
21
+ it("accepts string values", () => {
22
+ expect(featurePolicyBehaviorValueSchema.parse("hello")).toBe("hello");
23
+ });
24
+
25
+ it("accepts number values", () => {
26
+ expect(featurePolicyBehaviorValueSchema.parse(42)).toBe(42);
27
+ });
28
+
29
+ it("accepts boolean values", () => {
30
+ expect(featurePolicyBehaviorValueSchema.parse(true)).toBe(true);
31
+ });
32
+
33
+ it("accepts null", () => {
34
+ expect(featurePolicyBehaviorValueSchema.parse(null)).toBeNull();
35
+ });
36
+
37
+ it("rejects objects", () => {
38
+ expect(() => featurePolicyBehaviorValueSchema.parse({})).toThrow();
39
+ });
40
+ });
41
+
42
+ describe("featurePolicySchema", () => {
43
+ it("accepts empty object (all fields optional)", () => {
44
+ expect(featurePolicySchema.parse({})).toEqual({});
45
+ });
46
+
47
+ it("accepts full policy", () => {
48
+ const policy = {
49
+ visibility: "enabled",
50
+ behavior: { key: "value" },
51
+ reason: "test",
52
+ expiresAt: "2026-12-31",
53
+ audience: "all",
54
+ };
55
+ expect(featurePolicySchema.parse(policy)).toEqual(policy);
56
+ });
57
+
58
+ it("rejects invalid visibility", () => {
59
+ expect(() => featurePolicySchema.parse({ visibility: "invalid" })).toThrow();
60
+ });
61
+ });
@@ -0,0 +1,16 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { horizontalAlignSchema } from "../horizontal-align.ts";
3
+
4
+ describe("horizontalAlignSchema", () => {
5
+ it("accepts valid alignment values", () => {
6
+ expect(horizontalAlignSchema.parse("left")).toBe("left");
7
+ expect(horizontalAlignSchema.parse("center")).toBe("center");
8
+ expect(horizontalAlignSchema.parse("right")).toBe("right");
9
+ });
10
+
11
+ it("rejects invalid alignment values", () => {
12
+ expect(() => horizontalAlignSchema.parse("top")).toThrow();
13
+ expect(() => horizontalAlignSchema.parse("")).toThrow();
14
+ expect(() => horizontalAlignSchema.parse(null)).toThrow();
15
+ });
16
+ });
@@ -0,0 +1,16 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { iconColorSchema } from "../icon-color.ts";
3
+
4
+ describe("iconColorSchema", () => {
5
+ it("accepts valid icon colors", () => {
6
+ for (const c of ["primary", "accent", "success", "warning", "error", "muted"]) {
7
+ expect(iconColorSchema.parse(c)).toBe(c);
8
+ }
9
+ });
10
+
11
+ it("rejects invalid icon colors", () => {
12
+ expect(() => iconColorSchema.parse("blue")).toThrow();
13
+ expect(() => iconColorSchema.parse("")).toThrow();
14
+ expect(() => iconColorSchema.parse(123)).toThrow();
15
+ });
16
+ });
@@ -0,0 +1,40 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { livePhotoTriggerSchema, livePhotoSchema } from "../live-photo.ts";
3
+
4
+ describe("livePhotoTriggerSchema", () => {
5
+ it("accepts valid triggers", () => {
6
+ for (const t of ["in-viewport", "tap", "autoplay"]) {
7
+ expect(livePhotoTriggerSchema.parse(t)).toBe(t);
8
+ }
9
+ });
10
+
11
+ it("rejects invalid trigger", () => {
12
+ expect(() => livePhotoTriggerSchema.parse("scroll")).toThrow();
13
+ });
14
+ });
15
+
16
+ describe("livePhotoSchema", () => {
17
+ it("accepts minimal valid object with defaults", () => {
18
+ const result = livePhotoSchema.parse({ enabled: true });
19
+ expect(result.enabled).toBe(true);
20
+ });
21
+
22
+ it("accepts full config", () => {
23
+ const config = {
24
+ enabled: true,
25
+ trigger: "in-viewport",
26
+ loop: true,
27
+ tapBehavior: "toggle",
28
+ preload: "metadata",
29
+ };
30
+ expect(livePhotoSchema.parse(config)).toEqual(config);
31
+ });
32
+
33
+ it("rejects unknown keys (strict)", () => {
34
+ expect(() => livePhotoSchema.parse({ enabled: true, unknown: true })).toThrow();
35
+ });
36
+
37
+ it("rejects invalid trigger", () => {
38
+ expect(() => livePhotoSchema.parse({ enabled: true, trigger: "scroll" })).toThrow();
39
+ });
40
+ });
@@ -0,0 +1,51 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ materialKindSchema,
4
+ materialSourceTypeSchema,
5
+ creditPartyKindSchema,
6
+ creditRoleSchema,
7
+ } from "../material-credit.ts";
8
+
9
+ describe("materialKindSchema", () => {
10
+ it("accepts valid kinds", () => {
11
+ const result = materialKindSchema.safeParse("image");
12
+ expect(result.success).toBe(true);
13
+ });
14
+
15
+ it("rejects invalid kind", () => {
16
+ expect(() => materialKindSchema.parse("3d-model")).toThrow();
17
+ });
18
+ });
19
+
20
+ describe("materialSourceTypeSchema", () => {
21
+ it("accepts valid source types", () => {
22
+ const result = materialSourceTypeSchema.safeParse("human-made");
23
+ expect(result.success).toBe(true);
24
+ });
25
+
26
+ it("rejects invalid source type", () => {
27
+ expect(() => materialSourceTypeSchema.parse("unknown")).toThrow();
28
+ });
29
+ });
30
+
31
+ describe("creditPartyKindSchema", () => {
32
+ it("accepts valid party kinds", () => {
33
+ const result = creditPartyKindSchema.safeParse("Person");
34
+ expect(result.success).toBe(true);
35
+ });
36
+
37
+ it("rejects invalid party kind", () => {
38
+ expect(() => creditPartyKindSchema.parse("Robot")).toThrow();
39
+ });
40
+ });
41
+
42
+ describe("creditRoleSchema", () => {
43
+ it("accepts valid roles", () => {
44
+ const result = creditRoleSchema.safeParse("creator");
45
+ expect(result.success).toBe(true);
46
+ });
47
+
48
+ it("rejects invalid role", () => {
49
+ expect(() => creditRoleSchema.parse("owner")).toThrow();
50
+ });
51
+ });
@@ -0,0 +1,26 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { mediaProfileSchema, mediaTriggerSchema } from "../media.ts";
3
+
4
+ describe("mediaProfileSchema", () => {
5
+ it("accepts valid profiles", () => {
6
+ for (const p of ["feature", "background", "ambient"]) {
7
+ expect(mediaProfileSchema.parse(p)).toBe(p);
8
+ }
9
+ });
10
+
11
+ it("rejects invalid profile", () => {
12
+ expect(() => mediaProfileSchema.parse("streaming")).toThrow();
13
+ });
14
+ });
15
+
16
+ describe("mediaTriggerSchema", () => {
17
+ it("accepts valid triggers", () => {
18
+ for (const t of ["in-viewport", "tap", "autoplay"]) {
19
+ expect(mediaTriggerSchema.parse(t)).toBe(t);
20
+ }
21
+ });
22
+
23
+ it("rejects invalid trigger", () => {
24
+ expect(() => mediaTriggerSchema.parse("scroll")).toThrow();
25
+ });
26
+ });
@@ -0,0 +1,58 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ createNavigationGroupEnum,
4
+ defaultNavigationGroups,
5
+ navigationTargetSchema,
6
+ navigationSchema,
7
+ } from "../navigation.ts";
8
+
9
+ describe("defaultNavigationGroups", () => {
10
+ it("contains expected groups", () => {
11
+ expect(defaultNavigationGroups).toEqual(["navigation", "legal", "contact"]);
12
+ });
13
+ });
14
+
15
+ describe("createNavigationGroupEnum", () => {
16
+ it("creates a Zod enum from group names", () => {
17
+ const enumSchema = createNavigationGroupEnum(["main", "footer"]);
18
+ expect(enumSchema.parse("main")).toBe("main");
19
+ expect(enumSchema.parse("footer")).toBe("footer");
20
+ });
21
+
22
+ it("rejects values not in the enum", () => {
23
+ const enumSchema = createNavigationGroupEnum(["main"]);
24
+ expect(() => enumSchema.parse("footer")).toThrow();
25
+ });
26
+ });
27
+
28
+ describe("navigationTargetSchema", () => {
29
+ it("validates a target with valid group", () => {
30
+ const groupEnum = createNavigationGroupEnum(["navigation", "legal"]);
31
+ const schema = navigationTargetSchema(groupEnum);
32
+ const result = schema.safeParse({
33
+ id: "home",
34
+ label: "Home",
35
+ semanticTarget: { kind: "internal", pageId: "home" },
36
+ group: "navigation",
37
+ });
38
+ expect(result.success).toBe(true);
39
+ });
40
+ });
41
+
42
+ describe("navigationSchema", () => {
43
+ it("validates a navigation object with targets", () => {
44
+ const groupEnum = createNavigationGroupEnum(["navigation"]);
45
+ const schema = navigationSchema(groupEnum);
46
+ const result = schema.safeParse({
47
+ targets: [
48
+ {
49
+ id: "home",
50
+ label: "Home",
51
+ semanticTarget: { kind: "internal", pageId: "home" },
52
+ group: "navigation",
53
+ },
54
+ ],
55
+ });
56
+ expect(result.success).toBe(true);
57
+ });
58
+ });
@@ -0,0 +1,19 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { componentOverridesSchema } from "../page-base.ts";
3
+
4
+ describe("componentOverridesSchema", () => {
5
+ it("accepts undefined (optional)", () => {
6
+ expect(componentOverridesSchema.parse(undefined)).toBeUndefined();
7
+ });
8
+
9
+ it("accepts empty record", () => {
10
+ expect(componentOverridesSchema.parse({})).toEqual({});
11
+ });
12
+
13
+ it("accepts a valid overrides map", () => {
14
+ const overrides = {
15
+ "components/Header": { title: "Custom" },
16
+ };
17
+ expect(componentOverridesSchema.parse(overrides)).toEqual(overrides);
18
+ });
19
+ });
@@ -0,0 +1,26 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ PARTICIPANT_TYPES,
4
+ PARTICIPANT_RELATIONSHIPS,
5
+ PARTICIPANT_STATUSES,
6
+ } from "../participant.ts";
7
+
8
+ describe("PARTICIPANT_TYPES", () => {
9
+ it("contains expected types", () => {
10
+ expect(PARTICIPANT_TYPES).toContain("human");
11
+ expect(PARTICIPANT_TYPES).toContain("ai-agent");
12
+ expect(PARTICIPANT_TYPES).toHaveLength(6);
13
+ });
14
+ });
15
+
16
+ describe("PARTICIPANT_RELATIONSHIPS", () => {
17
+ it("is a non-empty array", () => {
18
+ expect(PARTICIPANT_RELATIONSHIPS.length).toBeGreaterThan(0);
19
+ });
20
+ });
21
+
22
+ describe("PARTICIPANT_STATUSES", () => {
23
+ it("is a non-empty array", () => {
24
+ expect(PARTICIPANT_STATUSES.length).toBeGreaterThan(0);
25
+ });
26
+ });
@@ -0,0 +1,12 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { PERSON_AFFILIATIONS } from "../person.ts";
3
+
4
+ describe("PERSON_AFFILIATIONS", () => {
5
+ it("contains the expected affiliation values", () => {
6
+ expect(PERSON_AFFILIATIONS).toEqual(["founder", "board", "team", "patron", "author"]);
7
+ });
8
+
9
+ it("is a readonly tuple", () => {
10
+ expect(PERSON_AFFILIATIONS).toHaveLength(5);
11
+ });
12
+ });
@@ -0,0 +1,30 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ PRINT_ORIENTATIONS,
4
+ PRINT_PAGE_SIZES,
5
+ PRINT_MARGINS,
6
+ PRINT_BACKGROUND_MODES,
7
+ PRINT_REGIONS,
8
+ } from "../print.ts";
9
+
10
+ describe("print constants", () => {
11
+ it("PRINT_ORIENTATIONS has expected values", () => {
12
+ expect(PRINT_ORIENTATIONS).toEqual(["portrait", "landscape", "auto"]);
13
+ });
14
+
15
+ it("PRINT_PAGE_SIZES has expected values", () => {
16
+ expect(PRINT_PAGE_SIZES).toEqual(["a4", "letter", "legal"]);
17
+ });
18
+
19
+ it("PRINT_MARGINS has expected values", () => {
20
+ expect(PRINT_MARGINS).toEqual(["normal", "narrow", "none"]);
21
+ });
22
+
23
+ it("PRINT_BACKGROUND_MODES has expected values", () => {
24
+ expect(PRINT_BACKGROUND_MODES).toEqual(["preserve", "flatten"]);
25
+ });
26
+
27
+ it("PRINT_REGIONS is a non-empty array", () => {
28
+ expect(PRINT_REGIONS.length).toBeGreaterThan(0);
29
+ });
30
+ });
@@ -0,0 +1,31 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { sectionBackgroundSchema } from "../section-background.ts";
3
+
4
+ describe("sectionBackgroundSchema", () => {
5
+ it("accepts color kind", () => {
6
+ const result = sectionBackgroundSchema.safeParse({ kind: "color" });
7
+ expect(result.success).toBe(true);
8
+ });
9
+
10
+ it("accepts image kind with imageName", () => {
11
+ const result = sectionBackgroundSchema.safeParse({
12
+ kind: "image",
13
+ imageName: "hero-bg",
14
+ });
15
+ expect(result.success).toBe(true);
16
+ });
17
+
18
+ it("accepts transparent kind", () => {
19
+ const result = sectionBackgroundSchema.safeParse({ kind: "transparent" });
20
+ expect(result.success).toBe(true);
21
+ });
22
+
23
+ it("rejects invalid kind", () => {
24
+ expect(() => sectionBackgroundSchema.parse({ kind: "video" })).toThrow();
25
+ });
26
+
27
+ it("rejects image kind without imageName", () => {
28
+ const result = sectionBackgroundSchema.safeParse({ kind: "image" });
29
+ expect(result.success).toBe(false);
30
+ });
31
+ });
@@ -0,0 +1,16 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { sectionBodyKindSchema } from "../section-body.ts";
3
+
4
+ describe("sectionBodyKindSchema", () => {
5
+ it("accepts valid body kinds", () => {
6
+ const validKinds = ["list", "split-list", "stats", "cards", "paragraphs", "comparison", "rich"];
7
+ for (const kind of validKinds) {
8
+ const result = sectionBodyKindSchema.safeParse(kind);
9
+ expect(result.success, `kind "${kind}" should be valid`).toBe(true);
10
+ }
11
+ });
12
+
13
+ it("rejects invalid body kind", () => {
14
+ expect(() => sectionBodyKindSchema.parse("accordion")).toThrow();
15
+ });
16
+ });
@@ -0,0 +1,17 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { standardCardSchema } from "../section-cards.ts";
3
+
4
+ describe("standardCardSchema", () => {
5
+ it("accepts a valid card", () => {
6
+ const result = standardCardSchema.safeParse({
7
+ title: "Test Card",
8
+ description: "Card body text",
9
+ });
10
+ expect(result.success).toBe(true);
11
+ });
12
+
13
+ it("rejects empty object", () => {
14
+ const result = standardCardSchema.safeParse({});
15
+ expect(result.success).toBe(false);
16
+ });
17
+ });
@@ -0,0 +1,41 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ ctaVariantSchema,
4
+ ctaSizeSchema,
5
+ ctaIconPositionSchema,
6
+ } from "../section-cta.ts";
7
+
8
+ describe("ctaVariantSchema", () => {
9
+ it("accepts valid variants", () => {
10
+ for (const v of ["primary", "secondary", "ghost", "danger"]) {
11
+ expect(ctaVariantSchema.parse(v)).toBe(v);
12
+ }
13
+ });
14
+
15
+ it("rejects invalid variant", () => {
16
+ expect(() => ctaVariantSchema.parse("link")).toThrow();
17
+ });
18
+ });
19
+
20
+ describe("ctaSizeSchema", () => {
21
+ it("accepts valid sizes", () => {
22
+ for (const s of ["sm", "md", "lg"]) {
23
+ expect(ctaSizeSchema.parse(s)).toBe(s);
24
+ }
25
+ });
26
+
27
+ it("rejects invalid size", () => {
28
+ expect(() => ctaSizeSchema.parse("xl")).toThrow();
29
+ });
30
+ });
31
+
32
+ describe("ctaIconPositionSchema", () => {
33
+ it("accepts valid positions", () => {
34
+ expect(ctaIconPositionSchema.parse("leading")).toBe("leading");
35
+ expect(ctaIconPositionSchema.parse("trailing")).toBe("trailing");
36
+ });
37
+
38
+ it("rejects invalid position", () => {
39
+ expect(() => ctaIconPositionSchema.parse("top")).toThrow();
40
+ });
41
+ });
@@ -0,0 +1,31 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ headingToneSchema,
4
+ sectionHeaderLevelSchema,
5
+ } from "../section-header.ts";
6
+
7
+ describe("headingToneSchema", () => {
8
+ it("accepts valid tones", () => {
9
+ for (const t of ["default", "primary", "accent", "muted", "inverse"]) {
10
+ expect(headingToneSchema.parse(t)).toBe(t);
11
+ }
12
+ });
13
+
14
+ it("rejects invalid tone", () => {
15
+ expect(() => headingToneSchema.parse("bright")).toThrow();
16
+ });
17
+ });
18
+
19
+ describe("sectionHeaderLevelSchema", () => {
20
+ it("accepts level 1", () => {
21
+ expect(sectionHeaderLevelSchema.parse(1)).toBe(1);
22
+ });
23
+
24
+ it("accepts level 2", () => {
25
+ expect(sectionHeaderLevelSchema.parse(2)).toBe(2);
26
+ });
27
+
28
+ it("rejects level 3", () => {
29
+ expect(() => sectionHeaderLevelSchema.parse(3)).toThrow();
30
+ });
31
+ });
@@ -0,0 +1,24 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { sectionImageParallaxPropSchema } from "../section-image.ts";
3
+
4
+ describe("sectionImageParallaxPropSchema", () => {
5
+ it("accepts boolean", () => {
6
+ expect(sectionImageParallaxPropSchema.parse(true)).toBe(true);
7
+ expect(sectionImageParallaxPropSchema.parse(false)).toBe(false);
8
+ });
9
+
10
+ it("accepts number in range 0..2", () => {
11
+ expect(sectionImageParallaxPropSchema.parse(0)).toBe(0);
12
+ expect(sectionImageParallaxPropSchema.parse(1)).toBe(1);
13
+ expect(sectionImageParallaxPropSchema.parse(2)).toBe(2);
14
+ });
15
+
16
+ it("rejects number out of range", () => {
17
+ expect(() => sectionImageParallaxPropSchema.parse(3)).toThrow();
18
+ expect(() => sectionImageParallaxPropSchema.parse(-1)).toThrow();
19
+ });
20
+
21
+ it("rejects string", () => {
22
+ expect(() => sectionImageParallaxPropSchema.parse("subtle")).toThrow();
23
+ });
24
+ });
@@ -0,0 +1,29 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ revealVariantSchema,
4
+ parallaxVariantSchema,
5
+ } from "../section-motion.ts";
6
+
7
+ describe("revealVariantSchema", () => {
8
+ it("accepts valid variants", () => {
9
+ for (const v of ["fade", "fade-up", "fade-up-stagger"]) {
10
+ expect(revealVariantSchema.parse(v)).toBe(v);
11
+ }
12
+ });
13
+
14
+ it("rejects invalid variant", () => {
15
+ expect(() => revealVariantSchema.parse("slide")).toThrow();
16
+ });
17
+ });
18
+
19
+ describe("parallaxVariantSchema", () => {
20
+ it("accepts valid variants", () => {
21
+ for (const v of ["subtle", "balanced", "dramatic"]) {
22
+ expect(parallaxVariantSchema.parse(v)).toBe(v);
23
+ }
24
+ });
25
+
26
+ it("rejects invalid variant", () => {
27
+ expect(() => parallaxVariantSchema.parse("extreme")).toThrow();
28
+ });
29
+ });