Our Personal Data Server from scratch!
0
fork

Configure Feed

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

at main 140 lines 5.4 kB view raw
1import { beforeEach, describe, expect, it } from "vitest"; 2import { fireEvent, render, screen, waitFor } from "@testing-library/svelte"; 3import Dashboard from "../routes/Dashboard.svelte"; 4import { 5 clearMocks, 6 jsonResponse, 7 mockData, 8 mockEndpoint, 9 setupAuthenticatedUser, 10 setupFetchMock, 11 setupUnauthenticatedUser, 12} from "./mocks.ts"; 13const STORAGE_KEY = "tranquil_pds_session"; 14describe("Dashboard", () => { 15 beforeEach(() => { 16 clearMocks(); 17 setupFetchMock(); 18 }); 19 describe("authentication guard", () => { 20 it("redirects to login when not authenticated", async () => { 21 setupUnauthenticatedUser(); 22 render(Dashboard); 23 await waitFor(() => { 24 expect(globalThis.location.pathname).toBe("/app/login"); 25 }); 26 }); 27 it("shows loading state while checking auth", () => { 28 const { container } = render(Dashboard); 29 expect(container.querySelector(".skeleton-section")).toBeInTheDocument(); 30 expect(container.querySelectorAll(".skeleton-card").length) 31 .toBeGreaterThan(0); 32 }); 33 }); 34 describe("authenticated view", () => { 35 beforeEach(() => { 36 setupAuthenticatedUser(); 37 }); 38 it("displays user account info and page structure", async () => { 39 render(Dashboard); 40 await waitFor(() => { 41 expect(screen.getByRole("heading", { name: /dashboard/i })) 42 .toBeInTheDocument(); 43 expect(screen.getByRole("heading", { name: /account overview/i })) 44 .toBeInTheDocument(); 45 expect(screen.getAllByText(/@testuser\.test\.tranquil\.dev/).length) 46 .toBeGreaterThan(0); 47 expect(screen.getByText(/did:web:test\.tranquil\.dev:u:testuser/)) 48 .toBeInTheDocument(); 49 expect(screen.getByText("test@example.com")).toBeInTheDocument(); 50 expect(screen.getByText("Verified")).toBeInTheDocument(); 51 expect(screen.getByText("Verified")).toHaveClass("badge", "success"); 52 }); 53 }); 54 it("displays unverified badge when email not confirmed", async () => { 55 setupAuthenticatedUser({ emailConfirmed: false }); 56 render(Dashboard); 57 await waitFor(() => { 58 expect(screen.getByText("Unverified")).toBeInTheDocument(); 59 expect(screen.getByText("Unverified")).toHaveClass("badge", "warning"); 60 }); 61 }); 62 it("displays all navigation cards", async () => { 63 render(Dashboard); 64 await waitFor(() => { 65 const navCards = [ 66 { name: /app passwords/i, href: "/app/app-passwords" }, 67 { name: /account settings/i, href: "/app/settings" }, 68 { name: /communication preferences/i, href: "/app/comms" }, 69 { name: /repository explorer/i, href: "/app/repo" }, 70 ]; 71 for (const { name, href } of navCards) { 72 const card = screen.getByRole("link", { name }); 73 expect(card).toBeInTheDocument(); 74 expect(card).toHaveAttribute("href", href); 75 } 76 }); 77 }); 78 it("displays invite codes card when invites are required and user is admin", async () => { 79 setupAuthenticatedUser({ isAdmin: true }); 80 mockEndpoint( 81 "com.atproto.server.describeServer", 82 () => 83 jsonResponse(mockData.describeServer({ inviteCodeRequired: true })), 84 ); 85 render(Dashboard); 86 await waitFor(() => { 87 const inviteCard = screen.getByRole("link", { name: /invite codes/i }); 88 expect(inviteCard).toBeInTheDocument(); 89 expect(inviteCard).toHaveAttribute("href", "/app/invite-codes"); 90 }); 91 }); 92 }); 93 describe("logout functionality", () => { 94 beforeEach(() => { 95 setupAuthenticatedUser(); 96 localStorage.setItem(STORAGE_KEY, JSON.stringify(mockData.session())); 97 mockEndpoint("/oauth/revoke", () => jsonResponse({})); 98 }); 99 it("calls oauth revoke and navigates to login on logout", async () => { 100 let revokeCalled = false; 101 mockEndpoint("/oauth/revoke", () => { 102 revokeCalled = true; 103 return jsonResponse({}); 104 }); 105 render(Dashboard); 106 await waitFor(() => { 107 expect(screen.getByRole("button", { name: /@testuser/i })) 108 .toBeInTheDocument(); 109 }); 110 await fireEvent.click(screen.getByRole("button", { name: /@testuser/i })); 111 await waitFor(() => { 112 expect(screen.getByRole("button", { name: /sign out/i })) 113 .toBeInTheDocument(); 114 }); 115 await fireEvent.click(screen.getByRole("button", { name: /sign out/i })); 116 await waitFor(() => { 117 expect(revokeCalled).toBe(true); 118 expect(globalThis.location.pathname).toBe("/app/login"); 119 }); 120 }); 121 it("clears session from localStorage after logout", async () => { 122 const storedSession = localStorage.getItem(STORAGE_KEY); 123 expect(storedSession).not.toBeNull(); 124 render(Dashboard); 125 await waitFor(() => { 126 expect(screen.getByRole("button", { name: /@testuser/i })) 127 .toBeInTheDocument(); 128 }); 129 await fireEvent.click(screen.getByRole("button", { name: /@testuser/i })); 130 await waitFor(() => { 131 expect(screen.getByRole("button", { name: /sign out/i })) 132 .toBeInTheDocument(); 133 }); 134 await fireEvent.click(screen.getByRole("button", { name: /sign out/i })); 135 await waitFor(() => { 136 expect(localStorage.getItem(STORAGE_KEY)).toBeNull(); 137 }); 138 }); 139 }); 140});