import { describe, it, expect, beforeEach, vi } from 'vitest'; import { screen, fireEvent, waitFor } from '@testing-library/react'; import { renderWithProviders } from '../test/utils'; 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 { useAuthStore } from '../stores/authStore'; import { NewAppointmentModal } from './AppointmentsPage'; const get = api.get as ReturnType; const post = api.post as ReturnType; const slot = { start: 1_800_000_000, end: 1_800_001_800, start_time: '15:00', end_time: '15:30', doctor_uuid: 'doc1', doctor_name: 'دکتر تست' }; beforeEach(() => { get.mockReset(); post.mockReset(); useAuthStore.setState({ primaryRole: 'doctor' } as any); post.mockResolvedValue({ success: true, data: { uuid: 'new1' } }); }); describe('NewAppointmentModal — جستجوی موبایل‌محور', () => { it('submit stays disabled until a mobile search is done', () => { renderWithProviders( {}} onSuccess={() => {}} />); fireEvent.change(screen.getByPlaceholderText('مثال: 09123456789'), { target: { value: '09121234567' } }); // بدون جستجو، ثبت نوبت غیرفعال است expect(screen.getByRole('button', { name: 'ثبت نوبت' })).toBeDisabled(); }); it('converts Persian digits to English and searches by the normalized mobile', async () => { get.mockResolvedValue({ success: true, data: { found: false } }); renderWithProviders( {}} onSuccess={() => {}} />); const input = screen.getByPlaceholderText('مثال: 09123456789') as HTMLInputElement; fireEvent.change(input, { target: { value: '۰۹۱۲۳۴۵۶۷۸۹' } }); expect(input.value).toBe('09123456789'); fireEvent.click(screen.getByRole('button', { name: 'جستجو' })); await waitFor(() => expect(get).toHaveBeenCalledWith('/api/v1/my/appointment/patient-lookup?mobile=09123456789')); }); it('searches on Enter key in the mobile field', async () => { get.mockResolvedValue({ success: true, data: { found: false } }); renderWithProviders( {}} onSuccess={() => {}} />); const input = screen.getByPlaceholderText('مثال: 09123456789'); fireEvent.change(input, { target: { value: '09121234567' } }); fireEvent.keyDown(input, { key: 'Enter' }); await waitFor(() => expect(get).toHaveBeenCalledWith('/api/v1/my/appointment/patient-lookup?mobile=09121234567')); }); it('found patient with national code books directly without extra fields', async () => { get.mockResolvedValue({ success: true, data: { found: true, name: 'علی محمدی', mobile: '09121234567', national_code: '0012345678' } }); renderWithProviders( {}} onSuccess={() => {}} />); fireEvent.change(screen.getByPlaceholderText('مثال: 09123456789'), { target: { value: '09121234567' } }); fireEvent.click(screen.getByRole('button', { name: 'جستجو' })); await screen.findByText('بیمار یافت شد'); expect(screen.getByText('علی محمدی')).toBeInTheDocument(); // فیلد کد ملی برای بیمارِ یافت‌شده نمایش داده نمی‌شود expect(screen.queryByPlaceholderText('کد ملی ۱۰ رقمی')).toBeNull(); fireEvent.click(screen.getByRole('button', { name: 'ثبت نوبت' })); await waitFor(() => expect(post).toHaveBeenCalledWith('/api/v1/my/appointment', expect.objectContaining({ doctor_uuid: 'doc1', patient_mobile: '09121234567', patient_name: 'علی محمدی', patient_national_code: '0012345678', }))); }); it('unknown mobile reveals national code + name fields and books the new patient', async () => { get.mockResolvedValue({ success: true, data: { found: false } }); renderWithProviders( {}} onSuccess={() => {}} />); fireEvent.change(screen.getByPlaceholderText('مثال: 09123456789'), { target: { value: '09990001122' } }); fireEvent.click(screen.getByRole('button', { name: 'جستجو' })); const nc = await screen.findByPlaceholderText('کد ملی ۱۰ رقمی'); fireEvent.change(screen.getByPlaceholderText('مثال: علی محمدی'), { target: { value: 'مریم خلیلی' } }); fireEvent.change(nc, { target: { value: '1234567891' } }); fireEvent.click(screen.getByRole('button', { name: 'ثبت نوبت' })); await waitFor(() => expect(post).toHaveBeenCalledWith('/api/v1/my/appointment', expect.objectContaining({ patient_mobile: '09990001122', patient_name: 'مریم خلیلی', patient_national_code: '1234567891', }))); }); }); describe('NewAppointmentModal — هزینه ویزیت', () => { /** تعرفه را برای همان پزشکِ اسلات برمی‌گرداند؛ بقیهٔ GETها بیمارِ ناشناس. */ function mockPricing(rials: number, requireVisit = false) { get.mockImplementation((url: string) => url.startsWith('/api/v1/insurance-pricing') ? Promise.resolve({ success: true, data: { free_visit_price_rials: rials, require_visit_price: requireVisit } }) : Promise.resolve({ success: true, data: { found: false } })); } it('تعرفه را با doctor_uuid همان اسلات می‌خواند و در فیلد می‌گذارد', async () => { mockPricing(2_120_000); renderWithProviders( {}} onSuccess={() => {}} />); await waitFor(() => expect(get).toHaveBeenCalledWith('/api/v1/insurance-pricing?doctor_uuid=doc1')); // ۲٬۱۲۰٬۰۰۰ ریال = ۲۱۲٬۰۰۰ تومان await waitFor(() => expect(screen.getByPlaceholderText('0')).toHaveValue('۲۱۲٬۰۰۰')); }); it('بدون تعرفه، فیلد صفر می‌ماند و اختیاری است', async () => { mockPricing(0); renderWithProviders( {}} onSuccess={() => {}} />); await screen.findByText(/تعرفه‌ای ثبت نشده/); expect(screen.getByPlaceholderText('0')).toHaveValue(''); expect(screen.getByText('(اختیاری)')).toBeInTheDocument(); }); it('با الزامی بودن هزینه ویزیت و مبلغ صفر، ثبت غیرفعال است', async () => { mockPricing(0, true); renderWithProviders( {}} onSuccess={() => {}} />); await screen.findByText('هزینه ویزیت الزامی است'); expect(screen.getByRole('button', { name: 'ثبت نوبت' })).toBeDisabled(); }); });