Files
plainleaf/plugs/index/complete.test.ts
T

160 lines
5.2 KiB
TypeScript

import { describe, expect, test } from "vitest";
import { createMockSystem } from "../../plug-api/system_mock.ts";
import type { PageMeta } from "@silverbulletmd/silverbullet/type/index";
import { indexMarkdown } from "./indexer.ts";
import { anchorComplete } from "./complete.ts";
import type { CompleteEvent } from "@silverbulletmd/silverbullet/type/client";
const pageMeta = (name: string): PageMeta => ({
ref: name,
tag: "page",
name,
perm: "rw",
lastModified: "",
created: "",
});
/**
* Indexes markdown for a page and stores all resulting objects into the
* mock index so that queryLuaObjects can find them.
*/
async function indexPage(text: string, name: string): Promise<void> {
const objects = await indexMarkdown(text, pageMeta(name));
await (globalThis as any).syscall("index.indexObjects", name, objects);
}
function makeCompleteEvent(
linePrefix: string,
currentPage = "TestPage",
): CompleteEvent {
return {
linePrefix,
pos: linePrefix.length,
pageName: currentPage,
parentNodes: [],
};
}
describe("anchorComplete", () => {
test("bare [[$p completes anchors matching prefix p", async () => {
createMockSystem();
await indexPage(`Hello $pete world.\n`, "PageA");
await indexPage(`Another $people here.\n`, "PageB");
await indexPage(`Unrelated $qux.\n`, "PageC");
const result = await anchorComplete(
makeCompleteEvent("Go to [["),
);
// No $ typed yet — should not trigger
expect(result).toBeNull();
});
test("bare [[$ with no prefix shows all anchors", async () => {
createMockSystem();
await indexPage(`Para $alpha here.\n`, "PageA");
await indexPage(`Para $beta here.\n`, "PageB");
const result = await anchorComplete(
makeCompleteEvent("[[$"),
);
expect(result).toBeTruthy();
const labels = result!.options.map((o) => o.label);
expect(labels).toContain("$alpha");
expect(labels).toContain("$beta");
});
test("[[$p filters anchors by prefix", async () => {
createMockSystem();
await indexPage(`Para $pete here.\n`, "PageA");
await indexPage(`Para $people here.\n`, "PageB");
await indexPage(`Para $qux here.\n`, "PageC");
const result = await anchorComplete(
makeCompleteEvent("[[$p"),
);
expect(result).toBeTruthy();
const labels = result!.options.map((o) => o.label);
expect(labels).toContain("$pete");
expect(labels).toContain("$people");
expect(labels).not.toContain("$qux");
});
test("from position is set to right after the [[ (before the $)", async () => {
createMockSystem();
await indexPage(`Para $anchor.\n`, "PageA");
const linePrefix = "[[$anc";
const result = await anchorComplete(makeCompleteEvent(linePrefix));
expect(result).toBeTruthy();
// pos=6, prefix="anc" (length 3), page="" (length 0)
// from = pos - prefix.length - 1($) - page.length = 6 - 3 - 1 - 0 = 2
// Position 2 is '$', so label "$anchor" replaces "$anc" → correct.
expect(result!.from).toBe(2);
});
test("page-qualified [[PageA$p filters to anchors on PageA", async () => {
createMockSystem();
await indexPage(`Para $pete here.\n`, "PageA");
await indexPage(`Para $pete here.\n`, "PageB"); // same anchor on different page
const result = await anchorComplete(
makeCompleteEvent("[[PageA$p"),
);
expect(result).toBeTruthy();
const options = result!.options;
// All returned options should have detail mentioning PageA only
for (const opt of options) {
expect(opt.detail).toContain("PageA");
expect(opt.detail).not.toContain("PageB");
}
});
test("page-qualified label includes page prefix", async () => {
createMockSystem();
await indexPage(`Para $sec1 here.\n`, "SomePage");
const result = await anchorComplete(
makeCompleteEvent("[[SomePage$"),
);
expect(result).toBeTruthy();
const labels = result!.options.map((o) => o.label);
// label should be "SomePage$sec1" (page prefix + $ + anchor name)
expect(labels).toContain("SomePage$sec1");
});
test("does not trigger when no $ is present in wikilink", async () => {
createMockSystem();
await indexPage(`Para $alpha.\n`, "PageA");
const result = await anchorComplete(
makeCompleteEvent("[[SomePage"),
);
expect(result).toBeNull();
});
test("options are sorted alphabetically by ref", async () => {
createMockSystem();
await indexPage(`$zebra para.\n`, "P1");
await indexPage(`$apple para.\n`, "P2");
await indexPage(`$mango para.\n`, "P3");
const result = await anchorComplete(makeCompleteEvent("[[$"));
expect(result).toBeTruthy();
const labels = result!.options.map((o) => o.label);
const sorted = [...labels].sort();
expect(labels).toEqual(sorted);
});
test("detail field contains hostTag and page", async () => {
createMockSystem();
await indexPage(`Para $info here.\n`, "DocPage");
const result = await anchorComplete(makeCompleteEvent("[[$info"));
expect(result).toBeTruthy();
const opt = result!.options.find((o) => o.label.includes("info"));
expect(opt).toBeTruthy();
expect(opt!.detail).toContain("paragraph");
expect(opt!.detail).toContain("DocPage");
});
});