test: add prose-fixture end-to-end smoke for kitchen view (task 0003)

Pull a reduced end-to-end smoke forward into the slice so the view flow
is exercised over a real on-disk recipe, not just a temp file.

Add fixtures/prose.md — a recipe using only this slice's modelled
constructs (frontmatter, headings, plain paragraphs) so it renders
losslessly. integration_test now runs `kitchen view fixtures/prose.md`
as a subprocess and asserts the frontmatter, separator, headings,
paragraphs, and flat document order end-to-end. smoke_test covers
`kitchen --help` exiting 0 with the view command listed.

The full fixtures/basic.md smoke remains task 0007, which needs 0004's
richer nodes before that fixture renders losslessly.
This commit is contained in:
2026-07-26 08:19:26 -04:00
parent d1c1f330c5
commit 97ecc86d63
4 changed files with 71 additions and 24 deletions

View File

@@ -68,4 +68,12 @@ They assert real observable behaviour against independent literals rather than r
### Review follow-up applied
The `runCli` and `stripAnsi` test helpers were extracted into `packages/bin/src/test-support.ts` to remove duplication the review flagged across the bin test files.
The scaffold's remaining `test.todo` placeholders (future block/inline types, annotations, the smoke test) are left intact for their owning tasks.
The scaffold's remaining `test.todo` placeholders (future block/inline types, annotations) are left intact for their owning tasks.
### End-to-end coverage pulled forward
A reduced end-to-end smoke was pulled forward so the vertical slice is validated the way a user runs it.
`fixtures/prose.md` is a committed recipe using only the constructs this slice models — frontmatter, headings at levels 13, and plain-text paragraphs — so it renders losslessly today.
`integration_test.ts` runs `kitchen view fixtures/prose.md` as a subprocess and asserts the ANSI-stripped output: exit 0, frontmatter YAML before the body, the separator, every heading and paragraph, and flat document order.
`smoke_test.ts` covers `kitchen --help` exiting 0 with the `view` command listed.
This is a subset of task 0007's smoke, which still owns the full `fixtures/basic.md` end-to-end once 0004's richer nodes make that fixture render losslessly.

22
fixtures/prose.md Normal file
View File

@@ -0,0 +1,22 @@
---
title: Buttered Toast
servings: 2
tags: [breakfast, simple]
---
# Buttered Toast
The simplest breakfast, and a good way to smoke-test the renderer.
Everything here is plain prose — no lists, code, or annotations yet.
## Method
Toast the bread until golden on both sides.
### Timing
Spread the butter while the toast is still warm so it soaks in.
## Serving
Cut into triangles and serve at once.

View File

@@ -1,33 +1,43 @@
import { describe, expect, test } from "bun:test";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { runCli, stripAnsi } from "./test-support.ts";
const CLI = `${import.meta.dir}/index.ts`;
const FIXTURE = join(import.meta.dir, "..", "..", "..", "fixtures", "prose.md");
describe("cli — integration", () => {
test("invokes core parser and produces output for a real fixture file", async () => {
const dir = mkdtempSync(join(tmpdir(), "kitchen-md-"));
const file = join(dir, "recipe.md");
writeFileSync(
file,
"---\ntitle: Test Recipe\nservings: 2\n---\n\n# Heading One\n\nA plain paragraph of prose.\n",
);
try {
const { exitCode, stdout } = await runCli(["view", file], CLI);
test("renders a real recipe fixture end-to-end", async () => {
const { exitCode, stdout } = await runCli(["view", FIXTURE], CLI);
const output = stripAnsi(stdout);
const lines = output.split("\n");
const lineOf = (needle: string): number => lines.findIndex((line) => line.includes(needle));
expect(exitCode).toBe(0);
expect(output).toContain("title: Test Recipe");
expect(output).toContain("title: Buttered Toast");
expect(output).toContain("servings: 2");
expect(output).toMatch(/─+/);
expect(output).toContain("Heading One");
expect(output).toContain("A plain paragraph of prose.");
} finally {
rmSync(dir, { recursive: true, force: true });
}
expect(output).toContain("tags:");
expect(output).toContain("breakfast");
expect(output).toContain("simple");
expect(lines.some((line) => /^─+$/.test(line))).toBe(true);
expect(output).toContain("Buttered Toast");
expect(output).toContain("Method");
expect(output).toContain("Timing");
expect(output).toContain("Serving");
expect(output).toContain("The simplest breakfast");
expect(output).toContain("Toast the bread until golden on both sides.");
expect(output).toContain("Spread the butter while the toast is still warm");
expect(output).toContain("Cut into triangles and serve at once.");
const separatorLine = lines.findIndex((line) => /^─+$/.test(line));
expect(lineOf("servings: 2")).toBeLessThan(separatorLine);
expect(separatorLine).toBeLessThan(lineOf("Method"));
expect(lineOf("Method")).toBeLessThan(lineOf("Timing"));
expect(lineOf("Timing")).toBeLessThan(lineOf("Serving"));
expect(lineOf("Method")).toBeLessThan(lineOf("Toast the bread until golden on both sides."));
});
});

View File

@@ -1,6 +1,13 @@
import { describe, test } from "bun:test";
import { describe, expect, test } from "bun:test";
import { runCli } from "./test-support.ts";
describe("smoke", () => {
test.todo("kitchen --help exits with code 0");
test("kitchen --help exits with code 0", async () => {
const { exitCode, stdout } = await runCli(["--help"], `${import.meta.dir}/index.ts`);
expect(exitCode).toBe(0);
expect(stdout).toMatch(/view/);
});
test.todo("kitchen parse <fixture> outputs structured JSON covering all annotation types");
});