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 TreatmentCasesPage from './TreatmentCasesPage'; const get = api.get as ReturnType; function caseRow(over: Record = {}) { return { uuid: 'case-1', status: 'active', total_sessions: 3, completed_sessions: 1, opened_at: 1_786_000_000, closed_at: null, service: { uuid: 'svc-1', name: 'لیزر توتال' }, supervisor: { uuid: 'doc-1', name: 'پزشک مدیسا' }, patient: { record_uuid: 'rec-1', name: 'محمد رسولی', mobile: '09120001111', record_number: '۱۲' }, areas: [{ uuid: 'ca-1', name: 'دست', category_uuid: 'cat-1' }], ...over, }; } /** فقط اندپوینت فهرست را جواب می‌دهد؛ بقیه خالی. */ function mockList(rows: unknown[]) { get.mockImplementation((url: string) => { if (url.startsWith('/api/v1/treatment-cases')) return Promise.resolve({ success: true, data: rows }); return Promise.resolve({ success: true, data: [] }); }); } beforeEach(() => { get.mockReset(); }); describe('صفحهٔ پرونده‌های درمان', () => { it('نام بیمار سرتیتر کارت است، نه نام سرویس', async () => { mockList([caseRow()]); renderWithProviders(); const name = await screen.findByText('محمد رسولی'); expect(name.tagName).toBe('STRONG'); expect(screen.getByText('لیزر توتال')).toBeInTheDocument(); }); /** جستجو باید به سرور برود، نه اینکه فهرست را در مرورگر فیلتر کند. */ it('عبارت جستجو را به‌صورت پارامتر q می‌فرستد', async () => { mockList([caseRow()]); renderWithProviders(); await screen.findByText('محمد رسولی'); fireEvent.change(screen.getByLabelText('جستجوی پرونده'), { target: { value: 'رسولی' } }); await waitFor( () => expect(get.mock.calls.some( (c: unknown[]) => typeof c[0] === 'string' && c[0].includes('q=%D8%B1%D8%B3%D9%88%D9%84%DB%8C'), )).toBe(true), { timeout: 2000 }, ); }); it('فیلتر وضعیت به‌صورت status می‌رود', async () => { mockList([caseRow()]); renderWithProviders(); await screen.findByText('محمد رسولی'); fireEvent.click(screen.getByRole('button', { name: 'رها شده' })); await waitFor(() => expect(get.mock.calls.some( (c: unknown[]) => typeof c[0] === 'string' && c[0].includes('status=abandoned'), )).toBe(true)); }); /** نتیجهٔ خالیِ جستجو با «هنوز پرونده‌ای ساخته نشده» یکی نیست. */ it('خالیِ جستجو پیام خودش را دارد', async () => { mockList([]); renderWithProviders(); await screen.findByText(/پرونده‌ای یافت نشد/); fireEvent.change(screen.getByLabelText('جستجوی پرونده'), { target: { value: 'هیچ' } }); expect(await screen.findByText(/برای «هیچ» پرونده‌ای پیدا نشد/, {}, { timeout: 2000 })).toBeInTheDocument(); }); it('خطا را از فهرست خالی جدا می‌کند', async () => { get.mockRejectedValue(new Error('boom')); renderWithProviders(); await waitFor(() => expect(screen.getByText(/خواندن پرونده‌ها ناموفق بود/)).toBeInTheDocument()); expect(screen.queryByText(/پرونده‌ای یافت نشد/)).not.toBeInTheDocument(); }); it('دکمهٔ ویرایش مودال را باز می‌کند', async () => { mockList([caseRow()]); renderWithProviders(); fireEvent.click(await screen.findByRole('button', { name: /ویرایش/ })); expect(await screen.findByText('ویرایش پروندهٔ درمان')).toBeInTheDocument(); }); });