shoc-frontend-new/src/test/app/(auth)/login.integration.test.tsx
2026-07-13 03:10:41 -03:00

168 lines
5.3 KiB
TypeScript

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 (
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/" element={<div data-testid="dashboard">Dashboard</div>} />
</Routes>
);
}
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(<LoginRoutes />, { 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(<LoginRoutes />, { 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(<LoginRoutes />, { 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(<LoginRoutes />, { 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();
});
});
});