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 errorResponse, 7 jsonResponse, 8 mockData, 9 mockEndpoint, 10 setupFetchMock, 11} from "./mocks"; 12describe("Login", () => { 13 beforeEach(() => { 14 clearMocks(); 15 setupFetchMock(); 16 window.location.hash = ""; 17 }); 18 describe("initial render", () => { 19 it("renders login form with all elements and correct initial state", () => { 20 render(Login); 21 expect(screen.getByRole("heading", { name: /sign in/i })) 22 .toBeInTheDocument(); 23 expect(screen.getByLabelText(/handle or email/i)).toBeInTheDocument(); 24 expect(screen.getByLabelText(/password/i)).toBeInTheDocument(); 25 expect(screen.getByRole("button", { name: /sign in/i })) 26 .toBeInTheDocument(); 27 expect(screen.getByRole("button", { name: /sign in/i })).toBeDisabled(); 28 expect(screen.getByText(/don't have an account/i)).toBeInTheDocument(); 29 expect(screen.getByRole("link", { name: /create one/i })).toHaveAttribute( 30 "href", 31 "#/register", 32 ); 33 }); 34 }); 35 describe("form validation", () => { 36 it("enables submit button only when both fields are filled", async () => { 37 render(Login); 38 const identifierInput = screen.getByLabelText(/handle or email/i); 39 const passwordInput = screen.getByLabelText(/password/i); 40 const submitButton = screen.getByRole("button", { name: /sign in/i }); 41 await fireEvent.input(identifierInput, { target: { value: "testuser" } }); 42 expect(submitButton).toBeDisabled(); 43 await fireEvent.input(identifierInput, { target: { value: "" } }); 44 await fireEvent.input(passwordInput, { 45 target: { value: "password123" }, 46 }); 47 expect(submitButton).toBeDisabled(); 48 await fireEvent.input(identifierInput, { target: { value: "testuser" } }); 49 expect(submitButton).not.toBeDisabled(); 50 }); 51 }); 52 describe("login submission", () => { 53 it("calls createSession with correct credentials", async () => { 54 let capturedBody: Record<string, string> | null = null; 55 mockEndpoint("com.atproto.server.createSession", (_url, options) => { 56 capturedBody = JSON.parse((options?.body as string) || "{}"); 57 return jsonResponse(mockData.session()); 58 }); 59 render(Login); 60 await fireEvent.input(screen.getByLabelText(/handle or email/i), { 61 target: { value: "testuser@example.com" }, 62 }); 63 await fireEvent.input(screen.getByLabelText(/password/i), { 64 target: { value: "mypassword" }, 65 }); 66 await fireEvent.click(screen.getByRole("button", { name: /sign in/i })); 67 await waitFor(() => { 68 expect(capturedBody).toEqual({ 69 identifier: "testuser@example.com", 70 password: "mypassword", 71 }); 72 }); 73 }); 74 it("shows styled error message on invalid credentials", async () => { 75 mockEndpoint( 76 "com.atproto.server.createSession", 77 () => 78 errorResponse( 79 "AuthenticationRequired", 80 "Invalid identifier or password", 81 401, 82 ), 83 ); 84 render(Login); 85 await fireEvent.input(screen.getByLabelText(/handle or email/i), { 86 target: { value: "wronguser" }, 87 }); 88 await fireEvent.input(screen.getByLabelText(/password/i), { 89 target: { value: "wrongpassword" }, 90 }); 91 await fireEvent.click(screen.getByRole("button", { name: /sign in/i })); 92 await waitFor(() => { 93 const errorDiv = screen.getByText(/invalid identifier or password/i); 94 expect(errorDiv).toBeInTheDocument(); 95 expect(errorDiv).toHaveClass("error"); 96 }); 97 }); 98 it("navigates to dashboard on successful login", async () => { 99 mockEndpoint( 100 "com.atproto.server.createSession", 101 () => jsonResponse(mockData.session()), 102 ); 103 render(Login); 104 await fireEvent.input(screen.getByLabelText(/handle or email/i), { 105 target: { value: "test" }, 106 }); 107 await fireEvent.input(screen.getByLabelText(/password/i), { 108 target: { value: "password" }, 109 }); 110 await fireEvent.click(screen.getByRole("button", { name: /sign in/i })); 111 await waitFor(() => { 112 expect(window.location.hash).toBe("#/dashboard"); 113 }); 114 }); 115 }); 116 describe("account verification flow", () => { 117 it("shows verification form with all controls when account is not verified", async () => { 118 mockEndpoint("com.atproto.server.createSession", () => ({ 119 ok: false, 120 status: 401, 121 json: async () => ({ 122 error: "AccountNotVerified", 123 message: "Account not verified", 124 did: "did:web:test.tranquil.dev:u:testuser", 125 }), 126 })); 127 render(Login); 128 await fireEvent.input(screen.getByLabelText(/handle or email/i), { 129 target: { value: "unverified@test.com" }, 130 }); 131 await fireEvent.input(screen.getByLabelText(/password/i), { 132 target: { value: "password" }, 133 }); 134 await fireEvent.click(screen.getByRole("button", { name: /sign in/i })); 135 await waitFor(() => { 136 expect(screen.getByRole("heading", { name: /verify your account/i })) 137 .toBeInTheDocument(); 138 expect(screen.getByLabelText(/verification code/i)).toBeInTheDocument(); 139 expect(screen.getByRole("button", { name: /resend code/i })) 140 .toBeInTheDocument(); 141 expect(screen.getByRole("button", { name: /back to login/i })) 142 .toBeInTheDocument(); 143 }); 144 }); 145 it("returns to login form when clicking back", async () => { 146 mockEndpoint("com.atproto.server.createSession", () => ({ 147 ok: false, 148 status: 401, 149 json: async () => ({ 150 error: "AccountNotVerified", 151 message: "Account not verified", 152 did: "did:web:test.tranquil.dev:u:testuser", 153 }), 154 })); 155 render(Login); 156 await fireEvent.input(screen.getByLabelText(/handle or email/i), { 157 target: { value: "test" }, 158 }); 159 await fireEvent.input(screen.getByLabelText(/password/i), { 160 target: { value: "password" }, 161 }); 162 await fireEvent.click(screen.getByRole("button", { name: /sign in/i })); 163 await waitFor(() => { 164 expect(screen.getByRole("button", { name: /back to login/i })) 165 .toBeInTheDocument(); 166 }); 167 await fireEvent.click( 168 screen.getByRole("button", { name: /back to login/i }), 169 ); 170 await waitFor(() => { 171 expect(screen.getByRole("heading", { name: /sign in/i })) 172 .toBeInTheDocument(); 173 expect(screen.queryByLabelText(/verification code/i)).not 174 .toBeInTheDocument(); 175 }); 176 }); 177 }); 178});