import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { useAuthStore } from '@/lib/auth-store'; // Mock next/navigation const mockPush = vi.fn(); const mockSearchParams = new URLSearchParams(); vi.mock('next/navigation', () => ({ useRouter: () => ({ push: mockPush }), useSearchParams: () => mockSearchParams, })); // Mock next/link vi.mock('next/link', () => ({ default: ({ children, href, ...props }: { children: React.ReactNode; href: string; [key: string]: unknown }) => ( {children} ), })); // Mock auth store vi.mock('@/lib/auth-store', () => { const store = { login: vi.fn(), isLoading: false, error: null, clearError: vi.fn(), }; return { useAuthStore: vi.fn((selector) => { if (typeof selector === 'function') return selector(store); return store; }), }; }); import LoginPage from '../login/page'; const mockedUseAuthStore = vi.mocked(useAuthStore); describe('LoginPage', () => { let mockStore: { login: ReturnType; isLoading: boolean; error: string | null; clearError: ReturnType; }; beforeEach(() => { vi.clearAllMocks(); mockStore = { login: vi.fn(), isLoading: false, error: null, clearError: vi.fn(), }; mockedUseAuthStore.mockImplementation((selector) => { if (typeof selector === 'function') return (selector as (s: typeof mockStore) => unknown)(mockStore); return mockStore as ReturnType; }); }); it('renders login form with phone and password fields', () => { render(); expect(screen.getByRole('heading', { name: 'Đăng nhập' })).toBeInTheDocument(); expect(screen.getByLabelText('Số điện thoại')).toBeInTheDocument(); expect(screen.getByLabelText('Mật khẩu')).toBeInTheDocument(); expect(screen.getByRole('button', { name: /đăng nhập/i })).toBeInTheDocument(); }); it('renders OAuth buttons', () => { render(); expect(screen.getByRole('button', { name: /google/i })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /zalo/i })).toBeInTheDocument(); }); it('renders register link', () => { render(); const registerLink = screen.getByRole('link', { name: /đăng ký/i }); expect(registerLink).toHaveAttribute('href', '/register'); }); it('submits form with valid data', async () => { mockStore.login.mockResolvedValue(undefined); render(); await userEvent.type(screen.getByLabelText('Số điện thoại'), '0912345678'); await userEvent.type(screen.getByLabelText('Mật khẩu'), 'password123'); await userEvent.click(screen.getByRole('button', { name: /đăng nhập/i })); await waitFor(() => { expect(mockStore.login).toHaveBeenCalledWith({ phone: '0912345678', password: 'password123', }); }); }); it('shows validation errors for empty fields', async () => { render(); await userEvent.click(screen.getByRole('button', { name: /đăng nhập/i })); await waitFor(() => { const alerts = screen.getAllByRole('alert'); expect(alerts.length).toBeGreaterThan(0); }); }); it('toggles password visibility', async () => { render(); const passwordInput = screen.getByLabelText('Mật khẩu'); expect(passwordInput).toHaveAttribute('type', 'password'); await userEvent.click(screen.getByText('Hiện')); expect(passwordInput).toHaveAttribute('type', 'text'); await userEvent.click(screen.getByText('Ẩn')); expect(passwordInput).toHaveAttribute('type', 'password'); }); it('displays store error message', () => { mockStore.error = 'Sai mật khẩu'; render(); expect(screen.getByText('Sai mật khẩu')).toBeInTheDocument(); }); it('navigates to home after successful login', async () => { mockStore.login.mockResolvedValue(undefined); render(); await userEvent.type(screen.getByLabelText('Số điện thoại'), '0912345678'); await userEvent.type(screen.getByLabelText('Mật khẩu'), 'password123'); await userEvent.click(screen.getByRole('button', { name: /đăng nhập/i })); await waitFor(() => { expect(mockPush).toHaveBeenCalledWith('/'); }); }); });