import { expect, mod, test } from "./fixtures.ts"; // Inline comments: signed HTML-comment notes (``), // optionally `@addressee:`-addressed to hand a question off, that render as a // card widget when the cursor is outside them, and as raw markdown when the // cursor overlaps the block. See plug-api/lib/comments.ts, // plugs/index/comment.ts, client/codemirror/comment_widget.ts, and // plugs/editor/comments.ts. test.describe("Comment card rendering", () => { test.use({ spaceFiles: { "index.md": "A claim.\n\n", }, }); test("conforming comment renders as card; cursor reveals raw", async ({ sbPage, }) => { const editor = sbPage.locator("#sb-editor .cm-content"); const card = editor.locator(".sb-comment-card"); // Cursor starts at doc position 0, outside the comment block, so it // should already render as a card. await expect(card).toBeVisible(); await expect(card).toContainText("@pete"); await expect(card).toContainText("verify"); await expect(editor).not.toContainText("\n", }, }); test("reply and resolve round-trip with undo", async ({ sbPage }) => { const editor = sbPage.locator("#sb-editor .cm-content"); const card = editor.locator(".sb-comment-card"); await expect(card).toBeVisible(); const docText = () => sbPage.evaluate(() => (globalThis as any).client.editorView.state.doc.toString() ); // Click Reply -> a pre-addressed line is inserted and the cursor lands // ready to type the reply body (see CommentCardWidget.reply()). await card.locator("button", { hasText: "Reply" }).click(); await sbPage.keyboard.type("on it"); // Move the cursor back out of the block to collapse it to a card again. await sbPage.evaluate(() => { (globalThis as any).client.editorView.dispatch({ selection: { anchor: 0 }, }); }); await expect(card).toBeVisible(); await expect(card.locator(".sb-comment-message")).toHaveCount(2); await expect(card).toContainText("on it"); // Click Resolve -> the whole comment block is removed from the document. await card.locator("button", { hasText: "Resolve" }).click(); await expect.poll(docText).not.toContain("\n', }, }); // Reply only makes sense when there's someone to reply to -- an // unaddressed note offers Resolve but no Reply button (see // CommentCardWidget.toDOM() in client/codemirror/comment_widget.ts). // buildReplyInsertion's unaddressed fallback path stays in place as a // safeguard for mixed threads (addressed earlier, unaddressed last), but // that path is no longer reachable from this card's UI. test("an unaddressed note's card offers Resolve but no Reply button", async ({ sbPage, }) => { const editor = sbPage.locator("#sb-editor .cm-content"); const card = editor.locator(".sb-comment-card"); await expect(card).toBeVisible(); await expect(card.locator(".sb-comment-message")).toHaveCount(1); await expect(card.locator("button", { hasText: "Resolve" })).toBeVisible(); await expect(card.locator("button", { hasText: "Reply" })).toHaveCount(0); }); }); // "published render hides comments": there is no e2e-reachable surface for // rendered/published HTML in this app (no share/preview route -- the client // only ever shows the editable CodeMirror view). Per the brief, the fallback // lives as a vitest case in client/markdown_renderer/markdown_render.test.ts // ("Conforming inline comment renders to nothing"), asserting CommentBlock // renders to null (empty HTML) for a conforming comment.