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( "One to seven controls is the normal section size. Eight to ten controls are allowed only for one cohesive entity and require semanticGroup on every control; ten is the hard maximum.", ); expect(contract.aiUsageRules).toContain( "Every Control Section Inventory entry declares required entityId, entity, exact targets, and groupingReason. One entity with ten or fewer controls stays in one section regardless of control type, visual height, or target namespace.", ); expect(contract.aiUsageRules).toContain( "An entity above ten controls splits into balanced workflow sections of two to ten controls. Every split section keeps the same entityId and entity and declares a unique workflowStage plus splitReason.", ); expect(contract.aiUsageRules).toContain( "Every app-authored controls-panel body section must have a short meaningful visible title. Runtime-created Setup renders as the first visible headerless controls block with no title, reset action, collapse button, or collapsed state; sticky footer action sections use the technical title Export but render without a visible heading.", ); expect(contract.aiUsageRules).toContain( "Every visible app-authored controls-panel section title renders through the standard 36px collapsible header row with vertically centered text and the runtime collapse icon; 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. Runtime Setup is not collapsible; 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( "Runtime Setup uses 12px top spacing so its first control row has equal top, left, and right insets. Ordinary controls-panel body sections keep 8px top spacing. Both use 24px bottom spacing; sticky footer action sections keep their dedicated spacing.", ); expect(contract.aiUsageRules).toContain( "Broad section titles such as Flow, Icon, Shapes, Scene, Text, Typography, or Motion are only valid for small cohesive groups; use specific titles such as Flow Motion, Flow Geometry, Letter Burst, Shape Colors, Logo Glow, Logo Plate, or Text Block for larger groups.", ); 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"); }); });