learn and share notes on atproto (wip) 馃
malfestio.stormlightlabs.org/
readability
solid
axum
atproto
srs
1import "fake-indexeddb/auto";
2import { api } from "$lib/api";
3import type { Note } from "$lib/model";
4import { MemoryRouter, Route } from "@solidjs/router";
5import { cleanup, render, screen, waitFor } from "@solidjs/testing-library";
6import { afterEach, describe, expect, it, type Mock, vi } from "vitest";
7import Notes from "../Notes";
8
9vi.mock("$lib/api", () => ({ api: { getNotes: vi.fn() } }));
10vi.mock("$lib/store", () => ({ authStore: { user: vi.fn(() => ({ did: "did:plc:test" })) } }));
11vi.mock("$lib/sync-store", () => ({ syncStore: { getLocalNotes: vi.fn().mockResolvedValue([]) } }));
12vi.mock("$lib/density-context", () => ({ useDensity: vi.fn(() => "comfortable") }));
13
14const mockNotes: Note[] = [{
15 id: "note-1",
16 owner_did: "did:plc:test123",
17 title: "First Note",
18 body: "Content of first note",
19 tags: ["rust"],
20 visibility: { type: "Private" },
21 created_at: "2026-01-01T10:00:00Z",
22 updated_at: "2026-01-01T12:00:00Z",
23}, {
24 id: "note-2",
25 owner_did: "did:plc:test123",
26 title: "Second Note",
27 body: "Content of second note",
28 tags: ["learning"],
29 visibility: { type: "Public" },
30 created_at: "2026-01-01T11:00:00Z",
31 updated_at: "2026-01-01T13:00:00Z",
32}];
33
34describe("Notes page", () => {
35 afterEach(() => {
36 cleanup();
37 vi.clearAllMocks();
38 });
39
40 it("renders page header", async () => {
41 (api.getNotes as Mock).mockResolvedValue({ ok: true, json: async () => mockNotes });
42 render(() => (
43 <MemoryRouter>
44 <Route path="/" component={Notes} />
45 </MemoryRouter>
46 ));
47 expect(screen.getByRole("heading", { name: "Notes" })).toBeInTheDocument();
48 expect(screen.getByText("Your personal knowledge base")).toBeInTheDocument();
49 });
50
51 it("renders notes from API", async () => {
52 (api.getNotes as Mock).mockResolvedValue({ ok: true, json: async () => mockNotes });
53 render(() => (
54 <MemoryRouter>
55 <Route path="/" component={Notes} />
56 </MemoryRouter>
57 ));
58 await waitFor(() => {
59 expect(screen.getByText("First Note")).toBeInTheDocument();
60 expect(screen.getByText("Second Note")).toBeInTheDocument();
61 });
62 });
63
64 it("shows empty state when no notes", async () => {
65 (api.getNotes as Mock).mockResolvedValue({ ok: true, json: async () => [] });
66 render(() => (
67 <MemoryRouter>
68 <Route path="/" component={Notes} />
69 </MemoryRouter>
70 ));
71 await waitFor(() => {
72 expect(screen.getByText("No notes yet")).toBeInTheDocument();
73 });
74 });
75
76 it("has New Note button", () => {
77 (api.getNotes as Mock).mockResolvedValue({ ok: true, json: async () => [] });
78 render(() => (
79 <MemoryRouter>
80 <Route path="/" component={Notes} />
81 </MemoryRouter>
82 ));
83 expect(screen.getByRole("link", { name: /new note/i })).toBeInTheDocument();
84 });
85
86 it("has search input", () => {
87 (api.getNotes as Mock).mockResolvedValue({ ok: true, json: async () => [] });
88 render(() => (
89 <MemoryRouter>
90 <Route path="/" component={Notes} />
91 </MemoryRouter>
92 ));
93 expect(screen.getByPlaceholderText("Search notes...")).toBeInTheDocument();
94 });
95});