165 lines
5.5 KiB
TypeScript
165 lines
5.5 KiB
TypeScript
import { mkdtempSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { dirname, join, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { describe, expect, it } from "@suaveplan/testing/runner";
|
|
import { parseCliArgs, runCli } from "./cli.js";
|
|
|
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
const FIXTURE = resolve(HERE, "../../tests/fixtures/tiny-plugin/src/index.ts");
|
|
|
|
describe("parseCliArgs", () => {
|
|
it("parses entry + outDir", { timeout: 5000 }, () => {
|
|
const a = parseCliArgs(["./entry.ts", "--out-dir", "/tmp/out"]);
|
|
expect(a.entry).toBe("./entry.ts");
|
|
expect(a.outDir).toBe("/tmp/out");
|
|
expect(a.optimizeFor).toBe("size");
|
|
expect(a.sourcemaps).toBe(false);
|
|
expect(a.help).toBe(false);
|
|
});
|
|
|
|
it("parses --optimize-for and --sourcemaps", { timeout: 5000 }, () => {
|
|
const a = parseCliArgs([
|
|
"./entry.ts",
|
|
"--out-dir",
|
|
"/tmp/out",
|
|
"--optimize-for",
|
|
"speed",
|
|
"--sourcemaps",
|
|
]);
|
|
expect(a.optimizeFor).toBe("speed");
|
|
expect(a.sourcemaps).toBe(true);
|
|
});
|
|
|
|
it("raises on invalid optimize-for", { timeout: 5000 }, () => {
|
|
expect(() =>
|
|
parseCliArgs(["x.ts", "--out-dir", "out", "--optimize-for", "tiny"])
|
|
).toThrow(/optimize-for/);
|
|
});
|
|
|
|
it("raises on missing entry", { timeout: 5000 }, () => {
|
|
expect(() => parseCliArgs(["--out-dir", "out"])).toThrow(/entry/);
|
|
});
|
|
|
|
it("raises on missing out-dir", { timeout: 5000 }, () => {
|
|
expect(() => parseCliArgs(["entry.ts"])).toThrow(/out-dir/);
|
|
});
|
|
|
|
it("raises on unknown flag and extra positional", { timeout: 5000 }, () => {
|
|
expect(() =>
|
|
parseCliArgs(["x.ts", "--out-dir", "o", "--bogus"])
|
|
).toThrow(/unknown flag/);
|
|
expect(() => parseCliArgs(["x.ts", "y.ts", "--out-dir", "o"])).toThrow(
|
|
/positional/
|
|
);
|
|
});
|
|
|
|
it("raises when --out-dir is at end without value", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
expect(() => parseCliArgs(["x.ts", "--out-dir"])).toThrow(
|
|
/requires a value/
|
|
);
|
|
});
|
|
|
|
it("honours -h and --help", { timeout: 5000 }, () => {
|
|
expect(parseCliArgs(["-h"]).help).toBe(true);
|
|
expect(parseCliArgs(["--help"]).help).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("runCli", () => {
|
|
it("compiles a fixture and prints a one-line JSON result", {
|
|
timeout: 120000,
|
|
}, async () => {
|
|
const out = mkdtempSync(join(tmpdir(), "cli-ok-"));
|
|
const stdoutLines: string[] = [];
|
|
const stderrLines: string[] = [];
|
|
let exitCode = -1;
|
|
await runCli([FIXTURE, "--out-dir", out], {
|
|
stdout: (l) => stdoutLines.push(l),
|
|
stderr: (l) => stderrLines.push(l),
|
|
exit: (c) => {
|
|
exitCode = c;
|
|
},
|
|
});
|
|
expect(exitCode).toBe(0);
|
|
const parsed = JSON.parse(stdoutLines[0] ?? "{}");
|
|
expect(parsed.wasmPath.endsWith(".wasm")).toBe(true);
|
|
expect(parsed.metadataPath.endsWith(".wasm.json")).toBe(true);
|
|
expect(parsed.routes).toBeGreaterThan(0);
|
|
rmSync(out, { recursive: true, force: true });
|
|
});
|
|
|
|
it("emits structured error JSON on bad args (exit 2)", {
|
|
timeout: 5000,
|
|
}, async () => {
|
|
const stderrLines: string[] = [];
|
|
let exitCode = -1;
|
|
await runCli([], {
|
|
stdout: () => undefined,
|
|
stderr: (l) => stderrLines.push(l),
|
|
exit: (c) => {
|
|
exitCode = c;
|
|
},
|
|
});
|
|
expect(exitCode).toBe(2);
|
|
const env = JSON.parse(stderrLines[0] ?? "{}");
|
|
expect(env.error).toBe("INVALID_ARGS");
|
|
});
|
|
|
|
it("exits 0 on --help", { timeout: 5000 }, async () => {
|
|
const stdoutLines: string[] = [];
|
|
let exitCode = -1;
|
|
await runCli(["--help"], {
|
|
stdout: (l) => stdoutLines.push(l),
|
|
stderr: () => undefined,
|
|
exit: (c) => {
|
|
exitCode = c;
|
|
},
|
|
});
|
|
expect(exitCode).toBe(0);
|
|
expect(
|
|
stdoutLines[0]?.includes("Usage") ||
|
|
stdoutLines[0]?.includes("suaveplan-wasm-compile")
|
|
).toBe(true);
|
|
});
|
|
|
|
it("exits 1 on a compile failure (missing entry file)", {
|
|
timeout: 30000,
|
|
}, async () => {
|
|
const stderrLines: string[] = [];
|
|
let exitCode = -1;
|
|
await runCli(["/no/such/file.ts", "--out-dir", "/tmp/x"], {
|
|
stdout: () => undefined,
|
|
stderr: (l) => stderrLines.push(l),
|
|
exit: (c) => {
|
|
exitCode = c;
|
|
},
|
|
});
|
|
expect(exitCode).toBe(1);
|
|
const env = JSON.parse(stderrLines[0] ?? "{}");
|
|
expect(typeof env.code).toBe("string");
|
|
});
|
|
|
|
it("wraps non-WasmCompilerError as WASM_COMPILER.UNCAUGHT", {
|
|
timeout: 5000,
|
|
}, async () => {
|
|
const stderrLines: string[] = [];
|
|
let exitCode = -1;
|
|
await runCli([FIXTURE, "--out-dir", "/tmp/cli-uncaught"], {
|
|
stdout: () => undefined,
|
|
stderr: (l) => stderrLines.push(l),
|
|
exit: (c) => {
|
|
exitCode = c;
|
|
},
|
|
compile: async () => {
|
|
throw new Error("synthetic non-wasm-compiler error");
|
|
},
|
|
});
|
|
expect(exitCode).toBe(1);
|
|
const env = JSON.parse(stderrLines[0] ?? "{}");
|
|
expect(env.code).toBe("WASM_COMPILER.UNCAUGHT");
|
|
});
|
|
});
|