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=""');
});