import { expect, test } from "bun:test";
import { parse } from "../src/index.ts";
const page = (inner: string) => `page Repro {
client {
${inner}
}
view { x }
}
`;
test("parses a sectioned api block into request, response and error", () => {
const ast = parse(
page(` api searchUsers POST /api/users {
request {
body {
name?: string
age?: number
}
}
response {
return data.users
}
error {
return []
}
}`),
);
const block = ast.dataApis[0]!;
expect(block.name).toBe("searchUsers");
expect(block.method).toBe("POST");
expect(block.path).toBe("/api/users");
expect(block.sections?.body).toEqual([
{ name: "name", optional: true, type: "string" },
{ name: "age", optional: true, type: "number" },
]);
expect(block.sections?.response.trim()).toBe("return data.users");
expect(block.sections?.error.trim()).toBe("return []");
});
test("a bare body still parses as the legacy response body", () => {
const ast = parse(
page(` api legacyUsers GET /api/users {
return users.length
}`),
);
const block = ast.dataApis[0]!;
expect(block.sections).toBeUndefined();
expect(block.body.trim()).toBe("return users.length");
});
test("GET parameters are parsed as required when not marked optional", () => {
const ast = parse(
page(` api listUsers GET /api/users {
request {
parameters {
team: string
}
}
response {
return data.users
}
}`),
);
expect(ast.dataApis[0]!.sections?.parameters).toEqual([
{ name: "team", optional: false, type: "string" },
]);
});
test("request inside an ssr block is rejected with a message naming the restriction", () => {
const source = `page Repro {
ssr {
api ssrUsers GET /api/users {
request {
parameters {
team: string
}
}
response {
return data.users
}
}
}
view { x }
}
`;
expect(() => parse(source)).toThrow(/ssr[\s\S]*request/i);
});