Files
WRNexusJS/packages/syntax/test/v060.test.ts
T
Clintchiz f68f79786f
Quality / quality (ubuntu-latest) (push) Failing after 9m46s
Quality / quality (windows-latest) (push) Canceled after 0s
fix: compile typed catches and reject event loop syntax
2026-08-12 19:36:18 +05:30

138 lines
4.5 KiB
TypeScript

import { expect, test } from "bun:test";
import { diagnose, eraseFunctionTypes, parse } from "../src/index.ts";
const source = `import type { PublicUser } from "@/types/user.ts"
import PublicLayout from "@/layouts/PublicLayout.wrn"
component ProfileCard {
props {
user: PublicUser
title?: string
open: boolean = false
size: "small" | "medium" | "large" = "medium"
}
state query: string = ""
state {
selected: PublicUser | null = null
loading: boolean = false
}
client state { menuOpen: boolean = false }
server state { sessionId: string | null = null }
computed ready: boolean = selected !== null
computed { label: string = title || user.name }
outputs {
confirm(payload: PublicUser)
cancel()
}
functions {
client async function confirmed(payload: PublicUser): Promise<void> {
const result = await server.confirmed(payload)
output.confirm(result)
}
server async function confirmed(payload: PublicUser): Promise<PublicUser> {
return payload
}
shared function normalize(value: string): string {
return value.trim()
}
}
view { <button>{label}</button> }
}`;
test("parses the WRNexusJS 0.6 language surface", () => {
const ast = parse(source);
expect(ast.structuredImports).toHaveLength(2);
expect(ast.props.map((prop) => [prop.name, prop.required])).toEqual([
["user", true],
["title", false],
["open", false],
["size", false],
]);
expect(ast.states.map((state) => `${state.runtime}:${state.name}`)).toEqual([
"shared:query",
"shared:selected",
"shared:loading",
"client:menuOpen",
"server:sessionId",
]);
expect(ast.outputs.map((output) => output.name)).toEqual(["confirm", "cancel"]);
expect(ast.runtimeFunctions.map((fn) => `${fn.runtime}:${fn.name}`)).toEqual([
"client:confirmed",
"server:confirmed",
"shared:normalize",
]);
});
test("supports global and page store roots", () => {
const globalStore = parse(`global store UserStore {
state { user: string | null = null }
computed { authenticated: boolean = user !== null }
persist { storage = "local" include = ["user"] version = 1 }
lifecycle { serverInit {} clientInit {} hydrate {} dispose {} }
functions { client function clear(): void { user = null } }
}`);
const pageStore = parse(`page store SearchStore { state { query: string = "" } }`);
expect(globalStore.kind).toBe("global-store");
expect(globalStore.persist?.storage).toBe("local");
expect(globalStore.storeLifecycle).toEqual(
expect.objectContaining({ serverInit: "", dispose: "" }),
);
expect(pageStore.kind).toBe("page-store");
});
test("rejects duplicate implementations in the same runtime", () => {
const diagnostics = diagnose(`component Invalid {
functions {
client function save(): void {}
client function save(): void {}
}
view { <div></div> }
}`);
expect(diagnostics.some((diagnostic) => diagnostic.code === "WRN-FUNCTION-DUPLICATE")).toBe(true);
});
test("readonly prop diagnostics ignore comparisons, strings, and shadowing parameters", () => {
const diagnostics = diagnose(`component Valid {
props { name: string = "field" mode: string = "exact" items: any[] = [] }
functions {
client function inspect(name, localItems) {
if (mode === "exact") localItems = items.filter((item) => item.name === name)
return document.querySelector("input[name='" + name + "']")
}
}
view { <div></div> }
}`);
expect(diagnostics.some((diagnostic) => diagnostic.code === "WRN-PROP-READONLY")).toBe(false);
});
test("readonly prop diagnostics still reject direct prop assignments", () => {
const diagnostics = diagnose(`component Invalid {
props { open: boolean = false }
functions { client function mutate() { open = true } }
view { <div></div> }
}`);
expect(diagnostics.some((diagnostic) => diagnostic.code === "WRN-PROP-READONLY")).toBe(true);
});
test("browser type erasure removes typed catch bindings", () => {
const source = `async function load(): Promise<void> {
try { await fetch("/api") } catch (error: any) { console.error(error) }
}`;
expect(eraseFunctionTypes(source)).toContain("catch (error)");
expect(eraseFunctionTypes(source)).not.toContain(": any");
});
test("diagnoses event syntax mistakenly used for loop directives", () => {
const diagnostics = diagnose(`component Rows {
view { <ul><li @for="row in rows" @key="row.id">{row.name}</li></ul> }
}`);
expect(diagnostics.filter((entry) => entry.code === "WRN-TEMPLATE-LOOP-DIRECTIVE")).toHaveLength(
2,
);
});