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});