import { describe, it, expect, beforeEach, vi } from 'vitest'; import { screen, fireEvent, waitFor } from '@testing-library/react'; import { renderWithProviders } from '../test/utils'; vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } })); vi.mock('../lib/api', () => ({ api: { get: vi.fn(), post: vi.fn(), patch: vi.fn(), put: vi.fn(), delete: vi.fn() }, ApiError: class extends Error {}, })); import { api } from '../lib/api'; import PatientRecordFormPage from './PatientRecordFormPage'; /** جنسیت را از SearchableSelect انتخاب می‌کند (شناسهٔ ورودی، نه placeholder). */ async function pickGender(optionLabel: string) { // فلاشِ رندرهای در صف (کوئریِ تنظیمات)؛ رندرِ وسط تعامل، منوی react-select را می‌بندد. await waitFor(() => expect(document.getElementById('patient-gender-select')).toBeInTheDocument()); await new Promise((r) => setTimeout(r, 0)); const input = document.getElementById('patient-gender-select') as HTMLInputElement; fireEvent.focus(input); fireEvent.keyDown(input, { key: 'ArrowDown' }); fireEvent.click(await screen.findByText(optionLabel)); } const get = api.get as ReturnType; const post = api.post as ReturnType; const patch = api.patch as ReturnType; beforeEach(() => { get.mockReset(); post.mockReset(); patch.mockReset(); // صفحه الگوی شمارهٔ پرونده را می‌خواند؛ بدون mock، ردِ همان کوئری وسط تعامل // یک رندر می‌آورد و منوی باز react-select را می‌بندد. get.mockResolvedValue({ success: true, data: { enabled: false, pattern: '{YY}-{SEQ:4}', reset_policy: 'none', counter: 0, next_preview: '05-0001', can_edit: true }, }); post.mockResolvedValue({ success: true, data: { uuid: 'new-1' } }); patch.mockResolvedValue({ success: true, data: {} }); }); describe('PatientRecordFormPage (تشکیل پرونده)', () => { it('creates a record then patches the demographic fields', async () => { renderWithProviders(, { route: '/admin/patients/new' }); // اول تنظیمات الگو برسد؛ رندرِ بعد از پاسخ، منوی باز react-select را می‌بندد. await waitFor(() => expect(get).toHaveBeenCalled()); fireEvent.change(screen.getByPlaceholderText('نام و نام خانوادگی را وارد نمایید'), { target: { value: 'بیمار نمونه' } }); fireEvent.change(screen.getByPlaceholderText('شماره پرونده'), { target: { value: 'P-1001' } }); await pickGender('زن'); // gender = female fireEvent.change(screen.getByPlaceholderText('کد ملی را وارد نمایید'), { target: { value: '1234567890' } }); fireEvent.change(screen.getByPlaceholderText('شماره تماس را وارد نمایید'), { target: { value: '09120000000' } }); fireEvent.click(screen.getByRole('button', { name: 'ثبت اطلاعات' })); await waitFor(() => expect(post).toHaveBeenCalledWith('/api/v1/patient', expect.objectContaining({ name: 'بیمار نمونه', mobile: '09120000000', national_code: '1234567890', record_number: 'P-1001', }))); await waitFor(() => expect(patch).toHaveBeenCalledWith('/api/v1/patient/new-1', expect.objectContaining({ gender: 'female' }))); }); it('blocks submit and shows a validation error for a bad national code', async () => { renderWithProviders(, { route: '/admin/patients/new' }); // اول تنظیمات الگو برسد؛ رندرِ بعد از پاسخ، منوی باز react-select را می‌بندد. await waitFor(() => expect(get).toHaveBeenCalled()); fireEvent.change(screen.getByPlaceholderText('نام و نام خانوادگی را وارد نمایید'), { target: { value: 'ب' } }); fireEvent.change(screen.getByPlaceholderText('شماره پرونده'), { target: { value: 'P-1' } }); await pickGender('مرد'); fireEvent.change(screen.getByPlaceholderText('کد ملی را وارد نمایید'), { target: { value: '12' } }); fireEvent.change(screen.getByPlaceholderText('شماره تماس را وارد نمایید'), { target: { value: '09120000000' } }); fireEvent.click(screen.getByRole('button', { name: 'ثبت اطلاعات' })); expect(await screen.findByText('کد ملی باید ۱۰ رقم باشد')).toBeInTheDocument(); expect(post).not.toHaveBeenCalled(); }); });