this repo has no description
1import { beforeEach, describe, expect, it, vi } from "vitest"; 2import { fireEvent, render, screen, waitFor } from "@testing-library/svelte"; 3import Login from "../routes/Login.svelte"; 4import { 5 clearMocks, 6 jsonResponse, 7 mockData, 8 mockEndpoint, 9 setupFetchMock, 10} from "./mocks"; 11import { _testSetState, type SavedAccount } from "../lib/auth.svelte"; 12 13describe("Login", () => { 14 beforeEach(() => { 15 clearMocks(); 16 setupFetchMock(); 17 globalThis.location.hash = ""; 18 mockEndpoint("/oauth/par", () => 19 jsonResponse({ request_uri: "urn:mock:request" }) 20 ); 21 }); 22 23 describe("initial render with no saved accounts", () => { 24 beforeEach(() => { 25 _testSetState({ 26 session: null, 27 loading: false, 28 error: null, 29 savedAccounts: [], 30 }); 31 }); 32 33 it("renders login page with title and OAuth button", async () => { 34 render(Login); 35 await waitFor(() => { 36 expect(screen.getByRole("heading", { name: /sign in/i })) 37 .toBeInTheDocument(); 38 expect(screen.getByRole("button", { name: /sign in/i })) 39 .toBeInTheDocument(); 40 }); 41 }); 42 43 it("shows create account link", async () => { 44 render(Login); 45 await waitFor(() => { 46 expect(screen.getByText(/don't have an account/i)).toBeInTheDocument(); 47 expect(screen.getByRole("link", { name: /create/i })).toHaveAttribute( 48 "href", 49 "#/register", 50 ); 51 }); 52 }); 53 54 it("shows forgot password and lost passkey links", async () => { 55 render(Login); 56 await waitFor(() => { 57 expect(screen.getByRole("link", { name: /forgot password/i })) 58 .toHaveAttribute("href", "#/reset-password"); 59 expect(screen.getByRole("link", { name: /lost passkey/i })) 60 .toHaveAttribute("href", "#/request-passkey-recovery"); 61 }); 62 }); 63 }); 64 65 describe("with saved accounts", () => { 66 const savedAccounts: SavedAccount[] = [ 67 { 68 did: "did:web:test.tranquil.dev:u:alice", 69 handle: "alice.test.tranquil.dev", 70 accessJwt: "mock-jwt-alice", 71 refreshJwt: "mock-refresh-alice", 72 }, 73 { 74 did: "did:web:test.tranquil.dev:u:bob", 75 handle: "bob.test.tranquil.dev", 76 accessJwt: "mock-jwt-bob", 77 refreshJwt: "mock-refresh-bob", 78 }, 79 ]; 80 81 beforeEach(() => { 82 _testSetState({ 83 session: null, 84 loading: false, 85 error: null, 86 savedAccounts, 87 }); 88 mockEndpoint("com.atproto.server.getSession", () => 89 jsonResponse(mockData.session({ handle: "alice.test.tranquil.dev" }))); 90 }); 91 92 it("displays saved accounts list", async () => { 93 render(Login); 94 await waitFor(() => { 95 expect(screen.getByText(/@alice\.test\.tranquil\.dev/)) 96 .toBeInTheDocument(); 97 expect(screen.getByText(/@bob\.test\.tranquil\.dev/)) 98 .toBeInTheDocument(); 99 }); 100 }); 101 102 it("shows sign in to another account option", async () => { 103 render(Login); 104 await waitFor(() => { 105 expect(screen.getByText(/sign in to another/i)).toBeInTheDocument(); 106 }); 107 }); 108 109 it("can click on saved account to switch", async () => { 110 render(Login); 111 await waitFor(() => { 112 expect(screen.getByText(/@alice\.test\.tranquil\.dev/)) 113 .toBeInTheDocument(); 114 }); 115 const aliceAccount = screen.getByText(/@alice\.test\.tranquil\.dev/) 116 .closest("[role='button']"); 117 if (aliceAccount) { 118 await fireEvent.click(aliceAccount); 119 } 120 await waitFor(() => { 121 expect(globalThis.location.hash).toBe("#/dashboard"); 122 }); 123 }); 124 125 it("can remove saved account with forget button", async () => { 126 render(Login); 127 await waitFor(() => { 128 expect(screen.getByText(/@alice\.test\.tranquil\.dev/)) 129 .toBeInTheDocument(); 130 const forgetButtons = screen.getAllByTitle(/remove/i); 131 expect(forgetButtons.length).toBe(2); 132 }); 133 }); 134 }); 135 136 describe("error handling", () => { 137 it("displays error message when auth state has error", async () => { 138 _testSetState({ 139 session: null, 140 loading: false, 141 error: "OAuth login failed", 142 savedAccounts: [], 143 }); 144 render(Login); 145 await waitFor(() => { 146 expect(screen.getByText(/oauth login failed/i)).toBeInTheDocument(); 147 expect(screen.getByText(/oauth login failed/i)).toHaveClass("error"); 148 }); 149 }); 150 }); 151 152 describe("verification flow", () => { 153 beforeEach(() => { 154 _testSetState({ 155 session: null, 156 loading: false, 157 error: null, 158 savedAccounts: [], 159 }); 160 }); 161 162 it("shows verification form when pending verification exists", async () => { 163 render(Login); 164 }); 165 }); 166 167 describe("loading state", () => { 168 it("shows loading state while auth is initializing", async () => { 169 _testSetState({ 170 session: null, 171 loading: true, 172 error: null, 173 savedAccounts: [], 174 }); 175 render(Login); 176 }); 177 }); 178});