import { screen, waitFor, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { Route, Routes } from "react-router"; import { beforeEach, describe, expect, it, vi } from "vitest"; import LoginPage from "@/app/(auth)/login"; import * as authApi from "@/domain/auth/api/auth-api"; import type { AuthUser } from "@/domain/auth/types/auth-user"; import { renderWithProviders } from "@/test/test-utils"; const { toastSuccessMock } = vi.hoisted(() => ({ toastSuccessMock: vi.fn(), })); const TEST_USER: AuthUser = { token: "test-jwt-token", expiration: new Date(Date.now() + 60 * 60 * 1000).toISOString(), email: "user@seahavenind.com", userRoles: "Admin", phoneNumber: "5551234567", fullname: "Test User", id: 1, }; vi.mock("@/domain/auth/api/auth-api", () => ({ getSession: vi.fn(), login: vi.fn(), logout: vi.fn(), changePassword: vi.fn(), })); vi.mock("react-toastify", () => ({ toast: { success: toastSuccessMock, error: vi.fn(), info: vi.fn(), }, })); function LoginRoutes() { return ( } /> Dashboard} /> ); } describe("LoginPage integration", () => { beforeEach(() => { vi.clearAllMocks(); vi.mocked(authApi.getSession).mockResolvedValue(null); }); it("keeps submit disabled until email and password are valid", async () => { const user = userEvent.setup(); renderWithProviders(, { route: "/login" }); await waitFor(() => { expect(screen.getByRole("heading", { name: "Welcome back" })).toBeInTheDocument(); }); const form = document.querySelector("form"); expect(form).not.toBeNull(); const submitButton = within(form!).getByRole("button", { name: /sign in/i }); const emailInput = within(form!).getByLabelText("Email"); const passwordInput = within(form!).getByLabelText("Password"); expect(submitButton).toBeDisabled(); await user.type(emailInput, "not-an-email"); await user.type(passwordInput, "secret123"); expect(submitButton).toBeDisabled(); await user.clear(emailInput); await user.type(emailInput, "user@seahavenind.com"); expect(submitButton).toBeEnabled(); }); it("logs in successfully and navigates to dashboard", async () => { const user = userEvent.setup(); vi.mocked(authApi.login).mockResolvedValue(TEST_USER); renderWithProviders(, { route: "/login" }); await waitFor(() => { expect(screen.getByLabelText("Email")).toBeInTheDocument(); }); const form = document.querySelector("form"); expect(form).not.toBeNull(); await user.type(within(form!).getByLabelText("Email"), "user@seahavenind.com"); await user.type(within(form!).getByLabelText("Password"), "secret123"); await user.click(within(form!).getByRole("button", { name: /sign in/i })); await waitFor(() => { expect(authApi.login).toHaveBeenCalledWith({ username: "user@seahavenind.com", password: "secret123", }); }); await waitFor(() => { expect(screen.getByTestId("dashboard")).toBeInTheDocument(); }); expect(screen.queryByRole("heading", { name: "Welcome back" })).not.toBeInTheDocument(); expect(toastSuccessMock).toHaveBeenCalledWith("Login successful!"); }); it("shows generic error message when login fails", async () => { const user = userEvent.setup(); vi.mocked(authApi.login).mockRejectedValue(new Error("Invalid credentials")); renderWithProviders(, { route: "/login" }); await waitFor(() => { expect(screen.getByLabelText("Email")).toBeInTheDocument(); }); const form = document.querySelector("form"); expect(form).not.toBeNull(); await user.type(within(form!).getByLabelText("Email"), "user@seahavenind.com"); await user.type(within(form!).getByLabelText("Password"), "wrong-password"); await user.click(within(form!).getByRole("button", { name: /sign in/i })); await waitFor(() => { expect(screen.getByText("Invalid email or password. Please try again.")).toBeInTheDocument(); }); expect(screen.getByRole("heading", { name: "Welcome back" })).toBeInTheDocument(); }); it("shows loading state while login is pending", async () => { const user = userEvent.setup(); let resolveLogin: (value: AuthUser) => void = () => {}; vi.mocked(authApi.login).mockImplementation( () => new Promise((resolve) => { resolveLogin = resolve; }), ); renderWithProviders(, { route: "/login" }); await waitFor(() => { expect(screen.getByLabelText("Email")).toBeInTheDocument(); }); const form = document.querySelector("form"); expect(form).not.toBeNull(); await user.type(within(form!).getByLabelText("Email"), "user@seahavenind.com"); await user.type(within(form!).getByLabelText("Password"), "secret123"); await user.click(within(form!).getByRole("button", { name: /sign in/i })); await waitFor(() => { expect(within(form!).getByRole("button", { name: /signing in/i })).toBeDisabled(); }); resolveLogin(TEST_USER); await waitFor(() => { expect(screen.getByTestId("dashboard")).toBeInTheDocument(); }); }); });