branch:
normalize.test.ts
9258 bytesRaw
import { describe, it, expect } from "vitest";
import { normalizeCode } from "../normalize";
describe("normalizeCode", () => {
describe("empty / whitespace input", () => {
it("returns default async arrow for empty string", () => {
expect(normalizeCode("")).toBe("async () => {}");
});
it("returns default async arrow for whitespace-only string", () => {
expect(normalizeCode(" \n\t ")).toBe("async () => {}");
});
});
describe("arrow function passthrough", () => {
it("passes through a simple async arrow function", () => {
const code = "async () => { return 1; }";
expect(normalizeCode(code)).toBe(code);
});
it("passes through a sync arrow function", () => {
const code = "() => { return 1; }";
expect(normalizeCode(code)).toBe(code);
});
it("passes through an arrow with parameters", () => {
const code = "async (a, b) => { return a + b; }";
expect(normalizeCode(code)).toBe(code);
});
it("passes through a concise-body arrow function", () => {
const code = "() => 42";
expect(normalizeCode(code)).toBe(code);
});
it("passes through arrow with trailing semicolon", () => {
// Semicolon is part of the ExpressionStatement; inner expr is still ArrowFunctionExpression
const result = normalizeCode("async () => { return 1; };");
expect(result).toBe("async () => { return 1; };");
});
});
describe("trailing expression → return insertion", () => {
it("wraps a single expression and returns it", () => {
const result = normalizeCode("1 + 2");
expect(result).toBe("async () => {\nreturn (1 + 2)\n}");
});
it("keeps preceding statements and returns the last expression", () => {
const code = "const x = 10;\nx * 2";
const result = normalizeCode(code);
expect(result).toContain("const x = 10;");
expect(result).toContain("return (x * 2)");
expect(result).toMatch(/^async \(\) => \{/);
});
it("returns a function call expression", () => {
const result = normalizeCode('console.log("hello")');
expect(result).toContain('return (console.log("hello"))');
});
it("returns an await expression", () => {
const result = normalizeCode("await fetch('http://example.com')");
expect(result).toContain("return (await fetch('http://example.com'))");
});
});
describe("non-expression last statement → plain wrap", () => {
it("wraps a variable declaration", () => {
const code = "const x = 42";
const result = normalizeCode(code);
expect(result).toBe("async () => {\nconst x = 42\n}");
});
it("wraps a for loop", () => {
const code = "for (let i = 0; i < 10; i++) {}";
const result = normalizeCode(code);
expect(result).toBe(`async () => {\n${code}\n}`);
});
it("wraps an if statement", () => {
const code = "if (true) { doStuff(); }";
const result = normalizeCode(code);
expect(result).toBe(`async () => {\n${code}\n}`);
});
it("wraps multiple statements ending with a declaration", () => {
const code = "const a = 1;\nconst b = 2;";
const result = normalizeCode(code);
expect(result).toBe(`async () => {\n${code}\n}`);
});
});
describe("parse error fallback", () => {
it("wraps syntactically invalid code as-is", () => {
const code = "const = oops {{{";
const result = normalizeCode(code);
expect(result).toBe(`async () => {\n${code}\n}`);
});
});
describe("whitespace trimming", () => {
it("trims leading and trailing whitespace before processing", () => {
const result = normalizeCode(" () => 42 ");
expect(result).toBe("() => 42");
});
});
// ── LLM failure modes ──────────────────────────────────────────────
describe("markdown code fences", () => {
it("strips ```js fences", () => {
const code = "```js\nasync () => { return 1; }\n```";
expect(normalizeCode(code)).toBe("async () => { return 1; }");
});
it("strips ```javascript fences", () => {
const code = "```javascript\nconst x = 1;\nx + 2\n```";
const result = normalizeCode(code);
expect(result).toContain("return (x + 2)");
expect(result).not.toContain("```");
});
it("strips ```typescript fences", () => {
const code = "```typescript\n() => 42\n```";
expect(normalizeCode(code)).toBe("() => 42");
});
it("strips ```ts fences", () => {
const code = "```ts\n() => 42\n```";
expect(normalizeCode(code)).toBe("() => 42");
});
it("strips ```tsx fences", () => {
const code = "```tsx\n() => 42\n```";
expect(normalizeCode(code)).toBe("() => 42");
});
it("strips ```jsx fences", () => {
const code = "```jsx\n() => 42\n```";
expect(normalizeCode(code)).toBe("() => 42");
});
it("strips bare ``` fences with no language tag", () => {
const code = "```\nasync () => { return 1; }\n```";
expect(normalizeCode(code)).toBe("async () => { return 1; }");
});
it("handles fences with trailing whitespace", () => {
const code = "```js\nasync () => { return 1; }\n``` ";
expect(normalizeCode(code)).toBe("async () => { return 1; }");
});
it("does not strip fences if code is not fully wrapped", () => {
// Only one backtick fence, not a complete pair
const code = "```js\nasync () => { return 1; }";
const result = normalizeCode(code);
// Falls through to parse error → wrapped
expect(result).toContain("async () => {");
});
});
describe("export default unwrapping", () => {
it("unwraps export default arrow function", () => {
const code = "export default async () => { return 1; }";
expect(normalizeCode(code)).toBe("async () => { return 1; }");
});
it("unwraps export default sync arrow function", () => {
const code = "export default () => { return 1; }";
expect(normalizeCode(code)).toBe("() => { return 1; }");
});
it("unwraps export default expression and normalizes it", () => {
const code = "export default 42";
const result = normalizeCode(code);
expect(result).toContain("return (42)");
});
it("wraps anonymous export default function as IIFE", () => {
const code = "export default function() { return 42; }";
const result = normalizeCode(code);
expect(result).toBe(
"async () => {\nreturn (function() { return 42; })();\n}"
);
});
it("wraps anonymous export default class as expression", () => {
const code = "export default class { constructor() {} }";
const result = normalizeCode(code);
expect(result).toBe(
"async () => {\nreturn (class { constructor() {} });\n}"
);
});
});
describe("named function declaration → auto-call", () => {
it("wraps and calls a single named function", () => {
const code = "async function doStuff() { return 42; }";
const result = normalizeCode(code);
expect(result).toContain(code);
expect(result).toContain("return doStuff();");
expect(result).toMatch(/^async \(\) => \{/);
});
it("wraps and calls a sync named function", () => {
const code = "function compute() { return 1 + 2; }";
const result = normalizeCode(code);
expect(result).toContain(code);
expect(result).toContain("return compute();");
});
it("does not auto-call when there are multiple statements", () => {
// Multiple statements: function + a call → not a single FunctionDeclaration
const code = "function helper() { return 1; }\nhelper()";
const result = normalizeCode(code);
// Last statement is expression → return insertion
expect(result).toContain("return (helper())");
});
});
describe("IIFE passthrough", () => {
it("wraps IIFE with return (technically works)", () => {
const code = "(async () => { return 42; })()";
const result = normalizeCode(code);
// Last expression → gets returned — this is functional
expect(result).toContain("return ((async () => { return 42; })())");
});
});
describe("top-level return (parse error in module mode)", () => {
it("wraps top-level return — works by accident in catch branch", () => {
const code = "return 42";
const result = normalizeCode(code);
// Parse error → caught → wrapped → actually works at runtime
expect(result).toBe("async () => {\nreturn 42\n}");
});
});
describe("combined LLM quirks", () => {
it("handles code fences + export default + arrow", () => {
const code = "```js\nexport default async () => { return 1; }\n```";
expect(normalizeCode(code)).toBe("async () => { return 1; }");
});
it("handles code fences + named function", () => {
const code =
"```js\nasync function run() { return await codemode.searchWeb({ query: 'test' }); }\n```";
const result = normalizeCode(code);
expect(result).toContain("return run();");
expect(result).not.toContain("```");
});
});
});