109 lines
3.6 KiB
JavaScript
109 lines
3.6 KiB
JavaScript
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
|
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
|
|
import { buildMarkdown, formatDelta, formatKb, indexBaseline, readReport } from "./render-bundle-budget-comment.mjs";
|
|
|
|
let tmpDir;
|
|
beforeEach(() => {
|
|
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "bundle-budget-render-"));
|
|
});
|
|
afterEach(() => {
|
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
});
|
|
|
|
describe("formatKb", () => {
|
|
it("formats bytes as KB to 1 decimal", () => {
|
|
expect(formatKb(2048)).toBe("2.0 KB");
|
|
expect(formatKb(1536)).toBe("1.5 KB");
|
|
});
|
|
it("returns dash for non-numeric", () => {
|
|
expect(formatKb(undefined)).toBe("—");
|
|
});
|
|
});
|
|
|
|
describe("formatDelta", () => {
|
|
it("returns signed KB delta vs baseline", () => {
|
|
expect(formatDelta(3000, 1000)).toBe("+2.0 KB");
|
|
expect(formatDelta(1000, 3000)).toBe("-2.0 KB");
|
|
expect(formatDelta(1000, 1000)).toBe("+0.0 KB");
|
|
});
|
|
it("returns dash when no baseline", () => {
|
|
expect(formatDelta(1000, undefined)).toBe("—");
|
|
});
|
|
});
|
|
|
|
describe("readReport", () => {
|
|
it("returns null for missing path", () => {
|
|
expect(readReport(path.join(tmpDir, "missing.json"))).toBeNull();
|
|
});
|
|
it("returns null for empty file", () => {
|
|
const p = path.join(tmpDir, "empty.json");
|
|
fs.writeFileSync(p, "");
|
|
expect(readReport(p)).toBeNull();
|
|
});
|
|
it("parses valid json", () => {
|
|
const p = path.join(tmpDir, "ok.json");
|
|
fs.writeFileSync(p, JSON.stringify({ a: 1 }));
|
|
expect(readReport(p)).toEqual({ a: 1 });
|
|
});
|
|
});
|
|
|
|
describe("indexBaseline", () => {
|
|
it("indexes by key", () => {
|
|
const idx = indexBaseline({
|
|
entries: [
|
|
{ key: "auth/login", gzipSize: 1000 },
|
|
{ key: "vendor-react", gzipSize: 200 },
|
|
],
|
|
});
|
|
expect(idx.get("auth/login")?.gzipSize).toBe(1000);
|
|
expect(idx.get("vendor-react")?.gzipSize).toBe(200);
|
|
});
|
|
it("returns empty map for null", () => {
|
|
expect(indexBaseline(null).size).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("buildMarkdown", () => {
|
|
it("includes marker for sticky-comment upsert", () => {
|
|
const md = buildMarkdown({ skipped: true });
|
|
expect(md).toContain("<!-- bundle-budget-report -->");
|
|
});
|
|
it("renders summary + per-asset table sorted by severity", () => {
|
|
const current = {
|
|
skipped: false,
|
|
totals: { ok: 1, warning: 1, exceeds: 1, unbudgeted: 0 },
|
|
entries: [
|
|
{ key: "auth/login", kind: "route", gzipSize: 50 * 1024, status: "ok", limitKb: 100 },
|
|
{ key: "app/$tenant/crm", kind: "route", gzipSize: 270 * 1024, status: "exceeds", limitKb: 250 },
|
|
{ key: "vendor-react", kind: "vendor", gzipSize: 140 * 1024, status: "warning", limitKb: 150 },
|
|
],
|
|
};
|
|
const baseline = {
|
|
entries: [
|
|
{ key: "auth/login", gzipSize: 52 * 1024 },
|
|
{ key: "app/$tenant/crm", gzipSize: 260 * 1024 },
|
|
],
|
|
};
|
|
const md = buildMarkdown(current, baseline);
|
|
expect(md).toContain("exceeds=1");
|
|
expect(md).toContain("`app/$tenant/crm`");
|
|
expect(md).toContain("[EXCEED]");
|
|
// exceeds row appears before warning row
|
|
const exceedIdx = md.indexOf("[EXCEED]");
|
|
const warnIdx = md.indexOf("[WARN]");
|
|
expect(exceedIdx).toBeLessThan(warnIdx);
|
|
// delta is rendered for matched key (current 270KB vs baseline 260KB)
|
|
expect(md).toContain("+10.0 KB");
|
|
// unmatched vendor-react has dash delta
|
|
expect(md).toContain("| —");
|
|
});
|
|
it("handles a skipped current report gracefully", () => {
|
|
const md = buildMarkdown({ skipped: true });
|
|
expect(md).toContain("No bundle artifacts measured");
|
|
});
|
|
});
|