vicky469_aside/tests/derivedCommentMetadata.test.ts
vicky469 0cdc6ca29d refactor(comments): remove resolved feature
Removes the resolved/unresolve comment feature entirely. Comments are
now always visible — there is no archived state or toggle.

- Removes `resolved?: boolean` from CommentThread, Comment, and all
  storage/normalization paths
- Removes resolveComment/unresolveComment from CommentManager,
  CommentMutationController, and public plugin API
- Removes shouldShowResolvedComments/setShowResolvedComments from
  session, mutation, and persistence controllers
- Removes sidebar resolve button, toolbar chip, and resolved empty
  states from AsideView
- Removes setThreadResolved sync event generation; keeps the op in the
  union type as a backward-compatible no-op so old stored events parse
  without error
- Deletes resolvedCommentVisibility.ts, commentSelectionVisibility.ts,
  and resolve-note-comment.mjs scripts
- Updates README to remove resolved feature mentions
- Updates all tests accordingly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 18:11:45 +08:00

159 lines
5.7 KiB
TypeScript

import * as assert from "node:assert/strict";
import test from "node:test";
import type { CachedMetadata, LinkCache, TFile } from "obsidian";
import type { Comment } from "../src/commentManager";
import { DerivedCommentMetadataManager } from "../src/core/derived/derivedCommentMetadata";
import {
getDerivedCommentLinksSignature,
mergeDerivedLinkTargetCounts,
mergeDerivedLinksIntoCache,
} from "../src/core/derived/derivedCommentMetadataPlanner";
import type { DerivedCommentLinks } from "../src/core/text/commentMentions";
function createFile(path: string): TFile {
return {
path,
basename: path.split("/").pop()?.replace(/\.[^.]+$/, "") ?? path,
extension: path.split(".").pop() ?? "",
} as TFile;
}
function createComment(overrides: Partial<Comment> = {}): Comment {
return {
id: overrides.id ?? "comment-1",
filePath: overrides.filePath ?? "Folder/Note.md",
startLine: overrides.startLine ?? 1,
startChar: overrides.startChar ?? 2,
endLine: overrides.endLine ?? 1,
endChar: overrides.endChar ?? 6,
selectedText: overrides.selectedText ?? "beta",
selectedTextHash: overrides.selectedTextHash ?? "hash:beta",
comment: overrides.comment ?? "See [[Target]] and [[Missing]].",
timestamp: overrides.timestamp ?? 123,
anchorKind: overrides.anchorKind ?? "selection",
orphaned: overrides.orphaned ?? false,
};
}
function createDerivedLinks(overrides: Partial<DerivedCommentLinks> = {}): DerivedCommentLinks {
const syntheticLink: LinkCache = {
link: "Target",
original: "[[Target]]",
position: {
start: { line: 1, col: 2, offset: 10 },
end: { line: 1, col: 10, offset: 18 },
},
};
return {
links: overrides.links ?? [syntheticLink],
resolved: overrides.resolved ?? { "Target.md": 1 },
unresolved: overrides.unresolved ?? { Missing: 1 },
};
}
test("derived metadata planner merges links into cache once and marks the result", () => {
const baseCache: CachedMetadata = {
links: [{
link: "Existing",
original: "[[Existing]]",
position: {
start: { line: 0, col: 0, offset: 0 },
end: { line: 0, col: 10, offset: 10 },
},
}],
};
const derivedLinks = createDerivedLinks();
const merged = mergeDerivedLinksIntoCache(baseCache, derivedLinks);
const mergedAgain = mergeDerivedLinksIntoCache(merged, derivedLinks);
assert.ok(merged);
assert.equal(merged?.links?.length, 2);
assert.equal(mergedAgain, merged);
});
test("derived metadata planner signature is stable across resolved and unresolved key order", () => {
const left = getDerivedCommentLinksSignature(createDerivedLinks({
resolved: { "b.md": 2, "a.md": 1 },
unresolved: { Zeta: 1, Alpha: 3 },
}));
const right = getDerivedCommentLinksSignature(createDerivedLinks({
resolved: { "a.md": 1, "b.md": 2 },
unresolved: { Alpha: 3, Zeta: 1 },
}));
assert.equal(left, right);
});
test("derived metadata planner merges target counts by subtracting previous and adding next", () => {
const merged = mergeDerivedLinkTargetCounts(
{ "a.md": 3, "b.md": 1 },
{ "a.md": 2, "b.md": 1 },
{ "a.md": 1, "c.md": 4 },
);
assert.deepEqual(merged, {
"a.md": 2,
"c.md": 4,
});
});
test("derived metadata manager augments metadata cache and keeps derived link counts in sync", () => {
const file = createFile("Folder/Note.md");
const targetFile = createFile("Target.md");
const triggerCalls: Array<{ name: string; args: unknown[] }> = [];
const baseCache: CachedMetadata = {
links: [{
link: "Existing",
original: "[[Existing]]",
position: {
start: { line: 0, col: 0, offset: 0 },
end: { line: 0, col: 10, offset: 10 },
},
}],
};
const metadataCache = {
getCache: (_path: string) => baseCache,
getFileCache: (_file: TFile) => baseCache,
getFirstLinkpathDest: (linkPath: string) => (linkPath === "Target" ? targetFile : null),
resolvedLinks: {} as Record<string, Record<string, number>>,
unresolvedLinks: {} as Record<string, Record<string, number>>,
trigger: (name: string, ...args: unknown[]) => {
triggerCalls.push({ name, args });
},
};
const app = {
metadataCache,
vault: {
getAbstractFileByPath: (path: string) => {
if (path === file.path) {
return file;
}
if (path === targetFile.path) {
return targetFile;
}
return null;
},
},
} as unknown as ConstructorParameters<typeof DerivedCommentMetadataManager>[0];
const manager = new DerivedCommentMetadataManager(app);
manager.installMetadataCacheAugmentation();
manager.syncDerivedCommentLinksForFile(file, "line 0\nline 1", [createComment()]);
const mergedCache = metadataCache.getCache(file.path);
assert.equal(mergedCache?.links?.length, 3);
assert.deepEqual(metadataCache.resolvedLinks[file.path], { "Target.md": 1 });
assert.deepEqual(metadataCache.unresolvedLinks[file.path], { Missing: 1 });
manager.clearDerivedCommentLinksForFile(file.path);
assert.equal(metadataCache.resolvedLinks[file.path], undefined);
assert.equal(metadataCache.unresolvedLinks[file.path], undefined);
manager.restoreMetadataCacheAugmentation();
assert.equal(metadataCache.getCache(file.path), baseCache);
assert.equal(triggerCalls.some((call) => call.name === "resolved"), true);
});