From 4212d900554f4462cd02e749b5acc16cfcbd6ae2 Mon Sep 17 00:00:00 2001 From: Ben Stull Date: Thu, 11 Jun 2026 14:39:54 -0700 Subject: [PATCH] =?UTF-8?q?test(f9):=20host=20E2E=20=E2=80=94=20authorship?= =?UTF-8?q?=20mode=20marks=20Claude's=20landed=20span?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- test/e2e/suite/authorship.test.ts | 74 +++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 test/e2e/suite/authorship.test.ts diff --git a/test/e2e/suite/authorship.test.ts b/test/e2e/suite/authorship.test.ts new file mode 100644 index 0000000..b37f4d7 --- /dev/null +++ b/test/e2e/suite/authorship.test.ts @@ -0,0 +1,74 @@ +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", + ); + }); +});