this repo has no description
1import { beforeEach, describe, expect, it } 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 mockEndpoint( 18 "/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 "/app/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", "/app/reset-password"); 59 expect(screen.getByRole("link", { name: /lost passkey/i })) 60 .toHaveAttribute("href", "/app/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( 89 "com.atproto.server.getSession", 90 () => 91 jsonResponse(mockData.session({ handle: "alice.test.tranquil.dev" })), 92 ); 93 }); 94 95 it("displays saved accounts list", async () => { 96 render(Login); 97 await waitFor(() => { 98 expect(screen.getByText(/@alice\.test\.tranquil\.dev/)) 99 .toBeInTheDocument(); 100 expect(screen.getByText(/@bob\.test\.tranquil\.dev/)) 101 .toBeInTheDocument(); 102 }); 103 }); 104 105 it("shows sign in to another account option", async () => { 106 render(Login); 107 await waitFor(() => { 108 expect(screen.getByText(/sign in to another/i)).toBeInTheDocument(); 109 }); 110 }); 111 112 it("can click on saved account to switch", async () => { 113 render(Login); 114 await waitFor(() => { 115 expect(screen.getByText(/@alice\.test\.tranquil\.dev/)) 116 .toBeInTheDocument(); 117 }); 118 const aliceAccount = screen.getByText(/@alice\.test\.tranquil\.dev/) 119 .closest("[role='button']"); 120 if (aliceAccount) { 121 await fireEvent.click(aliceAccount); 122 } 123 await waitFor(() => { 124 expect(globalThis.location.pathname).toBe("/app/dashboard"); 125 }); 126 }); 127 128 it("can remove saved account with forget button", async () => { 129 render(Login); 130 await waitFor(() => { 131 expect(screen.getByText(/@alice\.test\.tranquil\.dev/)) 132 .toBeInTheDocument(); 133 const forgetButtons = screen.getAllByTitle(/remove/i); 134 expect(forgetButtons.length).toBe(2); 135 }); 136 }); 137 }); 138 139 describe("error handling", () => { 140 it("displays error message when auth state has error", async () => { 141 _testSetState({ 142 session: null, 143 loading: false, 144 error: "OAuth login failed", 145 savedAccounts: [], 146 }); 147 render(Login); 148 await waitFor(() => { 149 expect(screen.getByText(/oauth login failed/i)).toBeInTheDocument(); 150 expect(screen.getByText(/oauth login failed/i)).toHaveClass("error"); 151 }); 152 }); 153 }); 154 155 describe("verification flow", () => { 156 beforeEach(() => { 157 _testSetState({ 158 session: null, 159 loading: false, 160 error: null, 161 savedAccounts: [], 162 }); 163 }); 164 165 it("shows verification form when pending verification exists", () => { 166 render(Login); 167 }); 168 }); 169 170 describe("loading state", () => { 171 it("shows loading state while auth is initializing", () => { 172 _testSetState({ 173 session: null, 174 loading: true, 175 error: null, 176 savedAccounts: [], 177 }); 178 render(Login); 179 }); 180 }); 181});