mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 16:19:01 +00:00
168 lines
5.3 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|