import { describe, expect, it } from "vitest"; import { getToolcraftComponentContract } from "./component-contracts"; describe("Toolcraft template component contracts: runtime and composition", () => { it("documents concise control label rules", () => { const contract = getToolcraftComponentContract("controlLabels"); expect(contract.kind).toBe("composition"); expect(contract.visualComponent).toBe("ControlFieldLabel"); expect(contract.aiUsageRules).toContain( "Control labels must be short UI names, usually one to three words.", ); expect(contract.aiUsageRules).toContain( "Do not put explanations, formulas, units, parenthetical hints, or usage instructions in control labels.", ); expect(contract.aiUsageRules).toContain( "A concise property label such as Speed, Color, Size, or Opacity is allowed when the nearest visible section or group clearly names the affected product entity.", ); expect(contract.aiUsageRules).toContain( "When the section is generic, mixed, missing, or otherwise weak context, include the affected entity or role in the label: Pattern color, Background opacity, Wave speed, Stroke width.", ); expect(contract.aiUsageRules).toContain( "Acceptance validators suggest semantic replacement labels for weak generic labels; fix the schema label instead of relying on runtime fallback rewriting.", ); expect(contract.aiUsageRules).toContain( "Ten declared controls is a non-blocking density-review threshold, not a section limit. Inspect simultaneously visible controls in reachable modes, compound-editor complexity, panel height, navigation, and reset scope; declaration count alone does not prove visual density.", ); expect(contract.aiUsageRules).toContain( "Every Control Section Inventory entry declares required entityId, entity, exact targets, and groupingReason. Group by user task and dependency cohesion, not by component type or target namespace. Recommend semanticGroup where helpful; mixed plain-color rows retain their mandatory grouping.", ); expect(contract.aiUsageRules).toContain( "A coherent section may exceed ten controls, and smaller entities may have justified workflow stages. Every split section keeps the same entityId and entity and declares a unique workflowStage plus concrete splitReason. A complete task may have one control; never split or merge solely to satisfy a count.", ); expect(contract.aiUsageRules).toContain( "Every app-authored controls-panel body section must have a short meaningful visible title. Runtime-created Settings uses the standard visible title, scoped reset action and collapse controls, and remains present before product sections in every mode. Local Save State as Default lives in its own headerless block above Settings; sticky footer action sections use the technical title Export but render without a visible heading.", ); expect(contract.aiUsageRules).toContain( "Controls-panel section titles normally use one to three words and name only the edited product entity or workflow stage. Four words is the exceptional maximum; starter acceptance rejects more than four semantic words or 32 Unicode code points.", ); expect(contract.aiUsageRules).toContain( "Use section.description only when the section scope or output relationship is not obvious from its concise title and visible controls. The runtime renders it only behind the standard filled question-mark help icon and never as a visible descriptor or subtitle.", ); expect(contract.aiUsageRules).toContain( "An actually overflowing legacy or localized section title stays on one line, fades at the right edge, and exposes its full text on hover. This defensive fallback never authorizes a generated app to keep an overlong title.", ); expect(contract.aiUsageRules).toContain( "Every visible app-authored controls-panel section title renders through the standard 36px collapsible header row with vertically centered, left-aligned text and a separate rightmost 24px design-system collapse icon button; title, help, reset and collapse controls are never nested, and generated apps must not hand-build section headers.", ); expect(contract.aiUsageRules).toContain( "Controls-panel section expand and collapse uses the standard runtime height/opacity animation; generated apps must not replace it with instant custom section visibility.", ); expect(contract.aiUsageRules).toContain( "Controls-panel section collapsed/expanded state persists as a runtime UI preference per app. It is not undo/redo state, not settings import/export state, and Reset controls must not clear it. Settings follows ordinary section collapse behavior; the separate defaults action block and sticky footer Export sections are not collapsible.", ); expect(contract.aiUsageRules).toContain( "Ordinary controls-panel section headers expose the runtime section reset action before the collapse button; it dispatches controls.resetTargets and restores only that section's control targets to their schema defaultValue.", ); expect(contract.aiUsageRules).toContain( "The local defaults action block uses public technical spacing with 12px on all sides. Settings uses the standard section header and ordinary body spacing, with 8px top and 24px bottom insets; sticky footer action sections keep their dedicated spacing.", ); expect(contract.aiUsageRules).toContain( "Section titles must identify the edited entity or workflow stage. Use more specific names when the scope is unclear, not merely because the section has many controls.", ); expect(contract.aiUsageRules).toContain( "Section titles in one controls panel must be unique.", ); expect(contract.aiUsageRules).toContain( "Bad: Grid Density (every Nth). Good: Grid Density, with Every 6th as the select option label.", ); expect(contract.aiUsageRules).toContain( "Use control.description for the short help tooltip shown beside visible labels. It must describe the product behavior or output affected by the control, not restate the label.", ); expect(contract.aiUsageRules).toContain( "Do not write label-recap descriptions such as Adjusts Opacity, Controls Speed, or Sets Background.", ); expect(contract.aiUsageRules).toContain( "If there is no useful product-specific explanation, omit control.description; the runtime should not show a help tooltip for that label.", ); expect(contract.aiUsageRules).toContain( "Do not add control.description to sequential colors such as Color 1, Color 2, or simple palette controls such as Spread when the section title already names the color or palette context.", ); expect(contract.aiUsageRules).toContain( "For compound controls such as FontPicker, do not use control.description to enumerate the control's owned fields. FontPicker descriptions must not recap font family, weight, size, case, color, opacity, letter spacing, or line height; use description only for non-obvious product scope or omit it.", ); expect(contract.aiUsageRules).toContain( "The runtime renders a filled Phosphor question icon beside each visible ControlFieldLabel; generated apps must not hand-build their own help icon beside built-in labels.", ); expect(contract.capabilities).toContain("control-description-tooltip"); expect(contract.capabilities).toContain("section-description-tooltip"); expect(contract.capabilities).toContain("section-title-overflow-fade"); }); });