learn and share notes on atproto (wip) 馃
malfestio.stormlightlabs.org/
readability
solid
axum
atproto
srs
1import type { Note } from "$lib/model";
2import type { WikiLink } from "$lib/wikilink";
3import { cleanup, render, screen } from "@solidjs/testing-library";
4import { JSX } from "solid-js";
5import { afterEach, describe, expect, it, vi } from "vitest";
6import { WikilinksPanel } from "../WikilinksPanel";
7
8vi.mock(
9 "@solidjs/router",
10 () => ({ A: (props: { href: string; children: JSX.Element }) => <a href={props.href}>{props.children}</a> }),
11);
12
13describe("WikilinksPanel", () => {
14 afterEach(() => {
15 cleanup();
16 vi.clearAllMocks();
17 });
18
19 const mockNotes: Note[] = [{
20 id: "note-1",
21 owner_did: "did:plc:test",
22 title: "Existing Note",
23 body: "Content",
24 tags: [],
25 visibility: { type: "Private" },
26 created_at: "2026-01-01T00:00:00Z",
27 updated_at: "2026-01-01T00:00:00Z",
28 }];
29
30 const mockLinks: WikiLink[] = [{ raw: "[[Existing Note]]", title: "Existing Note", start: 0, end: 17 }, {
31 raw: "[[Missing Note]]",
32 title: "Missing Note",
33 start: 20,
34 end: 36,
35 }, { raw: "[[Aliased|Display]]", title: "Aliased", alias: "Display", start: 40, end: 59 }];
36
37 const resolveNote = (title: string) => mockNotes.find((n) => n.title === title) ?? null;
38
39 it("renders heading title", () => {
40 render(() => <WikilinksPanel links={[]} notes={[]} resolveNote={() => null} />);
41 expect(screen.getByText("Wikilinks")).toBeInTheDocument();
42 });
43
44 it("shows empty state when no links", () => {
45 render(() => <WikilinksPanel links={[]} notes={[]} resolveNote={() => null} />);
46 expect(screen.getByText("No outgoing links")).toBeInTheDocument();
47 });
48
49 it("renders resolved links as navigation links", () => {
50 render(() => <WikilinksPanel links={mockLinks} notes={mockNotes} resolveNote={resolveNote} />);
51 const existingLink = screen.getByRole("link", { name: /Existing Note/ });
52 expect(existingLink).toHaveAttribute("href", "/notes/note-1");
53 });
54
55 it("renders unresolved links with strikethrough", () => {
56 render(() => <WikilinksPanel links={mockLinks} notes={mockNotes} resolveNote={resolveNote} />);
57 const missingLink = screen.getByText("Missing Note");
58 expect(missingLink).toHaveClass("line-through");
59 });
60
61 it("deduplicates links by title", () => {
62 const duplicateLinks: WikiLink[] = [{ raw: "[[Same]]", title: "Same", start: 0, end: 8 }, {
63 raw: "[[Same]]",
64 title: "Same",
65 start: 10,
66 end: 18,
67 }];
68
69 render(() => <WikilinksPanel links={duplicateLinks} notes={[]} resolveNote={() => null} />);
70 const sameLinks = screen.getAllByText("Same");
71 expect(sameLinks).toHaveLength(1);
72 });
73});