Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ Pull request requirements:

After submission, CI runs the automated checks. Maintainers apply a type label, assign the pull request's owner, and review the scope, implementation, and verification evidence; priority and Project status stay on the issue. Contributors should address review feedback or explain unresolved trade-offs. Maintainers squash merge accepted pull requests using the pull request title as the commit title on `main`. The pull request body becomes that commit's body and is the permanent record of the change; intermediate commits do not survive the merge. That is why the pull request body carries verification evidence and intermediate commit messages do not.

Verify changes locally before merging. Use `pnpm check:frontend` for frontend-only work, `pnpm check:backend` for Rust/Tauri-only work, and `pnpm check` for cross-cutting updates. For manual testing of Markdown and the article navigator, open the committed `corpus/` directory or one of its focused scenario directories in the app.
Verify changes locally before merging. Use `pnpm check:frontend` for frontend-only work, `pnpm check:backend` for Rust/Tauri-only work, and `pnpm check` for cross-cutting updates. For manual testing of Markdown and the article navigator, open the committed `corpus/` directory or one of its focused scenario directories in the app. Markdown round-trip convergence over `corpus/commonmark/`, `corpus/gfm/`, and `corpus/isolated/end-of-file/` is asserted automatically by `src/features/editor/tests/corpusRoundTrip.test.tsx`, so the manual pass over those directories covers rendering, interaction, and navigator behavior; the remaining subtrees are verified only by hand.

Frontend checks enforce a coverage floor. It is a ratchet set just below the measured numbers rather than a target: a change that falls below it needs tests, not a lower floor, and the floor is raised when the measured numbers move up.

Expand Down
51 changes: 51 additions & 0 deletions src/features/editor/tests/corpusRoundTrip.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { beforeEach, describe, expect, it } from "vitest";

import { createMarkdownReferenceContext } from "@/test/factories/editor";
import { setupMilkdownEditorMount } from "@/test/utils/milkdown";
import { mockTauriApiCommand } from "@/test/utils/tauriApi";

const mountEditor = setupMilkdownEditorMount(createMarkdownReferenceContext());

const corpusFiles = [
"commonmark/blocks.md",
"commonmark/code.md",
"commonmark/emphasis.md",
"commonmark/html.md",
"commonmark/links-and-images.md",
"commonmark/lists-and-blockquotes.md",
"commonmark/text-and-breaks.md",
"gfm/autolinks.md",
"gfm/strikethrough.md",
"gfm/tables.md",
"gfm/tagfilter.md",
"gfm/task-lists.md",
"isolated/end-of-file/incomplete-html-comment.md",
"isolated/end-of-file/unclosed-code-fence.md",
"isolated/end-of-file/unclosed-directive.md",
"isolated/end-of-file/unclosed-html-block.md",
];

const readCorpusFile = (relativePath: string) =>
readFileSync(resolve(process.cwd(), "corpus", relativePath), "utf8");

// The editor is allowed to normalize on first open, so the baseline is the first
// serialization rather than the corpus file.
describe("Corpus round trip", () => {
beforeEach(() => {
mockTauriApiCommand("resolveMarkdownImageTarget", ({ target }) => ({
kind: "renderable",
path: `C:/Notes/${target}`,
}));
});

it.each(corpusFiles)("converges on a stable serialization for %s", async (relativePath) => {
const source = readCorpusFile(relativePath);

const first = (await mountEditor(source)).getMarkdown();
const second = (await mountEditor(first)).getMarkdown();

expect(second).toBe(first);
});
});