import React from 'react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { vi, type Mock } from 'vitest'; import { MemoryRouter, Route, Routes } from 'react-router-dom'; import Activate from './Activate'; import { api } from '../api'; import { AuthProvider } from '../auth'; vi.mock('../api', () => ({ api: { activate: vi.fn(), }, })); const mockActivate = api.activate as Mock; function renderActivate(token = 'valid-token') { return render( } /> Login Page} /> , ); } beforeEach(() => { mockActivate.mockReset(); }); test('renders password fields', () => { renderActivate(); expect(screen.getByLabelText(/^password$/i)).toBeInTheDocument(); expect(screen.getByLabelText(/confirm password/i)).toBeInTheDocument(); expect(screen.getByRole('button', { name: /activate account/i })).toBeInTheDocument(); }); test('shows error when no token in URL', () => { render( } /> , ); expect(screen.getByText(/no invite token/i)).toBeInTheDocument(); }); test('shows error when passwords do not match', async () => { renderActivate(); fireEvent.change(screen.getByLabelText(/^password$/i), { target: { value: 'password1' } }); fireEvent.change(screen.getByLabelText(/confirm password/i), { target: { value: 'password2' } }); fireEvent.click(screen.getByRole('button', { name: /activate account/i })); expect(await screen.findByText(/passwords do not match/i)).toBeInTheDocument(); }); test('shows error when password too short', async () => { renderActivate(); fireEvent.change(screen.getByLabelText(/^password$/i), { target: { value: 'short' } }); fireEvent.change(screen.getByLabelText(/confirm password/i), { target: { value: 'short' } }); fireEvent.click(screen.getByRole('button', { name: /activate account/i })); expect(await screen.findByText(/at least 8 characters/i)).toBeInTheDocument(); }); test('calls api.activate and navigates to login on success', async () => { mockActivate.mockResolvedValueOnce({ id: 1, name: 'Alice' }); renderActivate('valid-token'); fireEvent.change(screen.getByLabelText(/^password$/i), { target: { value: 'goodpassword' } }); fireEvent.change(screen.getByLabelText(/confirm password/i), { target: { value: 'goodpassword' } }); fireEvent.click(screen.getByRole('button', { name: /activate account/i })); await waitFor(() => { expect(mockActivate).toHaveBeenCalledWith('valid-token', 'goodpassword'); }); expect(await screen.findByText(/login page/i)).toBeInTheDocument(); }); test('shows error when api.activate fails', async () => { mockActivate.mockRejectedValueOnce(new Error('invalid or expired invite token')); renderActivate('bad-token'); fireEvent.change(screen.getByLabelText(/^password$/i), { target: { value: 'goodpassword' } }); fireEvent.change(screen.getByLabelText(/confirm password/i), { target: { value: 'goodpassword' } }); fireEvent.click(screen.getByRole('button', { name: /activate account/i })); expect(await screen.findByText(/invalid or expired invite token/i)).toBeInTheDocument(); });