learn and share notes on atproto (wip) 馃 malfestio.stormlightlabs.org/
readability solid axum atproto srs
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 197 lines 7.6 kB view raw
1import { api } from "$lib/api"; 2import { toast } from "$lib/toast"; 3import { cleanup, fireEvent, render, screen, waitFor, within } from "@solidjs/testing-library"; 4import { JSX } from "solid-js"; 5import { afterEach, describe, expect, it, vi } from "vitest"; 6import DeckView from "../DeckView"; 7 8const { mockNavigate } = vi.hoisted(() => ({ mockNavigate: vi.fn() })); 9 10vi.mock( 11 "$lib/api", 12 () => ({ 13 api: { 14 getDeck: vi.fn(), 15 getDeckCards: vi.fn(), 16 forkDeck: vi.fn(), 17 getComments: vi.fn(), 18 addComment: vi.fn(), 19 getDueCards: vi.fn(), 20 submitReview: vi.fn(), 21 }, 22 }), 23); 24 25vi.mock("$lib/toast", () => ({ toast: { success: vi.fn(), error: vi.fn() } })); 26 27vi.mock( 28 "@solidjs/router", 29 () => ({ 30 useParams: () => ({ id: "123" }), 31 useNavigate: () => mockNavigate, 32 A: (props: { href: string; children: JSX.Element }) => <a href={props.href}>{props.children}</a>, 33 }), 34); 35 36describe("DeckView", () => { 37 afterEach(() => { 38 cleanup(); 39 vi.clearAllMocks(); 40 }); 41 42 const mockDeck = { 43 id: "123", 44 title: "Test Deck", 45 description: "A test deck", 46 tags: ["test"], 47 visibility: { type: "Public" }, 48 owner_did: "did:test", 49 }; 50 51 const mockCards = [{ id: "c1", front: "Front 1", back: "Back 1" }, { id: "c2", front: "Front 2", back: "Back 2" }]; 52 53 it("renders deck details and cards", async () => { 54 vi.mocked(api.getDeck).mockResolvedValue( 55 { ok: true, json: () => Promise.resolve(mockDeck) } as unknown as Response, 56 ); 57 vi.mocked(api.getDeckCards).mockResolvedValue( 58 { ok: true, json: () => Promise.resolve(mockCards) } as unknown as Response, 59 ); 60 vi.mocked(api.getDueCards).mockResolvedValue({ ok: true, json: () => Promise.resolve([]) } as unknown as Response); 61 vi.mocked(api.getComments).mockResolvedValue({ ok: true, json: () => Promise.resolve([]) } as unknown as Response); 62 63 render(() => <DeckView />); 64 65 await waitFor(() => expect(screen.getByText("Test Deck")).toBeInTheDocument()); 66 expect(screen.getByText("A test deck")).toBeInTheDocument(); 67 expect(screen.getByText("#test")).toBeInTheDocument(); 68 expect(screen.getByText("Front 1")).toBeInTheDocument(); 69 }); 70 71 it("handles deck fork flow successfully", async () => { 72 vi.mocked(api.getDeck).mockResolvedValue( 73 { ok: true, json: () => Promise.resolve(mockDeck) } as unknown as Response, 74 ); 75 vi.mocked(api.getDeckCards).mockResolvedValue( 76 { ok: true, json: () => Promise.resolve(mockCards) } as unknown as Response, 77 ); 78 vi.mocked(api.getDueCards).mockResolvedValue({ ok: true, json: () => Promise.resolve([]) } as unknown as Response); 79 vi.mocked(api.forkDeck).mockResolvedValue( 80 { ok: true, json: () => Promise.resolve({ id: "456" }) } as unknown as Response, 81 ); 82 vi.mocked(api.getComments).mockResolvedValue({ ok: true, json: () => Promise.resolve([]) } as unknown as Response); 83 84 render(() => <DeckView />); 85 86 await waitFor(() => expect(screen.getByText("Test Deck")).toBeInTheDocument()); 87 88 const forkButton = screen.getByRole("button", { name: /Fork Deck/i }); 89 fireEvent.click(forkButton); 90 91 const dialog = await screen.findByRole("dialog"); 92 expect(within(dialog).getByText(/Are you sure you want to fork/)).toBeInTheDocument(); 93 94 const confirmButton = within(dialog).getByRole("button", { name: /Fork Deck/i }); 95 fireEvent.click(confirmButton); 96 97 await waitFor(() => { 98 expect(api.forkDeck).toHaveBeenCalledWith("123"); 99 expect(toast.success).toHaveBeenCalledWith("Deck forked successfully!"); 100 expect(mockNavigate).toHaveBeenCalledWith("/decks/456"); 101 }); 102 }); 103 104 it("handles deck fork failure", async () => { 105 vi.mocked(api.getDeck).mockResolvedValue( 106 { ok: true, json: () => Promise.resolve(mockDeck) } as unknown as Response, 107 ); 108 vi.mocked(api.getDeckCards).mockResolvedValue( 109 { ok: true, json: () => Promise.resolve(mockCards) } as unknown as Response, 110 ); 111 vi.mocked(api.getDueCards).mockResolvedValue({ ok: true, json: () => Promise.resolve([]) } as unknown as Response); 112 vi.mocked(api.forkDeck).mockResolvedValue({ ok: false } as unknown as Response); 113 vi.mocked(api.getComments).mockResolvedValue({ ok: true, json: () => Promise.resolve([]) } as unknown as Response); 114 115 render(() => <DeckView />); 116 117 await waitFor(() => expect(screen.getByText("Test Deck")).toBeInTheDocument()); 118 119 const forkButton = screen.getByRole("button", { name: /Fork Deck/i }); 120 fireEvent.click(forkButton); 121 122 const dialog = await screen.findByRole("dialog"); 123 const confirmButton = within(dialog).getByRole("button", { name: /Fork Deck/i }); 124 fireEvent.click(confirmButton); 125 126 await waitFor(() => { 127 expect(api.forkDeck).toHaveBeenCalledWith("123"); 128 expect(toast.error).toHaveBeenCalledWith("Failed to fork deck."); 129 expect(mockNavigate).not.toHaveBeenCalled(); 130 }); 131 }); 132 133 it("renders not found state when deck returns error", async () => { 134 vi.mocked(api.getDeck).mockResolvedValue({ ok: false } as unknown as Response); 135 vi.mocked(api.getDueCards).mockResolvedValue({ ok: true, json: () => Promise.resolve([]) } as unknown as Response); 136 render(() => <DeckView />); 137 await waitFor(() => expect(screen.getByText(/Deck not found/i)).toBeInTheDocument()); 138 }); 139 it("renders study button with due cards count", async () => { 140 vi.mocked(api.getDeck).mockResolvedValue( 141 { ok: true, json: () => Promise.resolve(mockDeck) } as unknown as Response, 142 ); 143 vi.mocked(api.getDeckCards).mockResolvedValue( 144 { ok: true, json: () => Promise.resolve(mockCards) } as unknown as Response, 145 ); 146 vi.mocked(api.getDueCards).mockResolvedValue( 147 { 148 ok: true, 149 json: () => Promise.resolve([{ review_id: "r1", card_id: "c1", deck_id: "123", front: "F", back: "B" }]), 150 } as unknown as Response, 151 ); 152 vi.mocked(api.getComments).mockResolvedValue({ ok: true, json: () => Promise.resolve([]) } as unknown as Response); 153 154 render(() => <DeckView />); 155 156 await waitFor(() => expect(screen.getByText("Test Deck")).toBeInTheDocument()); 157 158 const studyButton = await screen.findByRole("button", { name: /Study Deck \(1 due\)/i }); 159 expect(studyButton).toBeInTheDocument(); 160 expect(studyButton).not.toBeDisabled(); 161 }); 162 163 it("enters study mode when study button is clicked", async () => { 164 vi.mocked(api.getDeck).mockResolvedValue( 165 { ok: true, json: () => Promise.resolve(mockDeck) } as unknown as Response, 166 ); 167 vi.mocked(api.getDeckCards).mockResolvedValue( 168 { ok: true, json: () => Promise.resolve(mockCards) } as unknown as Response, 169 ); 170 vi.mocked(api.getDueCards).mockResolvedValue( 171 { 172 ok: true, 173 json: () => 174 Promise.resolve([{ 175 review_id: "r1", 176 card_id: "c1", 177 deck_id: "123", 178 front: "Study Front", 179 back: "Study Back", 180 deck_title: "Test Deck", 181 hints: [], 182 }]), 183 } as unknown as Response, 184 ); 185 vi.mocked(api.getComments).mockResolvedValue({ ok: true, json: () => Promise.resolve([]) } as unknown as Response); 186 187 render(() => <DeckView />); 188 189 await waitFor(() => expect(screen.getByText("Test Deck")).toBeInTheDocument()); 190 191 const studyButton = await screen.findByRole("button", { name: /Study Deck \(1 due\)/i }); 192 fireEvent.click(studyButton); 193 194 await waitFor(() => expect(screen.getByText("Card 1 of 1")).toBeInTheDocument()); 195 expect(screen.getByText("Study Front")).toBeInTheDocument(); 196 }); 197});