F6: diff-view toggle (Feature #17) #18

Merged
benstull merged 9 commits from f6-diff-view-toggle into main 2026-06-11 14:21:46 +00:00
2 changed files with 173 additions and 0 deletions
Showing only changes of commit aed665a978 - Show all commits
@@ -0,0 +1,7 @@
# Diff view fixture
The baseline opening paragraph stays put so the diff suite can anchor on it.
A target sentence Claude will rewrite via the seam.
A closing paragraph for the operator to edit by hand.
+166
View File
@@ -0,0 +1,166 @@
import * as assert from "assert";
import * as fs from "fs";
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/diffview.md";
async function openDoc(): Promise<vscode.TextDocument> {
const uri = vscode.Uri.file(path.join(WS, DOC_REL));
const doc = await vscode.workspace.openTextDocument(uri);
await vscode.window.showTextDocument(doc);
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?.diffViewController, "extension exports diffViewController");
return api;
}
const settle = () => new Promise((r) => setTimeout(r, 300));
// Order-dependent (F2F4 pattern): later tests consume earlier state. Owns
// docs/diffview.md exclusively.
suite("F6 diff-view toggle (host E2E — programmatic seam ingress, no LLM)", () => {
const TARGET = "A target sentence Claude will rewrite via the seam.";
const REPLACEMENT = "A SENTENCE CLAUDE REWROTE via the seam.";
test("opening a tracked doc captures an `opened` baseline equal to the buffer (INV-18)", async () => {
const doc = await openDoc();
const api = await getApi();
// renderIfOpen on open already called ensureBaseline; assert it captured.
const baseline = api.diffViewController.getBaseline(DOC_REL);
assert.ok(baseline, "baseline captured on first sight");
assert.strictEqual(baseline!.reason, "opened");
assert.strictEqual(baseline!.text, doc.getText(), "baseline = open-time buffer");
});
test("toggle opens a diff tab (original scheme cowriting-baseline) over the live doc (PUC-1)", async () => {
const api = await getApi();
assert.strictEqual(api.diffViewController.isDiffOpen(DOC_REL), false, "no diff open yet");
await vscode.commands.executeCommand("cowriting.toggleDiffView");
await settle();
assert.strictEqual(api.diffViewController.isDiffOpen(DOC_REL), true, "diff tab open");
// The active tab is a TextDiff whose original is the baseline scheme.
const active = vscode.window.tabGroups.activeTabGroup.activeTab;
assert.ok(active && active.input instanceof vscode.TabInputTextDiff, "active tab is a diff");
assert.strictEqual(
(active!.input as vscode.TabInputTextDiff).original.scheme,
"cowriting-baseline",
"left side served by the baseline provider",
);
});
test("typing leaves the baseline unchanged while the buffer diverges", async () => {
const doc = await openDoc();
const api = await getApi();
const before = api.diffViewController.getBaseline(DOC_REL)!.text;
const edit = new vscode.WorkspaceEdit();
edit.insert(doc.uri, new vscode.Position(0, 0), "OPERATOR ADDED LINE\n");
assert.ok(await vscode.workspace.applyEdit(edit), "operator edit applied");
await settle();
assert.strictEqual(api.diffViewController.getBaseline(DOC_REL)!.text, before, "baseline unchanged by typing");
assert.notStrictEqual(doc.getText(), before, "buffer diverged");
});
test("accepting a proposal advances the baseline past the landed text (PUC-2, INV-18)", async () => {
const doc = await openDoc();
const api = await getApi();
const start = doc.getText().indexOf(TARGET);
assert.ok(start >= 0, "fixture contains the target");
const id = await vscode.commands.executeCommand<string>("cowriting.proposeAgentEdit", {
uri: doc.uri.toString(),
start,
end: start + TARGET.length,
newText: REPLACEMENT,
model: "sonnet",
sessionId: "e2e-diff",
turnId: "turn-d1",
});
assert.ok(id, "propose returns an id");
assert.ok(await api.proposalController.acceptById(DOC_REL, id!), "accept applies via the seam");
await settle();
const baseline = api.diffViewController.getBaseline(DOC_REL)!;
assert.strictEqual(baseline.reason, "machine-landing", "baseline advanced on the landing");
assert.ok(baseline.text.includes(REPLACEMENT), "landed text is in the baseline (won't show as a change)");
assert.ok(!baseline.text.includes(TARGET), "old target gone from the baseline too");
assert.strictEqual(baseline.text, doc.getText(), "baseline == buffer right after the landing");
});
test("an operator edit after the landing makes baseline ≠ buffer (the operator delta)", async () => {
const doc = await openDoc();
const api = await getApi();
const edit = new vscode.WorkspaceEdit();
edit.insert(doc.uri, new vscode.Position(0, 0), "POST-LANDING OPERATOR LINE\n");
assert.ok(await vscode.workspace.applyEdit(edit));
await settle();
assert.notStrictEqual(
api.diffViewController.getBaseline(DOC_REL)!.text,
doc.getText(),
"operator changes show against the advanced baseline",
);
});
test("pin resets the baseline to now: baseline == buffer, reason pinned (PUC-3)", async () => {
const doc = await openDoc();
const api = await getApi();
await vscode.commands.executeCommand("cowriting.pinDiffBaseline");
await settle();
const baseline = api.diffViewController.getBaseline(DOC_REL)!;
assert.strictEqual(baseline.reason, "pinned");
assert.strictEqual(baseline.text, doc.getText(), "pinned baseline == current buffer (diff empties)");
});
test("the baseline is persisted on disk under the storage dir with the expected content (PUC-4)", async () => {
const api = await getApi();
const p = api.diffViewController.baselineFilePath(DOC_REL);
assert.ok(p, "storage-backed baseline path is available");
assert.ok(fs.existsSync(p!), `baseline file exists at ${p}`);
const onDisk = JSON.parse(fs.readFileSync(p!, "utf8"));
assert.strictEqual(onDisk.docPath, DOC_REL);
assert.strictEqual(onDisk.reason, "pinned", "last epoch (pin) persisted");
assert.strictEqual(onDisk.text, api.diffViewController.getBaseline(DOC_REL)!.text, "on-disk == in-memory");
// INV-19: nothing leaked into the repo's .threads sidecar tree.
assert.ok(!p!.includes(`${path.sep}.threads${path.sep}`), "baseline is NOT in the sidecar tree");
});
test("toggle again closes the diff tab and reveals the normal editor (PUC-1)", async () => {
const api = await getApi();
if (!api.diffViewController.isDiffOpen(DOC_REL)) {
await vscode.commands.executeCommand("cowriting.toggleDiffView");
await settle();
}
assert.strictEqual(api.diffViewController.isDiffOpen(DOC_REL), true, "diff open before close");
await vscode.commands.executeCommand("cowriting.toggleDiffView");
await settle();
assert.strictEqual(api.diffViewController.isDiffOpen(DOC_REL), false, "diff tab closed");
// "Normal editor is back": the file is the active text editor, and no
// baseline-diff tab for it remains in any group.
assert.strictEqual(
vscode.window.activeTextEditor?.document.uri.toString(),
vscode.Uri.file(path.join(WS, DOC_REL)).toString(),
"the normal editor for the doc is active after closing the diff",
);
});
test("toggling on an untracked doc warns and opens no diff (PUC-5)", async () => {
await getApi();
const untracked = await vscode.workspace.openTextDocument({ content: "scratch", language: "markdown" });
await vscode.window.showTextDocument(untracked);
await settle();
await vscode.commands.executeCommand("cowriting.toggleDiffView");
await settle();
// No baseline-diff tab for an untitled doc anywhere.
const anyDiff = vscode.window.tabGroups.all.some((g) =>
g.tabs.some(
(t) =>
t.input instanceof vscode.TabInputTextDiff &&
t.input.original.scheme === "cowriting-baseline" &&
t.input.modified.toString() === untracked.uri.toString(),
),
);
assert.strictEqual(anyDiff, false, "no diff opened for the untracked doc");
});
});