import { describe, it, expect, vi, beforeEach } from 'vitest' import { render, screen, fireEvent, waitFor } from '@testing-library/react' import { MemoryRouter, Route, Routes } from 'react-router-dom' import ResetPassword from '../pages/ResetPassword' vi.mock('../api/client', () => ({ default: { post: vi.fn(), get: vi.fn(), interceptors: { request: { use: vi.fn() }, response: { use: vi.fn() }, }, }, })) import api from '../api/client' const mockNavigate = vi.fn() vi.mock('react-router-dom', async (importOriginal) => { const actual = await importOriginal() return { ...actual, useNavigate: () => mockNavigate, } }) describe('ResetPassword page', () => { beforeEach(() => { vi.clearAllMocks() }) const renderPage = (search = '') => render( } /> ) it('renders form', () => { renderPage('?token=abc') // Use getAllByText since "Новый пароль" appears as h1 and label expect(screen.getAllByText('Новый пароль').length).toBeGreaterThan(0) expect(screen.getByPlaceholderText('Минимум 8 символов')).toBeInTheDocument() expect(screen.getByText('Сохранить пароль')).toBeInTheDocument() }) it('shows error when no token on submit', async () => { renderPage() fireEvent.change(screen.getByPlaceholderText('Минимум 8 символов'), { target: { value: 'newpassword123' }, }) fireEvent.click(screen.getByText('Сохранить пароль')) await waitFor(() => { expect(screen.getByText('Токен не найден')).toBeInTheDocument() }) }) it('shows success state on successful reset', async () => { api.post.mockResolvedValueOnce({}) renderPage('?token=valid-token') fireEvent.change(screen.getByPlaceholderText('Минимум 8 символов'), { target: { value: 'newpassword123' }, }) fireEvent.click(screen.getByText('Сохранить пароль')) await waitFor(() => { expect(screen.getByText('Пароль изменён! 🎉')).toBeInTheDocument() }, { timeout: 3000 }) }) it('shows error on failure', async () => { api.post.mockRejectedValueOnce({ response: { data: { error: 'Token invalid' } } }) renderPage('?token=bad-token') fireEvent.change(screen.getByPlaceholderText('Минимум 8 символов'), { target: { value: 'newpassword123' }, }) fireEvent.click(screen.getByText('Сохранить пароль')) await waitFor(() => { expect(screen.getByText('Token invalid')).toBeInTheDocument() }, { timeout: 3000 }) }) it('toggles password visibility', () => { renderPage('?token=abc') const passwordInput = screen.getByPlaceholderText('Минимум 8 символов') expect(passwordInput.type).toBe('password') const toggleBtn = passwordInput.parentElement.querySelector('button[type="button"]') fireEvent.click(toggleBtn) expect(passwordInput.type).toBe('text') }) it('calls correct API endpoint', async () => { api.post.mockResolvedValueOnce({}) renderPage('?token=mytoken') fireEvent.change(screen.getByPlaceholderText('Минимум 8 символов'), { target: { value: 'mynewpassword' }, }) fireEvent.click(screen.getByText('Сохранить пароль')) await waitFor(() => { expect(api.post).toHaveBeenCalledWith('/auth/reset-password', { token: 'mytoken', new_password: 'mynewpassword', }) }, { timeout: 3000 }) }) })