51 lines
2.2 KiB
TypeScript
51 lines
2.2 KiB
TypeScript
import { readdirSync, readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import { parseMarkdown, roundTripMarkdown } from "@/components/MemoEditor/Editor/markdownCodec";
|
|
|
|
const CORPUS_DIR = join(__dirname, "fixtures", "markdown-corpus");
|
|
|
|
function fixtures(group: string): Array<[name: string, source: string]> {
|
|
const dir = join(CORPUS_DIR, group);
|
|
return readdirSync(dir)
|
|
.filter((f) => f.endsWith(".md"))
|
|
.sort()
|
|
.map((f) => [f, readFileSync(join(dir, f), "utf8")]);
|
|
}
|
|
|
|
// Fidelity contract part 1: supported constructs round-trip semantically.
|
|
// "Semantic" = the parsed document tree is identical before and after a
|
|
// serialize cycle; marker style (e.g. `*` vs `-` bullets) may normalize.
|
|
describe("markdown round-trip corpus: supported syntax (semantic equality)", () => {
|
|
for (const [name, source] of fixtures("supported")) {
|
|
it(`${name}: parse → serialize → parse is identity`, () => {
|
|
const serialized = roundTripMarkdown(source);
|
|
expect(parseMarkdown(serialized)).toEqual(parseMarkdown(source));
|
|
});
|
|
|
|
it(`${name}: serialization is idempotent`, () => {
|
|
const once = roundTripMarkdown(source);
|
|
expect(roundTripMarkdown(once)).toBe(once);
|
|
});
|
|
}
|
|
});
|
|
|
|
// Fidelity contract part 2: unmodeled constructs round-trip byte-for-byte.
|
|
// Fixtures in preserved/ contain only preserved constructs + inert prose,
|
|
// so the entire file must survive a round trip unchanged (modulo outer trim).
|
|
// One known whitespace normalization: a block construct separated from the
|
|
// next block by a single \n gains a blank line (\n\n) on the first round trip
|
|
// — the Document serializer joins sibling blocks with \n\n. It is a one-time
|
|
// normalization; the idempotence tests below pin the output as stable.
|
|
describe("markdown round-trip corpus: preserved syntax (byte equality)", () => {
|
|
for (const [name, source] of fixtures("preserved")) {
|
|
it(`${name}: round-trips byte-for-byte`, () => {
|
|
expect(roundTripMarkdown(source).trim()).toBe(source.trim());
|
|
});
|
|
|
|
it(`${name}: serialization is idempotent`, () => {
|
|
const once = roundTripMarkdown(source);
|
|
expect(roundTripMarkdown(once)).toBe(once);
|
|
});
|
|
}
|
|
});
|