Files
vscode-cowriting-plugin/test/e2e/suite/trackChangesPreview.test.ts
T
BenStullsBets 447a1170ce feat: baseline router — git HEAD + snapshot per INV-7/D13/D14; retire machine-landing advance (spec §6.4)
Task 2 of the native-surfaces migration plan. GitBaselineAdapter resolves a
document's HEAD blob via the built-in vscode.git extension (hardened with a
.git/logs/HEAD watch that nudges repo.status(), since the git extension's own
watcher doesn't reliably notice out-of-band commits on non-workspace-folder
repos in the E2E host). DiffViewController is reworked into a baseline router:
head mode (git-tracked, never persisted, re-read on commit) vs snapshot mode
(captured on CoeditingRegistry entry, re-pinned by "Mark Changes as Reviewed"
— renamed from cowriting.pinDiffBaseline, D14). The shipped machine-landing
baseline advance (#48/INV-18) is retired: a landed Claude edit now stays a
visible change-since-baseline until commit or review (INV-7/D21). Legacy
on-disk reasons ("opened"/"machine-landing") migrate to "entered" via
BaselineStore.normalizeReason on load.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 07:09:55 -07:00

141 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import * as assert from "assert";
import * as path from "path";
import * as vscode from "vscode";
import type { CowritingApi } from "../../../src/extension";
const WS = process.env.E2E_WORKSPACE!;
const DOC_REL = "docs/preview.md";
const docUri = () => vscode.Uri.file(path.join(WS, DOC_REL)).toString();
/** Open the doc AND enter coediting (INV-10/Task 2) — the baseline is
* established only on entry; re-entering an already-coedited doc is a no-op
* so the once-captured baseline survives across this order-dependent suite. */
async function openDoc(rel = DOC_REL): Promise<vscode.TextDocument> {
const uri = vscode.Uri.file(path.join(WS, rel));
const doc = await vscode.workspace.openTextDocument(uri);
await vscode.window.showTextDocument(doc);
await vscode.commands.executeCommand("cowriting.coeditDocument");
return doc;
}
async function getApi(): Promise<CowritingApi> {
const ext = vscode.extensions.getExtension("benstull.vscode-cowriting-plugin")!;
const api = (await ext.activate()) as CowritingApi;
assert.ok(api?.trackChangesPreviewController, "extension exports trackChangesPreviewController");
return api;
}
const settle = () => new Promise((r) => setTimeout(r, 400));
const kinds = (api: CowritingApi) =>
(api.trackChangesPreviewController.getLastModel(docUri()) ?? []).map((o) => o.kind);
// Order-dependent (F2F6 pattern): later tests consume earlier state.
suite("F7 track-changes preview (host E2E — markdown only, programmatic seam, no LLM)", () => {
const TARGET = "A target sentence Claude will rewrite via the seam.";
const REPLACEMENT = "A SENTENCE CLAUDE REWROTE via the seam.";
test("open on a markdown doc → panel open, opened baseline shows no changes (PUC-1)", async () => {
const doc = await openDoc();
const api = await getApi();
assert.strictEqual(api.trackChangesPreviewController.isOpen(docUri()), false, "no panel yet");
await vscode.commands.executeCommand("cowriting.showTrackChangesPreview");
await settle();
assert.strictEqual(api.trackChangesPreviewController.isOpen(docUri()), true, "panel open");
const model = api.trackChangesPreviewController.getLastModel(docUri());
assert.ok(model && model.length > 0, "a model was computed");
assert.ok(
model!.every((o) => o.kind === "unchanged"),
"baseline == buffer at open every block unchanged",
);
void doc;
});
test("typing produces a changed/added block (PUC-2)", async () => {
const doc = await openDoc();
const api = await getApi();
const edit = new vscode.WorkspaceEdit();
const end = doc.positionAt(doc.getText().length);
edit.insert(doc.uri, end, "\n\nA freshly typed paragraph.\n");
assert.ok(await vscode.workspace.applyEdit(edit), "operator edit applied");
await settle();
assert.ok(
kinds(api).some((k) => k === "added" || k === "changed"),
"an added/changed block appears after typing",
);
});
test("accepting a proposal does NOT advance the baseline; the landed block stays marked (PUC-3, INV-7/D21, #48 retired)", async () => {
const doc = await openDoc();
const api = await getApi();
const start = doc.getText().indexOf(TARGET);
assert.ok(start >= 0, "fixture contains the target sentence");
const id = await vscode.commands.executeCommand<string>("cowriting.proposeAgentEdit", {
uri: doc.uri.toString(),
start,
end: start + TARGET.length,
newText: REPLACEMENT,
model: "sonnet",
sessionId: "e2e-f7",
turnId: "turn-f7-1",
});
assert.ok(id, "propose returns an id");
assert.ok(await api.proposalController.acceptById(DOC_REL, id!), "accept applies via the seam");
await settle();
// Native-surfaces migration (INV-7/D21): the shipped machine-landing baseline
// advance (#48/INV-18) is retired — the landed block stays a visible change
// until "Mark Changes as Reviewed".
const model = api.trackChangesPreviewController.getLastModel(docUri()) ?? [];
const replacementMarked = model.some(
(o) => o.kind !== "unchanged" && o.block.raw.includes("CLAUDE REWROTE"),
);
assert.ok(replacementMarked, "the just-landed text still renders marked (baseline does not auto-advance)");
});
test("markReviewed resets the baseline to now preview shows no marks (PUC-4)", async () => {
const doc = await openDoc();
const api = await getApi();
await vscode.commands.executeCommand("cowriting.markReviewed");
await settle();
assert.ok(
kinds(api).every((k) => k === "unchanged"),
"after markReviewed, every block is unchanged (baseline == buffer)",
);
void doc;
});
test("a non-markdown doc command warns and opens no panel (PUC-6, markdown-only)", async () => {
const txt = await openDoc("docs/notes.txt");
const api = await getApi();
const key = txt.uri.toString();
assert.notStrictEqual(txt.languageId, "markdown", "fixture is not markdown");
await vscode.commands.executeCommand("cowriting.showTrackChangesPreview");
await settle();
assert.strictEqual(api.trackChangesPreviewController.isOpen(key), false, "no panel for non-markdown");
});
test("a changed flowchart augments the emitted mermaid source (#22, INV-29)", async () => {
const doc = await openDoc();
const api = await getApi();
// Show the preview and pin the baseline so the fixture's `a --> b` flowchart
// is the "before"; then add an edge `a --> c` and confirm the intra-diagram diff.
await vscode.commands.executeCommand("cowriting.showTrackChangesPreview");
await vscode.commands.executeCommand("cowriting.markReviewed");
await settle();
const anchor = doc.getText().indexOf("a --> b");
assert.ok(anchor >= 0, "fixture contains the flowchart edge");
const insertAt = doc.positionAt(anchor + "a --> b".length);
const edit = new vscode.WorkspaceEdit();
edit.insert(doc.uri, insertAt, "\n a --> c");
assert.ok(await vscode.workspace.applyEdit(edit), "flowchart edit applied");
await settle();
const model = api.trackChangesPreviewController.getLastModel(docUri()) ?? [];
const mermaidOp = model.find((o) => o.kind === "changed" && o.block.type === "mermaid");
assert.ok(mermaidOp, "the flowchart block is a changed mermaid op");
const html = api.trackChangesPreviewController.renderHtmlFor(docUri());
assert.match(html, /classDef cwAdded/, "augmented with cwAdded classDef");
assert.match(html, /class\s+c\s+cwAdded/, "node c colored added");
assert.match(html, /cw-mermaid-legend/, "legend emitted");
});
});