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 ServiceSlotPicker, { type ServicePick } from './ServiceSlotPicker'; import type { BookingService } from '../../hooks/useDoctorBookingServices'; const get = api.get as ReturnType; const services: BookingService[] = [ { uuid: 'i1', name: 'بوتاکس', duration_minutes: 30, price_rials: 0, service_section: { uuid: 'sec1', name: 'زیبایی' } }, { uuid: 'i2', name: 'فیلر لب', duration_minutes: 20, price_rials: 0, service_section: { uuid: 'sec1', name: 'زیبایی' } }, { uuid: 'i3', name: 'لیزر موهای زائد', duration_minutes: 45, price_rials: 0, service_section: { uuid: 'sec2', name: 'لیزر' } }, ]; const pickSection = async (optionLabel: string) => { const input = document.getElementById('service-mode-section-select') as HTMLInputElement; fireEvent.focus(input); fireEvent.keyDown(input, { key: 'ArrowDown' }); fireEvent.click(await screen.findByText(optionLabel)); }; beforeEach(() => { get.mockReset(); get.mockResolvedValue({ success: true, data: { total_duration_minutes: 50, buffer_minutes: 0, start_times: [] } }); }); describe('ServiceSlotPicker — انتخاب سرویس بر اساس بخش + مدت قابل‌ویرایش', () => { it('accumulates services across sections and reports section→service + durations', async () => { let last: ServicePick = { serviceUuids: [], durations: {}, slot: null }; renderWithProviders( { last = v; }} />, ); // بخش زیبایی → دو سرویس await pickSection('زیبایی'); fireEvent.click(await screen.findByText('بوتاکس')); fireEvent.click(await screen.findByText('فیلر لب')); // بخش لیزر → یک سرویس؛ لیست انباشته حفظ می‌شود await pickSection('لیزر'); fireEvent.click(await screen.findByText('لیزر موهای زائد')); expect(screen.getByText('سرویس‌های انتخاب‌شده (3)')).toBeInTheDocument(); // chip نام بخش را کنار سرویس نشان می‌دهد (دو chip از بخش زیبایی) expect(screen.getAllByText('زیبایی').length).toBeGreaterThanOrEqual(2); await waitFor(() => expect(last.serviceUuids).toEqual(['i1', 'i2', 'i3'])); expect(last.durations).toEqual({ i1: 30, i2: 20, i3: 45 }); }); it('lets the secretary override a service duration for this appointment only', async () => { let last: ServicePick = { serviceUuids: [], durations: {}, slot: null }; renderWithProviders( { last = v; }} />, ); await pickSection('زیبایی'); fireEvent.click(await screen.findByText('بوتاکس')); fireEvent.change(screen.getByLabelText('مدت بوتاکس'), { target: { value: '90' } }); await waitFor(() => expect(last.durations).toEqual({ i1: 90 })); }); it('removes a selected service from the accumulated list', async () => { let last: ServicePick = { serviceUuids: [], durations: {}, slot: null }; renderWithProviders( { last = v; }} />, ); await pickSection('زیبایی'); fireEvent.click(await screen.findByText('بوتاکس')); fireEvent.click(await screen.findByText('فیلر لب')); expect(screen.getByText('سرویس‌های انتخاب‌شده (2)')).toBeInTheDocument(); fireEvent.click(screen.getByLabelText('حذف بوتاکس')); expect(screen.getByText('سرویس‌های انتخاب‌شده (1)')).toBeInTheDocument(); await waitFor(() => expect(last.serviceUuids).toEqual(['i2'])); }); });