import { describe, it, expect, beforeEach, vi } from 'vitest'; import { screen, 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 StaffTreatmentSessionsPage from './StaffTreatmentSessionsPage'; const get = api.get as ReturnType; const AT = Math.floor(new Date('2026-08-07T09:30:00Z').getTime() / 1000); function session(over: Record = {}) { return { uuid: 'ses-1', case_uuid: 'case-1', service_name: 'لیزر توتال', patient_name: 'محمد رسولی', resource_name: 'کندلا ۲۰۲۳', session_number: 2, total_sessions: 3, status: 'booked', due_at: AT, started_at: null, finished_at: null, note: null, performed_by: null, appointment: { uuid: 'a-1', slot_start: AT, slot_end: AT + 1800, status: 'confirmed' }, areas: [{ status: 'completed' }, { status: 'pending' }], ...over, }; } beforeEach(() => { get.mockReset(); }); describe('صفحهٔ جلسات امروز پرسنل', () => { it('نام بیمار و دستگاه را نشان می‌دهد، نه فقط نام سرویس', async () => { get.mockResolvedValue({ success: true, data: [session()] }); renderWithProviders(); expect(await screen.findByText('محمد رسولی')).toBeInTheDocument(); expect(screen.getByText('کندلا ۲۰۲۳')).toBeInTheDocument(); expect(screen.getByText('لیزر توتال')).toBeInTheDocument(); }); /** صف است: کار باقی‌مانده باید بالای کارِ تمام‌شده بنشیند. */ it('کار باقی‌مانده را از کار تمام‌شده جدا می‌کند', async () => { get.mockResolvedValue({ success: true, data: [ session({ uuid: 's-done', status: 'done', patient_name: 'بیمار تمام‌شده' }), session({ uuid: 's-open', status: 'booked', patient_name: 'بیمار در انتظار' }), ], }); renderWithProviders(); const pendingHead = await screen.findByText('در انتظار انجام'); const doneHead = screen.getByText('تمام‌شده'); expect(pendingHead.compareDocumentPosition(doneHead) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); const open = screen.getByText('بیمار در انتظار'); const done = screen.getByText('بیمار تمام‌شده'); expect(open.compareDocumentPosition(done) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); }); /** غیبت برای امروز بسته است، هرچند جلسه دوباره برنامه‌ریزی می‌شود. */ it('غیبت را در گروه تمام‌شده می‌گذارد', async () => { get.mockResolvedValue({ success: true, data: [session({ status: 'no_show' })] }); renderWithProviders(); expect(await screen.findByText('تمام‌شده')).toBeInTheDocument(); expect(screen.getByText('کار باقی‌مانده‌ای ندارید.')).toBeInTheDocument(); }); /** خطای سرور نباید «امروز جلسه‌ای ندارید» خوانده شود. */ it('خطا را از فهرست خالی جدا می‌کند', async () => { get.mockRejectedValue(new Error('boom')); renderWithProviders(); await waitFor(() => expect(screen.getByText(/خواندن جلسات امروز ناموفق بود/)).toBeInTheDocument()); expect(screen.queryByText(/جلسه‌ای برای شما ثبت نشده/)).not.toBeInTheDocument(); }); it('فهرست خالی پیام خودش را دارد', async () => { get.mockResolvedValue({ success: true, data: [] }); renderWithProviders(); expect(await screen.findByText(/جلسه‌ای برای شما ثبت نشده/)).toBeInTheDocument(); }); it('کل ردیف لینکِ همان جلسه است', async () => { get.mockResolvedValue({ success: true, data: [session()] }); renderWithProviders(); const row = (await screen.findByText('محمد رسولی')).closest('a'); expect(row).toHaveAttribute('href', '/admin/my-sessions/ses-1'); }); });