import * as assert from "node:assert/strict"; import test from "node:test"; import { ALL_COMMENTS_NOTE_PATH, LEGACY_ALL_COMMENTS_NOTE_PATH, ALL_COMMENTS_NOTE_IMAGE_ALT, ALL_COMMENTS_NOTE_IMAGE_CAPTION, ALL_COMMENTS_NOTE_IMAGE_URL, buildAllCommentsNoteContent, buildCommentLocationLineNumberMap, buildIndexCommentBlockId, buildIndexNoteNavigationMap, buildCommentLocationUrl, findCommentLocationLineNumber, findCommentLocationTargetInMarkdownLine, findFileHeadingPathInMarkdownLine, findIndexMarkdownLineTarget, isAllCommentsNotePath, normalizeAllCommentsNoteImageCaption, normalizeAllCommentsNoteImageUrl, normalizeAllCommentsNotePath, parseCommentLocationUrl, parseIndexFileOpenUrl, } from "../src/core/derived/allCommentsNote"; import type { Comment, CommentThread } from "../src/commentManager"; function createComment(overrides: Partial = {}): Comment { return { id: "comment-1", filePath: "Folder/Note.md", startLine: 4, startChar: 2, endLine: 4, endChar: 7, selectedText: "hello", selectedTextHash: "hash-1", comment: "This is a side note.", timestamp: 1710000000000, ...overrides, }; } function createThread(overrides: Partial = {}): CommentThread { return { id: "thread-1", filePath: "Folder/Note.md", startLine: 4, startChar: 2, endLine: 4, endChar: 7, selectedText: "hello", selectedTextHash: "hash-1", entries: [{ id: "entry-1", body: "This is a side note.", timestamp: 1710000000000, }], createdAt: 1710000000000, updatedAt: 1710000000000, ...overrides, }; } function escapeHtmlText(value: string): string { return value .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function expectedFileRow(filePath: string): string { const fileName = filePath.split("/").pop() ?? filePath; const escapedPath = escapeHtmlText(filePath); return `- ${escapeHtmlText(fileName)}`; } function countOccurrences(value: string, needle: string): number { return value.split(needle).length - 1; } test("buildCommentLocationUrl encodes vault, file, and comment id", () => { const url = buildCommentLocationUrl("dev vault", createComment({ filePath: "Folder/My Note.md", id: "comment 1", })); assert.equal( url, "obsidian://aside-comment?vault=dev%20vault&file=Folder%2FMy%20Note.md&commentId=comment%201" ); }); test("parseCommentLocationUrl extracts file path and comment id", () => { assert.deepEqual( parseCommentLocationUrl("obsidian://aside-comment?vault=dev&file=Folder%2FMy%20Note.md&commentId=comment%201"), { filePath: "Folder/My Note.md", commentId: "comment 1", }, ); assert.equal(parseCommentLocationUrl("obsidian://open?vault=dev&file=Folder%2FMy%20Note.md"), null); }); test("findCommentLocationTargetInMarkdownLine finds the side note target in a generated markdown list row", () => { assert.deepEqual( findCommentLocationTargetInMarkdownLine(' [hello](obsidian://aside-comment?vault=dev&file=Folder%2FNote.md&commentId=comment-1&kind=page) #hi'), { filePath: "Folder/Note.md", commentId: "comment-1", }, ); assert.equal(findCommentLocationTargetInMarkdownLine("**Folder/Note.md**"), null); }); test("findFileHeadingPathInMarkdownLine extracts the source file path from an index heading row", () => { assert.equal( findFileHeadingPathInMarkdownLine('##### Note.md'), "Folder/Note.md", ); assert.equal( findFileHeadingPathInMarkdownLine('##### Legacy.md'), "Folder/Legacy.md", ); assert.equal( findFileHeadingPathInMarkdownLine('- Linked.md'), "Folder/Linked.md", ); assert.equal( findFileHeadingPathInMarkdownLine('- [Linked.md](obsidian://open?vault=dev&file=Folder%2FLinked.md)'), "Folder/Linked.md", ); assert.equal(findFileHeadingPathInMarkdownLine("### Folder"), null); }); test("parseIndexFileOpenUrl extracts file paths from generated file links", () => { assert.equal( parseIndexFileOpenUrl("obsidian://open?vault=dev&file=Folder%2FLinked.md"), "Folder/Linked.md", ); assert.equal(parseIndexFileOpenUrl("obsidian://aside-comment?vault=dev&file=Folder%2FLinked.md"), null); }); test("findIndexMarkdownLineTarget resolves both comment rows and file heading rows", () => { assert.deepEqual( findIndexMarkdownLineTarget(' [hello](obsidian://aside-comment?vault=dev&file=Folder%2FNote.md&commentId=comment-1&kind=page) #hi'), { kind: "comment", filePath: "Folder/Note.md", commentId: "comment-1", }, ); assert.deepEqual( findIndexMarkdownLineTarget('##### Note.md'), { kind: "file", filePath: "Folder/Note.md", }, ); }); test("findCommentLocationLineNumber returns null for the simplified generated index", () => { const content = buildAllCommentsNoteContent("dev", [ createComment({ id: "comment-1", filePath: "Folder/Note.md", }), ]); assert.equal(findCommentLocationLineNumber(content, "comment-1"), null); }); test("buildCommentLocationLineNumberMap still indexes legacy comment rows", () => { const content = [ ' [alpha](obsidian://aside-comment?vault=dev&file=Folder%2FNote.md&commentId=comment-1&kind=anchored)', ' [beta](obsidian://aside-comment?vault=dev&file=Folder%2FOther.md&commentId=comment-2&kind=anchored)', ].join("\n"); const lineNumbersByCommentId = buildCommentLocationLineNumberMap(content); assert.equal(lineNumbersByCommentId.get("comment-1"), 0); assert.equal(lineNumbersByCommentId.get("comment-2"), 1); assert.equal(lineNumbersByCommentId.get("missing-comment"), undefined); }); test("buildIndexNoteNavigationMap tracks simplified file link rows", () => { const content = buildAllCommentsNoteContent("dev", [ createComment({ id: "comment-1", filePath: "Projects/Alpha/Note A.md", }), createComment({ id: "comment-2", filePath: "Projects/Alpha/Note B.md", }), ]); const navigationMap = buildIndexNoteNavigationMap(content); assert.equal(navigationMap.fileLineByFilePath.get("Projects/Alpha/Note A.md") === undefined, false); assert.equal(navigationMap.fileLineByFilePath.get("Projects/Alpha/Note B.md") === undefined, false); assert.equal(navigationMap.targetsByCommentId.size, 0); assert.equal(content.split("\n")[navigationMap.fileLineByFilePath.get("Projects/Alpha/Note A.md") ?? -1] ?? "", expectedFileRow("Projects/Alpha/Note A.md")); assert.equal(content.split("\n")[navigationMap.fileLineByFilePath.get("Projects/Alpha/Note B.md") ?? -1] ?? "", expectedFileRow("Projects/Alpha/Note B.md")); }); test("buildIndexCommentBlockId normalizes comment ids into stable block ids", () => { assert.equal( buildIndexCommentBlockId("Comment 01/alpha"), "aside-index-comment-comment-01-alpha", ); }); test("buildAllCommentsNoteContent lists unique files grouped by folder", () => { const content = buildAllCommentsNoteContent("dev", [ createComment({ id: "comment-1", filePath: "Projects/Alpha/Note B.md", }), createComment({ id: "comment-2", filePath: "Projects/Alpha/Note A.md", }), createComment({ id: "comment-3", filePath: "Projects/Alpha/Note A.md", }), ]); assert.equal(content.match(/^Projects\/Alpha$/gm)?.length ?? 0, 1); assert.equal(countOccurrences(content, expectedFileRow("Projects/Alpha/Note A.md")), 1); assert.equal(countOccurrences(content, expectedFileRow("Projects/Alpha/Note B.md")), 1); assert.equal(content.includes(`Projects/Alpha\n${expectedFileRow("Projects/Alpha/Note A.md")}\n\n${expectedFileRow("Projects/Alpha/Note B.md")}`), true); assert.doesNotMatch(content, /commentId=/); assert.doesNotMatch(content, /aside-index-kind-dot/); }); test("buildAllCommentsNoteContent renders deduped source-file tags", () => { const content = buildAllCommentsNoteContent("dev", [ createComment({ id: "comment-1", filePath: "Projects/Alpha/Note A.md", comment: "Needs #Review and #follow/up.", }), createComment({ id: "comment-2", filePath: "Projects/Alpha/Note A.md", comment: "Duplicate #review plus #idea.", }), createComment({ id: "comment-3", filePath: "Projects/Alpha/Note B.md", comment: "No tags here.", }), ]); assert.equal( content.includes(`${expectedFileRow("Projects/Alpha/Note A.md")} #follow/up #idea #Review`), true, ); assert.equal(content.includes(`${expectedFileRow("Projects/Alpha/Note A.md")}\n #follow/up`), false); assert.equal(content.includes("Tags:"), false); }); test("buildAllCommentsNoteContent includes tags from every visible thread entry", () => { const content = buildAllCommentsNoteContent("dev", [ createThread({ id: "thread-1", filePath: "Projects/Alpha/Note A.md", entries: [ { id: "entry-1", body: "Root #root", timestamp: 1710000000000, }, { id: "entry-2", body: "Reply #reply", timestamp: 1710000001000, }, ], }), ]); assert.equal( content.includes(`${expectedFileRow("Projects/Alpha/Note A.md")} #reply #root`), true, ); }); test("buildAllCommentsNoteContent renders only the header when there are no comments", () => { const content = buildAllCommentsNoteContent("dev", []); assert.equal( content, `![${ALL_COMMENTS_NOTE_IMAGE_ALT}](${ALL_COMMENTS_NOTE_IMAGE_URL})\n
${ALL_COMMENTS_NOTE_IMAGE_CAPTION}
\n`, ); }); test("buildAllCommentsNoteContent ignores comments attached to the generated aggregate note", () => { const content = buildAllCommentsNoteContent("dev", [ createComment({ filePath: ALL_COMMENTS_NOTE_PATH, selectedText: "self", }), createComment({ id: "comment-2", filePath: "Z.md", selectedText: "real", }), ]); assert.doesNotMatch(content, /Aside index\.md/); assert.equal(content.includes(expectedFileRow("Z.md")), true); }); test("buildAllCommentsNoteContent ignores comments whose source file no longer exists", () => { const content = buildAllCommentsNoteContent("dev", [ createComment({ id: "missing-note", filePath: "Missing.md", selectedText: "gone", }), createComment({ id: "real-note", filePath: "Real.md", selectedText: "still here", }), ], { hasSourceFile: (filePath) => filePath === "Real.md", }); assert.doesNotMatch(content, /Missing\.md/); assert.equal(content.includes(expectedFileRow("Real.md")), true); }); test("buildAllCommentsNoteContent escapes folder and file link HTML", () => { const content = buildAllCommentsNoteContent("dev", [ createComment({ filePath: "A&B/.md", }), ]); assert.match(content, /^A&B$/m); assert.equal(content.includes(expectedFileRow("A&B/.md")), true); }); test("buildAllCommentsNoteContent groups files under a shared folder heading", () => { const longPath = "Clippings/Vlad Tenev and Tudor Achim on mathematical superintelligence and the end of buggy software.md"; const content = buildAllCommentsNoteContent("dev", [ createComment({ filePath: longPath, }), ]); assert.match( content, new RegExp(`Clippings\\n${expectedFileRow(longPath).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`), ); }); test("buildAllCommentsNoteContent groups multiple files under the same path heading", () => { const content = buildAllCommentsNoteContent("dev", [ createComment({ id: "comment-1", filePath: "Projects/Alpha/Note A.md", anchorKind: "page", }), createComment({ id: "comment-2", filePath: "Projects/Alpha/Note B.md", selectedText: "beta", }), ]); assert.equal(content.match(/^Projects\/Alpha$/gm)?.length ?? 0, 1); assert.equal(content.includes(`Projects/Alpha\n${expectedFileRow("Projects/Alpha/Note A.md")}`), true); assert.equal(content.includes(expectedFileRow("Projects/Alpha/Note B.md")), true); }); test("isAllCommentsNotePath matches the generated note path", () => { assert.equal(isAllCommentsNotePath(ALL_COMMENTS_NOTE_PATH), true); assert.equal(isAllCommentsNotePath("notes/custom index.md", "notes/custom index.md"), true); assert.equal(isAllCommentsNotePath("Random.md"), false); }); test("normalizeAllCommentsNotePath keeps the current and legacy names and adds md when needed", () => { assert.equal(ALL_COMMENTS_NOTE_PATH, "🐰 Aside Index.md"); assert.equal(LEGACY_ALL_COMMENTS_NOTE_PATH, "Aside index.md"); assert.equal(normalizeAllCommentsNotePath(""), ALL_COMMENTS_NOTE_PATH); assert.equal(normalizeAllCommentsNotePath("notes/custom index"), "notes/custom index.md"); assert.equal(normalizeAllCommentsNotePath("notes/custom index.md"), "notes/custom index.md"); }); test("normalizeAllCommentsNoteImageUrl keeps the default when blank", () => { assert.equal(normalizeAllCommentsNoteImageUrl(""), ALL_COMMENTS_NOTE_IMAGE_URL); assert.equal(normalizeAllCommentsNoteImageUrl(" https://example.com/header.webp "), "https://example.com/header.webp"); }); test("normalizeAllCommentsNoteImageCaption keeps the default when missing and allows blank", () => { assert.equal(normalizeAllCommentsNoteImageCaption(null), ALL_COMMENTS_NOTE_IMAGE_CAPTION); assert.equal(normalizeAllCommentsNoteImageCaption(" Custom caption "), "Custom caption"); assert.equal(normalizeAllCommentsNoteImageCaption(" "), ""); });