import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; vi.mock('@/services/response', () => ({ request: { getCancellationPreview: vi.fn(), cancelAppointment: vi.fn(), }, })); import { request } from '@/services/response'; import ModalDeleteComment from './index'; const appointment = { uuid: 'a-1', slot_start: 2_000_000_000, status: 'confirmed' }; const preview = (over = {}) => ({ data: { penalty_rials: 2_500_000, deposit_refundable: true, credit_refundable: true, within_free_window: false, notes: [], paid_rials: 5_000_000, ...over, }, }); describe('مودال لغو نوبت سایت', () => { beforeEach(() => { request.cancelAppointment.mockResolvedValue({ data: {} }); }); /** ⭐ تا پیش از این، دکمهٔ تأیید فقط پنجره را می‌بست و هیچ درخواستی نمی‌رفت. */ it('پیش از تأیید، جریمه را از سرور نشان می‌دهد', async () => { request.getCancellationPreview.mockResolvedValue(preview()); const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'لغو کردن نوبت' })); // ۲٬۵۰۰٬۰۰۰ ریال ⇒ ۲۵۰٬۰۰۰ تومان. قالب رقم به ICU محیط وابسته است، پس فقط // «مبلغ درست + واحد» سنجیده می‌شود نه شکل رقم‌ها. const amount = await screen.findByText(/تومان$/); expect(amount.textContent.replace(/[^0-9۰-۹٠-٩]/g, '')).toMatch(/2500{2}0|۲۵۰۰۰۰|٢٥٠٠٠٠/); expect(request.cancelAppointment).not.toHaveBeenCalled(); }); it('در بازهٔ رایگان «بدون جریمه» می‌گوید، نه صفر', async () => { request.getCancellationPreview.mockResolvedValue( preview({ penalty_rials: 0, within_free_window: true }), ); const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'لغو کردن نوبت' })); expect(await screen.findByText('بدون جریمه')).toBeInTheDocument(); expect(screen.getByText('این لغو در بازهٔ رایگان است.')).toBeInTheDocument(); }); it('تأیید، لغو را واقعاً می‌فرستد و والد را خبر می‌کند', async () => { request.getCancellationPreview.mockResolvedValue(preview()); const onDone = vi.fn(); const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'لغو کردن نوبت' })); await screen.findByText(/جریمهٔ لغو/); await user.click(screen.getByRole('button', { name: 'لغو نوبت' })); await waitFor(() => expect(request.cancelAppointment).toHaveBeenCalledWith('a-1', { by: 'user' }), ); await waitFor(() => expect(onDone).toHaveBeenCalled()); }); /** پیش‌نمایشی که نیامده نباید لغو را قفل کند — ولی باید صریح بگوید نیامده. */ it('نبودِ پیش‌نمایش، لغو را قفل نمی‌کند', async () => { request.getCancellationPreview.mockRejectedValue(new Error('down')); const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'لغو کردن نوبت' })); expect(await screen.findByText(/پیامد مالی لغو در دسترس نیست/)).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'لغو نوبت' })).not.toBeDisabled(); }); });