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 settle = () => new Promise((r) => setTimeout(r, 300)); async function getApi(): Promise { const ext = vscode.extensions.getExtension("benstull.vscode-cowriting-plugin")!; const api = (await ext.activate()) as CowritingApi; assert.ok(api?.trackChangesPreviewController && api?.proposalController, "exports preview + proposal"); return api; } // F9 host E2E (no LLM): authorship mode reflects Claude's landed span. Owns its // own markdown doc, disjoint from the other suites' fixtures. suite("F9 authorship preview (host E2E — seam ingress, no LLM)", () => { const DOC_REL = "docs/f9authorship.md"; const TARGET = "The sentence Claude will compose over."; test("authorship mode marks Claude's accepted edit; track-changes mode still works", async () => { const abs = path.join(WS, DOC_REL); fs.mkdirSync(path.dirname(abs), { recursive: true }); fs.writeFileSync(abs, `# F9\n\n${TARGET}\n`, "utf8"); const uri = vscode.Uri.file(abs); const doc = await vscode.workspace.openTextDocument(uri); await vscode.window.showTextDocument(doc); await settle(); const api = await getApi(); const key = uri.toString(); // open the preview (track-changes mode by default) await vscode.commands.executeCommand("cowriting.showTrackChangesPreview"); await settle(); assert.ok(api.trackChangesPreviewController.isOpen(key), "preview open"); assert.strictEqual(api.trackChangesPreviewController.getMode(key), "changes", "defaults to track-changes"); // Claude composes via the seam (propose → accept) const start = doc.getText().indexOf(TARGET); const id = await vscode.commands.executeCommand("cowriting.proposeAgentEdit", { uri: key, start, end: start + TARGET.length, newText: "The sentence CLAUDE COMPOSED.", model: "sonnet", sessionId: "e2e-f9", turnId: "turn-f9", }); assert.ok(await api.proposalController.acceptById(DOC_REL, id!), "accept applies"); await settle(); // attribution has a Claude span now const claudeSpan = api.attributionController.getSpans(DOC_REL).find((s) => s.authorKind === "agent"); assert.ok(claudeSpan, "Claude span recorded by F3"); // flip to authorship mode → the preview reads a Claude span api.trackChangesPreviewController.setMode(key, "authorship"); await settle(); assert.strictEqual(api.trackChangesPreviewController.getMode(key), "authorship"); const spans = api.attributionController.spansFor(doc); assert.ok(spans.some((s) => s.author === "claude"), "spansFor reports a Claude span for the preview"); // back to track-changes — still functional (regression) api.trackChangesPreviewController.setMode(key, "changes"); await settle(); assert.strictEqual(api.trackChangesPreviewController.getMode(key), "changes"); assert.ok( (api.trackChangesPreviewController.getLastModel(key) ?? []).length >= 1, "track-changes model still computed", ); }); });