import { describe, it, expect, beforeEach, vi } from 'vitest'; import { screen, fireEvent } 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 { useAuthStore } from '../stores/authStore'; import ClinicServicesPage from './ClinicServicesPage'; const get = api.get as ReturnType; beforeEach(() => { // FeatureGate → useSubscription only fetches for doctor/clinic/secretary roles useAuthStore.setState({ primaryRole: 'doctor' }); get.mockReset(); get.mockImplementation((url: string) => { if (url.includes('/subscription/my')) return Promise.resolve({ success: true, data: { subscription: null, used_trial: false, effective_plan: { name: 'professional', level: 2, max_secretaries: 10, features: { services: true } }, } }); if (url.includes('/payment/config')) return Promise.resolve({ success: true, data: { test_mode: true, gateways: [] } }); if (url.includes('/service-sections')) return Promise.resolve({ success: true, data: [ { uuid: 'sec1', name: 'کندلا ۲۰۲۱', active: true, items_count: 10 }, ] }); if (url.includes('/service-items/sec1')) return Promise.resolve({ success: true, data: [ { uuid: 'it1', name: 'فول بادی', price_rials: 35_000_000, active: true, duration_minutes: 50, staff: { uuid: 'st1', full_name: 'مریم امینی' }, staff_members: [{ uuid: 'st1', full_name: 'مریم امینی' }, { uuid: 'st2', full_name: 'سحر رحمانی' }] }, ] }); if (url.includes('/staff')) return Promise.resolve({ success: true, data: [ { uuid: 'st1', full_name: 'مریم امینی', active: true }, { uuid: 'st2', full_name: 'سحر رحمانی', active: true }, ] }); return Promise.resolve({ success: true, data: [] }); }); }); describe('ClinicServicesPage (خدمات)', () => { it('renders section cards with the service count inside the settings shell', async () => { renderWithProviders(, { route: '/admin/clinic-services' }); // settings shell menu + section card expect(await screen.findByText('کندلا ۲۰۲۱')).toBeInTheDocument(); expect(screen.getByText(/۱۰ سرویس/)).toBeInTheDocument(); expect(screen.getByText('بخش جدید')).toBeInTheDocument(); }); it('drills into a section and shows a service with all its personnel chips', async () => { renderWithProviders(, { route: '/admin/clinic-services' }); fireEvent.click(await screen.findByText('کندلا ۲۰۲۱')); expect(await screen.findByText('فول بادی')).toBeInTheDocument(); expect(screen.getByText('مریم امینی')).toBeInTheDocument(); expect(screen.getByText('سحر رحمانی')).toBeInTheDocument(); expect(screen.getByText('سرویس جدید')).toBeInTheDocument(); }); it('opens the ⋮ actions menu with insurance and tariff entries for a service', async () => { renderWithProviders(, { route: '/admin/clinic-services' }); fireEvent.click(await screen.findByText('کندلا ۲۰۲۱')); fireEvent.click(await screen.findByTitle('عملیات')); expect(await screen.findByText('ویرایش')).toBeInTheDocument(); expect(screen.getByText('تعرفه‌های سالانه')).toBeInTheDocument(); expect(screen.getByText('پوشش بیمه')).toBeInTheDocument(); }); it('فرم ویرایش سرویس دیگر سوییچ بیمه ندارد و به بخش پوشش بیمه ارجاع می‌دهد', async () => { renderWithProviders(, { route: '/admin/clinic-services' }); fireEvent.click(await screen.findByText('کندلا ۲۰۲۱')); fireEvent.click(await screen.findByTitle('عملیات')); fireEvent.click(await screen.findByText('ویرایش')); expect(await screen.findByText('ویرایش سرویس')).toBeInTheDocument(); expect(screen.queryByText('این خدمت شامل بیمه می‌شود')).not.toBeInTheDocument(); expect(screen.queryByText('قیمت تقریبی با بیمه (تومان)')).not.toBeInTheDocument(); expect(screen.getByText(/در بخش «پوشش بیمه» تنظیم می‌شود/)).toBeInTheDocument(); }); it('ذخیره‌ی سرویس فیلدهای بیمه را نمی‌فرستد', async () => { const patch = api.patch as ReturnType; patch.mockResolvedValue({ success: true, data: {} }); renderWithProviders(, { route: '/admin/clinic-services' }); fireEvent.click(await screen.findByText('کندلا ۲۰۲۱')); fireEvent.click(await screen.findByTitle('عملیات')); fireEvent.click(await screen.findByText('ویرایش')); fireEvent.click(await screen.findByText('ذخیره سرویس')); await vi.waitFor(() => expect(patch).toHaveBeenCalled()); const body = patch.mock.calls[0][1]; expect(body).not.toHaveProperty('insurance_covered'); expect(body).not.toHaveProperty('insurance_price_rials'); }); it('کارت سرویسِ تحت پوشش، نشان «تحت پوشش» را نمایش می‌دهد', async () => { get.mockImplementation((url: string) => { if (url.includes('/subscription/my')) return Promise.resolve({ success: true, data: { subscription: null, used_trial: false, effective_plan: { name: 'professional', level: 2, max_secretaries: 10, features: { services: true } }, } }); if (url.includes('/payment/config')) return Promise.resolve({ success: true, data: { test_mode: true, gateways: [] } }); if (url.includes('/service-sections')) return Promise.resolve({ success: true, data: [ { uuid: 'sec1', name: 'کندلا ۲۰۲۱', active: true, items_count: 1 }, ] }); if (url.includes('/service-items/sec1')) return Promise.resolve({ success: true, data: [ { uuid: 'it1', name: 'فول بادی', price_rials: 35_000_000, active: true, insurance_covered: true }, ] }); return Promise.resolve({ success: true, data: [] }); }); renderWithProviders(, { route: '/admin/clinic-services' }); fireEvent.click(await screen.findByText('کندلا ۲۰۲۱')); expect(await screen.findByText('تحت پوشش')).toBeInTheDocument(); }); });