Files

101 lines
3.5 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 type { CompleteEvent } from "@silverbulletmd/silverbullet/type/client";
import { footnoteComplete, pageComplete } from "./complete.ts";
function makeCompleteEvent(
linePrefix: string,
currentPage = "TestPage",
): CompleteEvent {
return {
linePrefix,
pos: linePrefix.length,
pageName: currentPage,
parentNodes: [],
};
}
async function indexPageMeta(name: string, tags: string[] = []): Promise<void> {
const obj: PageMeta = {
ref: name,
tag: "page",
tags,
name,
perm: "rw",
lastModified: "0",
created: "0",
};
await (globalThis as any).syscall("index.indexObjects", name, [obj]);
}
describe("pageComplete meta-page caret prefix", () => {
test("[[^ returns only meta-tagged pages", async () => {
createMockSystem();
await indexPageMeta("CONFIG", ["meta"]);
await indexPageMeta("Library/Std", ["meta/library"]);
await indexPageMeta("RegularPage", []);
const result = await pageComplete(makeCompleteEvent("[[^"));
expect(result).toBeTruthy();
const labels = result!.options.map((o) => o.label);
expect(labels).toContain("^CONFIG");
expect(labels).toContain("^Library/Std");
expect(labels).not.toContain("RegularPage");
expect(labels).not.toContain("^RegularPage");
});
test("[[^CON keeps the caret prefix on labels and `from` covers it", async () => {
createMockSystem();
await indexPageMeta("CONFIG", ["meta"]);
const linePrefix = "[[^CON";
const result = await pageComplete(makeCompleteEvent(linePrefix));
expect(result).toBeTruthy();
// `from` must be the position right before the caret so CodeMirror
// filters `^CON` against `^CONFIG` (a direct prefix match).
expect(result!.from).toBe(linePrefix.length - "^CON".length);
expect(result!.options.map((o) => o.label)).toContain("^CONFIG");
});
test("[[ without caret excludes meta pages", async () => {
createMockSystem();
await indexPageMeta("CONFIG", ["meta"]);
await indexPageMeta("RegularPage", []);
const result = await pageComplete(makeCompleteEvent("[["));
expect(result).toBeTruthy();
const labels = result!.options.map((o) => o.label);
expect(labels).toContain("RegularPage");
expect(labels).not.toContain("CONFIG");
expect(labels).not.toContain("^CONFIG");
});
});
describe("footnoteComplete does not collide with [[^ wikilinks (#1966)", () => {
// Regression: footnoteComplete used to match `[^...` inside `[[^...`,
// returning a `from` position that disagreed with pageComplete's, causing
// the merged completion to be dropped client-side and a console error.
test("[[^ returns null", async () => {
expect(await footnoteComplete(makeCompleteEvent("[[^"))).toBeNull();
});
test("[[^CON returns null", async () => {
expect(await footnoteComplete(makeCompleteEvent("[[^CON"))).toBeNull();
});
test("![[^ (image embed) returns null", async () => {
expect(await footnoteComplete(makeCompleteEvent("![[^"))).toBeNull();
});
test("triple bracket [[[^foo also returns null", async () => {
expect(await footnoteComplete(makeCompleteEvent("[[[^foo"))).toBeNull();
});
test("plain text without [^ returns null", async () => {
expect(await footnoteComplete(makeCompleteEvent("hello"))).toBeNull();
expect(await footnoteComplete(makeCompleteEvent("[[CONFIG"))).toBeNull();
});
});