import { expect, test } from "bun:test"; import { readFileSync } from "node:fs"; import { join } from "node:path"; import { compile } from "../src/index.ts"; /** * `pattern=""` is not inert. An empty pattern compiles to a regex that matches * only the empty string, so EVERY typed value becomes invalid and the form * silently refuses to submit -- no error, no request. * * `@wrnexus/ui`'s input declares `pattern: string = ""` and renders * `pattern="{pattern}"`, so every input that did not opt into a pattern shipped * one that could never match. This broke sign-up in a real app. * * Attributes reach the output through two different emitters, and both matter: * a component's interpolated attribute is baked at render time via * `__wrnAttr`, while a page's static element is serialized by `renderAttr`. */ const uiInput = join(import.meta.dir, "..", "..", "ui", "components", "input.wrn"); function render(view: string): string { return compile(`page P {\n view { ${view} }\n}\n`, "P.wrn").code; } test("the real ui input never emits a bare pattern attribute", () => { const code = compile(readFileSync(uiInput, "utf8"), uiInput).code; // The broken shape: the attribute is always present, empty or not. expect(code).not.toContain('pattern="${__wrnAttr(pattern)}"'); // The fixed shape: the attribute itself is decided at render time. expect(code).toContain("__wrnOptionalAttr"); }); test("an interpolated constraint attribute on a component is emitted through the helper", () => { const source = `component Field {\n props { pattern: string = "" }\n view { }\n}\n`; const code = compile(source, "Field.wrn").code; expect(code).toContain("__wrnOptionalAttr"); expect(code).not.toContain('pattern="${__wrnAttr(pattern)}"'); }); test("a static empty constraint attribute on a page element is dropped", () => { const code = render(``); expect(code).not.toContain('pattern=""'); expect(code).not.toContain('minlength=""'); }); test("a populated constraint attribute survives on both paths", () => { expect(render(``)).toContain('pattern="[0-9]+"'); const component = compile( `component F {\n props { p: string = "x" }\n view { }\n}\n`, "F.wrn", ).code; expect(component).toContain("pattern"); }); test("attributes outside the constraint set keep their empty values", () => { // An empty value or class is meaningful and must survive untouched. const code = render(``); expect(code).toContain('value=""'); });