import { beforeEach, describe, expect, it } from "vitest"; import { fireEvent, render, screen, waitFor } from "@testing-library/svelte"; import Dashboard from "../routes/Dashboard.svelte"; import { clearMocks, jsonResponse, mockData, mockEndpoint, setupAuthenticatedUser, setupFetchMock, setupUnauthenticatedUser, } from "./mocks.ts"; const STORAGE_KEY = "tranquil_pds_session"; describe("Dashboard", () => { beforeEach(() => { clearMocks(); setupFetchMock(); }); describe("authentication guard", () => { it("redirects to login when not authenticated", async () => { setupUnauthenticatedUser(); render(Dashboard); await waitFor(() => { expect(globalThis.location.pathname).toBe("/app/login"); }); }); it("shows loading state while checking auth", () => { const { container } = render(Dashboard); expect(container.querySelector(".skeleton-header")).toBeInTheDocument(); expect(container.querySelectorAll(".skeleton-nav-item").length) .toBeGreaterThan(0); }); }); describe("authenticated view", () => { beforeEach(() => { setupAuthenticatedUser(); }); it("displays user account info and sidebar structure", async () => { render(Dashboard); await waitFor(() => { expect(screen.getAllByText(/@testuser\.test\.tranquil\.dev/).length) .toBeGreaterThan(0); expect(screen.getByText(/did:web:test\.tranquil\.dev:u:testuser/)) .toBeInTheDocument(); expect(screen.getByText("Verified")).toBeInTheDocument(); expect(screen.getByText("Verified")).toHaveClass("badge", "success"); }); }); it("displays unverified badge when email not confirmed", async () => { setupAuthenticatedUser({ emailConfirmed: false }); render(Dashboard); await waitFor(() => { expect(screen.getByText("Unverified")).toBeInTheDocument(); expect(screen.getByText("Unverified")).toHaveClass("badge", "warning"); }); }); it("displays sidebar navigation items", async () => { render(Dashboard); await waitFor(() => { const navItems = [ /general/i, /security/i, /sessions/i, /app passwords/i, ]; for (const name of navItems) { expect(screen.getByRole("button", { name })).toBeInTheDocument(); } }); }); it("displays invite codes nav when invites are required and user is admin", async () => { setupAuthenticatedUser({ isAdmin: true }); mockEndpoint( "com.atproto.server.describeServer", () => jsonResponse(mockData.describeServer({ inviteCodeRequired: true })), ); render(Dashboard); await waitFor(() => { expect(screen.getByRole("button", { name: /invite codes/i })) .toBeInTheDocument(); }); }); }); describe("logout functionality", () => { beforeEach(() => { setupAuthenticatedUser(); localStorage.setItem(STORAGE_KEY, JSON.stringify(mockData.session())); mockEndpoint("/oauth/revoke", () => jsonResponse({})); }); it("calls oauth revoke and navigates to login on logout", async () => { let revokeCalled = false; mockEndpoint("/oauth/revoke", () => { revokeCalled = true; return jsonResponse({}); }); render(Dashboard); await waitFor(() => { expect(screen.getByRole("button", { name: /@testuser/i })) .toBeInTheDocument(); }); await fireEvent.click(screen.getByRole("button", { name: /@testuser/i })); await waitFor(() => { expect(screen.getByRole("button", { name: /sign out/i })) .toBeInTheDocument(); }); await fireEvent.click(screen.getByRole("button", { name: /sign out/i })); await waitFor(() => { expect(revokeCalled).toBe(true); expect(globalThis.location.pathname).toBe("/app/login"); }); }); it("clears session from localStorage after logout", async () => { const storedSession = localStorage.getItem(STORAGE_KEY); expect(storedSession).not.toBeNull(); render(Dashboard); await waitFor(() => { expect(screen.getByRole("button", { name: /@testuser/i })) .toBeInTheDocument(); }); await fireEvent.click(screen.getByRole("button", { name: /@testuser/i })); await waitFor(() => { expect(screen.getByRole("button", { name: /sign out/i })) .toBeInTheDocument(); }); await fireEvent.click(screen.getByRole("button", { name: /sign out/i })); await waitFor(() => { expect(localStorage.getItem(STORAGE_KEY)).toBeNull(); }); }); }); });