chore(template): clean-slate template snapshot from bb4a0c7
Curated, product-agnostic snapshot of the post-story-04 tree: demo content deleted, auth-only reference feature, web-next shell, all gates green. Product-specific docs, ADRs 027-029, PRDs/epics/archive, editor library traces, and product naming are curated out; generic template repairs (coverage provider devDeps, root test:coverage script, live lint fixes, root-only release-please) are kept. See TEMPLATE.md for provenance, curation list, and usage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
This commit is contained in:
190
scripts/work/prd-ship.test.mjs
Normal file
190
scripts/work/prd-ship.test.mjs
Normal file
@@ -0,0 +1,190 @@
|
||||
import { test, describe } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import os from "node:os";
|
||||
import {
|
||||
parseFrontmatter,
|
||||
serializeFrontmatter,
|
||||
flipPrdStatus,
|
||||
findPrdPath,
|
||||
} from "./prd-ship.mjs";
|
||||
|
||||
describe("parseFrontmatter", () => {
|
||||
test("parses scalar keys from a basic frontmatter block", () => {
|
||||
const text = `---
|
||||
id: test-1
|
||||
title: Hello
|
||||
status: approved
|
||||
---
|
||||
|
||||
body
|
||||
`;
|
||||
const { frontmatter, body } = parseFrontmatter(text);
|
||||
assert.equal(frontmatter.id, "test-1");
|
||||
assert.equal(frontmatter.title, "Hello");
|
||||
assert.equal(frontmatter.status, "approved");
|
||||
assert.match(body, /body/);
|
||||
});
|
||||
|
||||
test("parses YAML list values (indented '- item')", () => {
|
||||
const text = `---
|
||||
id: test-2
|
||||
shipping-commits:
|
||||
- abc123
|
||||
- def456
|
||||
---
|
||||
|
||||
body
|
||||
`;
|
||||
const { frontmatter } = parseFrontmatter(text);
|
||||
assert.deepEqual(frontmatter["shipping-commits"], ["abc123", "def456"]);
|
||||
});
|
||||
|
||||
test("returns empty frontmatter when none present", () => {
|
||||
const { frontmatter, body } = parseFrontmatter("just body content\n");
|
||||
assert.deepEqual(frontmatter, {});
|
||||
assert.equal(body, "just body content\n");
|
||||
});
|
||||
});
|
||||
|
||||
describe("flipPrdStatus", () => {
|
||||
const baseText = `---
|
||||
id: test-prd
|
||||
title: Test PRD
|
||||
type: prd
|
||||
status: approved
|
||||
created: 2026-01-01
|
||||
---
|
||||
|
||||
# body
|
||||
`;
|
||||
|
||||
test("flips approved -> shipped with today's date", () => {
|
||||
const result = flipPrdStatus(baseText, { shippedDate: "2026-05-13" });
|
||||
const { frontmatter } = parseFrontmatter(result);
|
||||
assert.equal(frontmatter.status, "shipped");
|
||||
assert.equal(frontmatter.shipped, "2026-05-13");
|
||||
});
|
||||
|
||||
test("flips in-review -> shipped", () => {
|
||||
const text = baseText.replace("status: approved", "status: in-review");
|
||||
const result = flipPrdStatus(text, { shippedDate: "2026-05-13" });
|
||||
const { frontmatter } = parseFrontmatter(result);
|
||||
assert.equal(frontmatter.status, "shipped");
|
||||
});
|
||||
|
||||
test("preserves body", () => {
|
||||
const result = flipPrdStatus(baseText, { shippedDate: "2026-05-13" });
|
||||
assert.match(result, /\n# body\n/);
|
||||
});
|
||||
|
||||
test("preserves other frontmatter keys", () => {
|
||||
const result = flipPrdStatus(baseText, { shippedDate: "2026-05-13" });
|
||||
const { frontmatter } = parseFrontmatter(result);
|
||||
assert.equal(frontmatter.id, "test-prd");
|
||||
assert.equal(frontmatter.title, "Test PRD");
|
||||
assert.equal(frontmatter.created, "2026-01-01");
|
||||
});
|
||||
|
||||
test("adds shipping-commits when supplied", () => {
|
||||
const result = flipPrdStatus(baseText, {
|
||||
shippedDate: "2026-05-13",
|
||||
commits: ["abc123", "def456"],
|
||||
});
|
||||
const { frontmatter } = parseFrontmatter(result);
|
||||
assert.deepEqual(frontmatter["shipping-commits"], ["abc123", "def456"]);
|
||||
});
|
||||
|
||||
test("refuses to flip draft (must go through human review)", () => {
|
||||
const text = baseText.replace("status: approved", "status: draft");
|
||||
assert.throws(() => flipPrdStatus(text), /still draft/);
|
||||
});
|
||||
|
||||
test("refuses to flip already-shipped (idempotent fail-soft)", () => {
|
||||
const text = baseText.replace("status: approved", "status: shipped");
|
||||
assert.throws(() => flipPrdStatus(text), /already marked shipped/);
|
||||
});
|
||||
|
||||
test("refuses unexpected statuses", () => {
|
||||
const text = baseText.replace("status: approved", "status: cancelled");
|
||||
assert.throws(() => flipPrdStatus(text), /Unexpected PRD status/);
|
||||
});
|
||||
|
||||
test("refuses files without frontmatter", () => {
|
||||
assert.throws(
|
||||
() => flipPrdStatus("no frontmatter here"),
|
||||
/no parseable frontmatter/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("findPrdPath", () => {
|
||||
test("finds a PRD by its id field", () => {
|
||||
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "prd-find-"));
|
||||
try {
|
||||
const prdsDir = path.join(tmpRoot, "prds");
|
||||
fs.mkdirSync(prdsDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(prdsDir, "2026-05-13-something.prd.md"),
|
||||
`---
|
||||
id: 2026-05-13-something
|
||||
status: approved
|
||||
---
|
||||
body
|
||||
`,
|
||||
);
|
||||
const found = findPrdPath(tmpRoot, "2026-05-13-something");
|
||||
assert.ok(found);
|
||||
assert.match(found, /something\.prd\.md$/);
|
||||
} finally {
|
||||
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("returns null when no PRD matches", () => {
|
||||
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "prd-find2-"));
|
||||
try {
|
||||
fs.mkdirSync(path.join(tmpRoot, "prds"), { recursive: true });
|
||||
const found = findPrdPath(tmpRoot, "nonexistent");
|
||||
assert.equal(found, null);
|
||||
} finally {
|
||||
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("serializeFrontmatter", () => {
|
||||
test("preserves key order from the original", () => {
|
||||
const orig = `id: x
|
||||
title: y
|
||||
status: approved`;
|
||||
const result = serializeFrontmatter(orig, {
|
||||
id: "x",
|
||||
title: "y",
|
||||
status: "shipped",
|
||||
});
|
||||
const lines = result.split("\n");
|
||||
assert.equal(lines[0], "id: x");
|
||||
assert.equal(lines[1], "title: y");
|
||||
assert.equal(lines[2], "status: shipped");
|
||||
});
|
||||
|
||||
test("appends new keys at the end", () => {
|
||||
const orig = `id: x
|
||||
status: approved`;
|
||||
const result = serializeFrontmatter(orig, {
|
||||
id: "x",
|
||||
status: "shipped",
|
||||
shipped: "2026-05-13",
|
||||
});
|
||||
assert.match(result, /shipped: 2026-05-13\n?$/);
|
||||
});
|
||||
|
||||
test("emits array values as YAML lists", () => {
|
||||
const result = serializeFrontmatter("", {
|
||||
"shipping-commits": ["abc", "def"],
|
||||
});
|
||||
assert.match(result, /shipping-commits:\n {2}- abc\n {2}- def/);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user