learn and share notes on atproto (wip) 馃
malfestio.stormlightlabs.org/
readability
solid
axum
atproto
srs
1import { api } from "$lib/api";
2import type { Note } from "$lib/model";
3import { cleanup, render, screen, waitFor } from "@solidjs/testing-library";
4import { JSX } from "solid-js";
5import { afterEach, describe, expect, it, vi } from "vitest";
6import NoteView from "../NoteView";
7
8vi.mock("$lib/api", () => ({ api: { getNote: vi.fn(), getNotes: vi.fn() } }));
9
10vi.mock(
11 "@solidjs/router",
12 () => ({
13 useParams: () => ({ id: "note-1" }),
14 A: (props: { href: string; children: JSX.Element }) => <a href={props.href}>{props.children}</a>,
15 }),
16);
17
18const mockNote: Note = {
19 id: "note-1",
20 owner_did: "did:plc:test123",
21 title: "Test Note Title",
22 body: "# Heading\n\nSome **markdown** content with [[Other Note]].",
23 tags: ["rust", "learning"],
24 visibility: { type: "Public" },
25 created_at: "2026-01-01T10:00:00Z",
26 updated_at: "2026-01-01T12:00:00Z",
27};
28
29const mockAllNotes: Note[] = [mockNote, {
30 id: "note-2",
31 owner_did: "did:plc:test123",
32 title: "Other Note",
33 body: "Links back to [[Test Note Title]]",
34 tags: [],
35 visibility: { type: "Private" },
36 created_at: "2026-01-01T10:00:00Z",
37 updated_at: "2026-01-01T12:00:00Z",
38}];
39
40describe("NoteView", () => {
41 afterEach(() => {
42 cleanup();
43 vi.clearAllMocks();
44 });
45
46 it("renders note title in heading", async () => {
47 vi.mocked(api.getNote).mockResolvedValue(
48 { ok: true, json: () => Promise.resolve(mockNote) } as unknown as Response,
49 );
50 vi.mocked(api.getNotes).mockResolvedValue(
51 { ok: true, json: () => Promise.resolve(mockAllNotes) } as unknown as Response,
52 );
53
54 render(() => <NoteView />);
55
56 await waitFor(() => {
57 expect(screen.getByRole("heading", { level: 1, name: "Test Note Title" })).toBeInTheDocument();
58 });
59 });
60
61 it("renders tags", async () => {
62 vi.mocked(api.getNote).mockResolvedValue(
63 { ok: true, json: () => Promise.resolve(mockNote) } as unknown as Response,
64 );
65 vi.mocked(api.getNotes).mockResolvedValue(
66 { ok: true, json: () => Promise.resolve(mockAllNotes) } as unknown as Response,
67 );
68
69 render(() => <NoteView />);
70
71 await waitFor(() => {
72 expect(screen.getAllByText("rust").length).toBeGreaterThan(0);
73 expect(screen.getAllByText("learning").length).toBeGreaterThan(0);
74 });
75 });
76
77 it("has back to notes link", async () => {
78 vi.mocked(api.getNote).mockResolvedValue(
79 { ok: true, json: () => Promise.resolve(mockNote) } as unknown as Response,
80 );
81 vi.mocked(api.getNotes).mockResolvedValue(
82 { ok: true, json: () => Promise.resolve(mockAllNotes) } as unknown as Response,
83 );
84
85 render(() => <NoteView />);
86
87 await waitFor(() => {
88 expect(screen.getByRole("link", { name: "Notes" })).toBeInTheDocument();
89 });
90 });
91
92 it("renders not found state when note returns error", async () => {
93 vi.mocked(api.getNote).mockResolvedValue({ ok: false } as unknown as Response);
94 vi.mocked(api.getNotes).mockResolvedValue({ ok: true, json: () => Promise.resolve([]) } as unknown as Response);
95
96 render(() => <NoteView />);
97
98 await waitFor(() => {
99 expect(screen.getByText("Note not found")).toBeInTheDocument();
100 });
101 });
102
103 it("renders outline panel heading", async () => {
104 vi.mocked(api.getNote).mockResolvedValue(
105 { ok: true, json: () => Promise.resolve(mockNote) } as unknown as Response,
106 );
107 vi.mocked(api.getNotes).mockResolvedValue(
108 { ok: true, json: () => Promise.resolve(mockAllNotes) } as unknown as Response,
109 );
110
111 render(() => <NoteView />);
112
113 await waitFor(() => {
114 expect(screen.getByText("Outline")).toBeInTheDocument();
115 });
116 });
117
118 it("renders wikilinks panel heading", async () => {
119 vi.mocked(api.getNote).mockResolvedValue(
120 { ok: true, json: () => Promise.resolve(mockNote) } as unknown as Response,
121 );
122 vi.mocked(api.getNotes).mockResolvedValue(
123 { ok: true, json: () => Promise.resolve(mockAllNotes) } as unknown as Response,
124 );
125
126 render(() => <NoteView />);
127
128 await waitFor(() => {
129 expect(screen.getByText("Wikilinks")).toBeInTheDocument();
130 });
131 });
132
133 it("renders backlinks panel heading", async () => {
134 vi.mocked(api.getNote).mockResolvedValue(
135 { ok: true, json: () => Promise.resolve(mockNote) } as unknown as Response,
136 );
137 vi.mocked(api.getNotes).mockResolvedValue(
138 { ok: true, json: () => Promise.resolve(mockAllNotes) } as unknown as Response,
139 );
140
141 render(() => <NoteView />);
142
143 await waitFor(() => {
144 expect(screen.getByText("Backlinks")).toBeInTheDocument();
145 });
146 });
147});