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