Files
plainleaf/plug-api/lib/ref.test.ts
T
2026-04-30 19:57:35 +02:00

175 lines
5.8 KiB
TypeScript

import { describe, expect, test } from "vitest";
import {
decodePageURI,
encodePageURI,
encodeRef,
isValidName,
isValidPath,
parseToRef,
} from "./ref.ts";
test("parseToRef() default cases", () => {
expect(parseToRef("foo")).toEqual({ path: "foo.md" });
expect(parseToRef("/foo")).toEqual({ path: "foo.md" });
expect(parseToRef("foo/bar")).toEqual({ path: "foo/bar.md" });
expect(parseToRef("foo.png")).toEqual({ path: "foo.png" });
expect(parseToRef("foo.md")).toEqual({ path: "foo.md" });
expect(parseToRef("foo.")).toEqual({ path: "foo..md" });
expect(parseToRef("foo..")).toEqual({ path: "foo...md" });
expect(parseToRef(" .foo")).toEqual({ path: " .foo" });
expect(parseToRef("foo[bar")).toEqual({ path: "foo[bar.md" });
expect(parseToRef("foo]bar")).toEqual({ path: "foo]bar.md" });
expect(parseToRef("foo(bar")).toEqual({ path: "foo(bar.md" });
expect(parseToRef("foo)bar")).toEqual({ path: "foo)bar.md" });
expect(parseToRef("/bar/.../foo")).toEqual({ path: "bar/.../foo.md" });
expect(parseToRef("/foo/.bar.md")).toEqual(null);
expect(parseToRef("foo.md.md")).toEqual(null);
expect(parseToRef("/.../foo")).toEqual(null);
expect(parseToRef("^.foo")).toEqual(null);
expect(parseToRef(".foobar")).toEqual(null);
expect(parseToRef("foo[[bar")).toEqual(null);
expect(parseToRef("foo]]bar")).toEqual(null);
expect(parseToRef("foo|bar")).toEqual(null);
expect(parseToRef("foo@bar")).toEqual(null);
expect(parseToRef("/../foo")).toEqual(null);
expect(parseToRef("/./foo")).toEqual(null);
expect(parseToRef("/bar/../foo")).toEqual(null);
expect(parseToRef("/bar/./foo")).toEqual(null);
expect(parseToRef("")).toEqual({ path: "" });
expect(parseToRef("/")).toEqual({ path: "" });
expect(parseToRef("/.md")).toEqual(null);
expect(parseToRef("/.foo")).toEqual(null);
expect(parseToRef("/@132")).toEqual({
path: "",
details: { type: "position", pos: 132 },
});
});
test("parseToRef() link cases", () => {
expect(parseToRef("^foo")).toEqual({ path: "foo.md", meta: true });
expect(parseToRef("foo# header")).toEqual({
path: "foo.md",
details: { type: "header", header: "header" },
});
expect(parseToRef("foo# header@123")).toEqual({
path: "foo.md",
details: { type: "header", header: "header@123" },
});
expect(parseToRef("foo@1231")).toEqual({
path: "foo.md",
details: { type: "position", pos: 1231 },
});
expect(parseToRef("foo@l42c69")).toEqual({
path: "foo.md",
details: { type: "linecolumn", line: 42, column: 69 },
});
expect(parseToRef("foo@L42C69")).toEqual({
path: "foo.md",
details: { type: "linecolumn", line: 42, column: 69 },
});
expect(parseToRef("foo@L42")).toEqual({
path: "foo.md",
details: { type: "linecolumn", line: 42, column: 1 },
});
expect(parseToRef("foo@ 123")).toEqual(null);
expect(parseToRef("foo@c69")).toEqual(null);
expect(parseToRef("foo@123#header")).toEqual(null);
expect(parseToRef("foo@123@l29")).toEqual(null);
});
test("encodeRef() cases", () => {
// Encoding
expect(encodeRef({ path: "foo.md" })).toEqual("foo");
expect(
encodeRef({ path: "foo.md", details: { type: "position", pos: 10 } }),
).toEqual("foo@10");
expect(
encodeRef({
path: "foo.md",
details: { type: "linecolumn", line: 10, column: 69 },
}),
).toEqual("foo@L10C69");
expect(
encodeRef({
path: "foo.md",
details: { type: "header", header: "bar" },
}),
).toEqual("foo#bar");
});
test("isValidPath() and isValidName()", () => {
expect(isValidPath("foo.md")).toBeTruthy();
expect(!isValidPath("foo")).toBeTruthy();
expect(!isValidPath("foo.md@123")).toBeTruthy();
expect(isValidName("foo")).toBeTruthy();
expect(!isValidName("foo.md")).toBeTruthy();
expect(!isValidName("foo@123")).toBeTruthy();
expect(!isValidName("^foo@123")).toBeTruthy();
expect(!isValidName("foo[[bar")).toBeTruthy();
// Disallow < and > in ref names
expect(!isValidName("hello<there")).toBeTruthy();
expect(!isValidName("hello>there")).toBeTruthy();
});
describe("anchor refs", () => {
test("parseToRef bare anchor", () => {
expect(parseToRef("$pete")).toEqual({
path: "",
details: { type: "anchor", name: "pete" },
});
});
test("parseToRef page-qualified anchor", () => {
expect(parseToRef("Some Page$pete")).toEqual({
path: "Some Page.md",
details: { type: "anchor", name: "pete" },
});
});
test("parseToRef accepts allowed character classes", () => {
for (const name of ["toc1", "tasks/7", "a:b", "a-b", "_foo"]) {
const ref = parseToRef(`$${name}`);
expect(ref?.details).toEqual({ type: "anchor", name });
}
});
test("parseToRef rejects digit-leading anchors", () => {
// "$100" should NOT be parsed as an anchor; falling through to
// the path branch should fail because the path would be "$100"
// which is not a valid path either.
expect(parseToRef("$100")).toBeNull();
});
test("encodeRef round-trips bare anchor", () => {
expect(encodeRef({ path: "", details: { type: "anchor", name: "pete" } }))
.toBe("$pete");
});
test("encodeRef round-trips page-qualified anchor", () => {
expect(
encodeRef({
path: "Some Page.md",
details: { type: "anchor", name: "pete" },
}),
).toBe("Some Page$pete");
});
});
test("Page URI encoding", () => {
expect(encodePageURI("foo")).toEqual("foo");
expect(encodePageURI("folder/foo")).toEqual("folder/foo");
expect(encodePageURI("hello there")).toEqual("hello%20there");
expect(encodePageURI("hello?there")).toEqual("hello%3Fthere");
// Now ensure all these cases are reversible
expect(decodePageURI("foo")).toEqual("foo");
expect(decodePageURI("folder/foo")).toEqual("folder/foo");
expect(decodePageURI("hello%20there")).toEqual("hello there");
expect(decodePageURI("hello%3Fthere")).toEqual("hello?there");
});