264 lines
7.4 KiB
TypeScript
264 lines
7.4 KiB
TypeScript
|
|
import React from "react";
|
||
|
|
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
|
||
|
|
import { LoginForm } from "@/components/auth/login-form";
|
||
|
|
import { useAuth, useEmailValidation } from "@/hooks/use-auth";
|
||
|
|
|
||
|
|
// Mock the hooks
|
||
|
|
jest.mock("@/hooks/use-auth");
|
||
|
|
jest.mock("@/service/landing/landing", () => ({
|
||
|
|
listRole: jest.fn().mockResolvedValue({
|
||
|
|
data: {
|
||
|
|
data: [
|
||
|
|
{ id: 1, name: "Admin" },
|
||
|
|
{ id: 2, name: "User" },
|
||
|
|
],
|
||
|
|
},
|
||
|
|
}),
|
||
|
|
}));
|
||
|
|
|
||
|
|
// Mock next-intl
|
||
|
|
jest.mock("next-intl", () => ({
|
||
|
|
useTranslations: () => (key: string, options?: any) => {
|
||
|
|
const defaults = {
|
||
|
|
logInPlease: "Log In Please",
|
||
|
|
acc: "Acc",
|
||
|
|
register: "Register",
|
||
|
|
password: "Password",
|
||
|
|
rememberMe: "Remember Me",
|
||
|
|
forgotPass: "Forgot Pass",
|
||
|
|
next: "Next",
|
||
|
|
categoryReg: "Category Reg",
|
||
|
|
selectOne: "Select One",
|
||
|
|
signIn: "Sign In",
|
||
|
|
};
|
||
|
|
return options?.defaultValue || defaults[key] || key;
|
||
|
|
},
|
||
|
|
}));
|
||
|
|
|
||
|
|
const mockUseAuth = useAuth as jest.MockedFunction<typeof useAuth>;
|
||
|
|
const mockUseEmailValidation = useEmailValidation as jest.MockedFunction<typeof useEmailValidation>;
|
||
|
|
|
||
|
|
describe("LoginForm", () => {
|
||
|
|
const mockLogin = jest.fn();
|
||
|
|
const mockValidateEmail = jest.fn();
|
||
|
|
const mockOnSuccess = jest.fn();
|
||
|
|
const mockOnError = jest.fn();
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
jest.clearAllMocks();
|
||
|
|
|
||
|
|
mockUseAuth.mockReturnValue({
|
||
|
|
login: mockLogin,
|
||
|
|
logout: jest.fn(),
|
||
|
|
refreshToken: jest.fn(),
|
||
|
|
isAuthenticated: false,
|
||
|
|
user: null,
|
||
|
|
loading: false,
|
||
|
|
error: null,
|
||
|
|
});
|
||
|
|
|
||
|
|
mockUseEmailValidation.mockReturnValue({
|
||
|
|
validateEmail: mockValidateEmail,
|
||
|
|
loading: false,
|
||
|
|
error: null,
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it("renders login form with all required fields", () => {
|
||
|
|
render(
|
||
|
|
<LoginForm
|
||
|
|
onSuccess={mockOnSuccess}
|
||
|
|
onError={mockOnError}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(screen.getByText("Log In Please")).toBeInTheDocument();
|
||
|
|
expect(screen.getByLabelText(/username/i)).toBeInTheDocument();
|
||
|
|
expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
|
||
|
|
expect(screen.getByRole("button", { name: /selanjutnya/i })).toBeInTheDocument();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("shows validation errors for invalid input", async () => {
|
||
|
|
render(
|
||
|
|
<LoginForm
|
||
|
|
onSuccess={mockOnSuccess}
|
||
|
|
onError={mockOnError}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
|
||
|
|
const submitButton = screen.getByRole("button", { name: /selanjutnya/i });
|
||
|
|
fireEvent.click(submitButton);
|
||
|
|
|
||
|
|
await waitFor(() => {
|
||
|
|
expect(screen.getByText("Username is required")).toBeInTheDocument();
|
||
|
|
expect(screen.getByText("Password is required")).toBeInTheDocument();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it("handles successful form submission", async () => {
|
||
|
|
mockValidateEmail.mockResolvedValue("success");
|
||
|
|
|
||
|
|
render(
|
||
|
|
<LoginForm
|
||
|
|
onSuccess={mockOnSuccess}
|
||
|
|
onError={mockOnError}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
|
||
|
|
const usernameInput = screen.getByLabelText(/username/i);
|
||
|
|
const passwordInput = screen.getByLabelText(/password/i);
|
||
|
|
const submitButton = screen.getByRole("button", { name: /selanjutnya/i });
|
||
|
|
|
||
|
|
fireEvent.change(usernameInput, { target: { value: "testuser" } });
|
||
|
|
fireEvent.change(passwordInput, { target: { value: "password123" } });
|
||
|
|
fireEvent.click(submitButton);
|
||
|
|
|
||
|
|
await waitFor(() => {
|
||
|
|
expect(mockValidateEmail).toHaveBeenCalledWith({
|
||
|
|
username: "testuser",
|
||
|
|
password: "password123",
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it("handles email validation step", async () => {
|
||
|
|
mockValidateEmail.mockResolvedValue("setup");
|
||
|
|
|
||
|
|
render(
|
||
|
|
<LoginForm
|
||
|
|
onSuccess={mockOnSuccess}
|
||
|
|
onError={mockOnError}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
|
||
|
|
const usernameInput = screen.getByLabelText(/username/i);
|
||
|
|
const passwordInput = screen.getByLabelText(/password/i);
|
||
|
|
const submitButton = screen.getByRole("button", { name: /selanjutnya/i });
|
||
|
|
|
||
|
|
fireEvent.change(usernameInput, { target: { value: "testuser" } });
|
||
|
|
fireEvent.change(passwordInput, { target: { value: "password123" } });
|
||
|
|
fireEvent.click(submitButton);
|
||
|
|
|
||
|
|
await waitFor(() => {
|
||
|
|
expect(mockOnError).toHaveBeenCalledWith("Email setup required");
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it("handles OTP step", async () => {
|
||
|
|
mockValidateEmail.mockResolvedValue("otp");
|
||
|
|
|
||
|
|
render(
|
||
|
|
<LoginForm
|
||
|
|
onSuccess={mockOnSuccess}
|
||
|
|
onError={mockOnError}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
|
||
|
|
const usernameInput = screen.getByLabelText(/username/i);
|
||
|
|
const passwordInput = screen.getByLabelText(/password/i);
|
||
|
|
const submitButton = screen.getByRole("button", { name: /selanjutnya/i });
|
||
|
|
|
||
|
|
fireEvent.change(usernameInput, { target: { value: "testuser" } });
|
||
|
|
fireEvent.change(passwordInput, { target: { value: "password123" } });
|
||
|
|
fireEvent.click(submitButton);
|
||
|
|
|
||
|
|
await waitFor(() => {
|
||
|
|
expect(mockOnError).toHaveBeenCalledWith("OTP verification required");
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it("toggles password visibility", () => {
|
||
|
|
render(
|
||
|
|
<LoginForm
|
||
|
|
onSuccess={mockOnSuccess}
|
||
|
|
onError={mockOnError}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
|
||
|
|
const passwordInput = screen.getByLabelText(/password/i);
|
||
|
|
const toggleButton = screen.getByLabelText(/show password/i);
|
||
|
|
|
||
|
|
expect(passwordInput).toHaveAttribute("type", "password");
|
||
|
|
|
||
|
|
fireEvent.click(toggleButton);
|
||
|
|
expect(passwordInput).toHaveAttribute("type", "text");
|
||
|
|
expect(screen.getByLabelText(/hide password/i)).toBeInTheDocument();
|
||
|
|
|
||
|
|
fireEvent.click(toggleButton);
|
||
|
|
expect(passwordInput).toHaveAttribute("type", "password");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("handles remember me checkbox", () => {
|
||
|
|
render(
|
||
|
|
<LoginForm
|
||
|
|
onSuccess={mockOnSuccess}
|
||
|
|
onError={mockOnError}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
|
||
|
|
const rememberMeCheckbox = screen.getByRole("checkbox", { name: /remember me/i });
|
||
|
|
|
||
|
|
expect(rememberMeCheckbox).toBeChecked();
|
||
|
|
|
||
|
|
fireEvent.click(rememberMeCheckbox);
|
||
|
|
expect(rememberMeCheckbox).not.toBeChecked();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("opens registration dialog", () => {
|
||
|
|
render(
|
||
|
|
<LoginForm
|
||
|
|
onSuccess={mockOnSuccess}
|
||
|
|
onError={mockOnError}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
|
||
|
|
const registerLink = screen.getByText("Register");
|
||
|
|
fireEvent.click(registerLink);
|
||
|
|
|
||
|
|
expect(screen.getByText("Category Reg")).toBeInTheDocument();
|
||
|
|
expect(screen.getByText("Select One")).toBeInTheDocument();
|
||
|
|
expect(screen.getByLabelText("Admin")).toBeInTheDocument();
|
||
|
|
expect(screen.getByLabelText("User")).toBeInTheDocument();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("handles loading state", async () => {
|
||
|
|
mockUseEmailValidation.mockReturnValue({
|
||
|
|
validateEmail: mockValidateEmail,
|
||
|
|
loading: true,
|
||
|
|
error: null,
|
||
|
|
});
|
||
|
|
|
||
|
|
render(
|
||
|
|
<LoginForm
|
||
|
|
onSuccess={mockOnSuccess}
|
||
|
|
onError={mockOnError}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
|
||
|
|
const submitButton = screen.getByRole("button", { name: /processing/i });
|
||
|
|
expect(submitButton).toBeDisabled();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("handles error state", async () => {
|
||
|
|
mockValidateEmail.mockRejectedValue(new Error("Validation failed"));
|
||
|
|
|
||
|
|
render(
|
||
|
|
<LoginForm
|
||
|
|
onSuccess={mockOnSuccess}
|
||
|
|
onError={mockOnError}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
|
||
|
|
const usernameInput = screen.getByLabelText(/username/i);
|
||
|
|
const passwordInput = screen.getByLabelText(/password/i);
|
||
|
|
const submitButton = screen.getByRole("button", { name: /selanjutnya/i });
|
||
|
|
|
||
|
|
fireEvent.change(usernameInput, { target: { value: "testuser" } });
|
||
|
|
fireEvent.change(passwordInput, { target: { value: "password123" } });
|
||
|
|
fireEvent.click(submitButton);
|
||
|
|
|
||
|
|
await waitFor(() => {
|
||
|
|
expect(mockOnError).toHaveBeenCalledWith("Validation failed");
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|