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 {}, })); vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } })); vi.mock('../hooks/usePermissions', () => ({ usePermissions: () => ({ can: () => true }), })); import { api } from '../lib/api'; import ResourcesPage from './ResourcesPage'; const get = api.get as ReturnType; const post = api.post as ReturnType; const branch = { id: '1', uuid: 'b1', type: 'clinic', clinic_id: 3, clinic_name: 'کلینیک', name: 'شعبهٔ مرکزی', map: { latitude: null, longitude: null }, address: null, telephone: null, active: true, timezone: 'Asia/Tehran', city: null, province: null, }; const laserType = { uuid: 't1', code: 'laser', name: 'دستگاه لیزر', is_system: false, active: true, resources_count: 1, created_at: 0, updated_at: 0 }; const skill = { uuid: 's1', name: 'آلکساندرایت', active: true, resources_count: 1, created_at: 0, updated_at: 0 }; const resource = { uuid: 'r1', name: 'لیزر ۱', address_uuid: 'b1', address_name: 'شعبهٔ مرکزی', type_uuid: 't1', type_code: 'laser', type_name: 'دستگاه لیزر', capacity: 3, setup_minutes: 5, cleanup_minutes: 10, attributes: {}, subject_kind: null, subject_uuid: null, skills: [{ skill_uuid: 's1', skill_name: 'آلکساندرایت', level: 4 }], active: true, created_at: 0, updated_at: 0, }; function mockApi() { get.mockImplementation((path: string) => { if (path.startsWith('/api/v1/branches')) return Promise.resolve({ success: true, data: [branch] }); if (path.startsWith('/api/v1/resource-types')) return Promise.resolve({ success: true, data: [laserType] }); if (path.startsWith('/api/v1/skills')) return Promise.resolve({ success: true, data: [skill] }); if (path.startsWith('/api/v1/resources')) return Promise.resolve({ success: true, data: [resource] }); return Promise.resolve({ success: true, data: [] }); }); post.mockResolvedValue({ success: true, data: resource }); } describe('ResourcesPage', () => { beforeEach(() => vi.clearAllMocks()); it('shows capacity as concurrency and both buffer minutes', async () => { mockApi(); renderWithProviders(, { route: '/admin/resources' }); await waitFor(() => expect(screen.getByText('لیزر ۱')).toBeInTheDocument()); expect(screen.getByText('3 نفر')).toBeInTheDocument(); expect(screen.getByText('5 / 10 دقیقه')).toBeInTheDocument(); }); it('labels a resource with no bridge as equipment', async () => { mockApi(); renderWithProviders(, { route: '/admin/resources' }); await waitFor(() => expect(screen.getByText(/تجهیزات/)).toBeInTheDocument()); }); it('renders each skill with its level', async () => { mockApi(); renderWithProviders(, { route: '/admin/resources' }); await waitFor(() => expect(screen.getByText('آلکساندرایت · 4')).toBeInTheDocument()); }); /** * فیلترها از URL خوانده می‌شوند و مستقیم به سرور می‌روند — فیلتر دوبارهٔ سمت * کلاینت روی فهرستی که خودش فیلترشده آمده، منبع اختلاف است. */ it('passes the URL filters straight to the server', async () => { mockApi(); renderWithProviders(, { route: '/admin/resources?address=b1&type=t1&skill=s1&status=1', }); await waitFor(() => expect(get).toHaveBeenCalled()); const called = get.mock.calls.map((c) => String(c[0])); const listCall = called.find((p) => p.startsWith('/api/v1/resources')); expect(listCall).toContain('address_uuid=b1'); expect(listCall).toContain('type_uuid=t1'); expect(listCall).toContain('skill_uuid=s1'); expect(listCall).toContain('active=1'); }); it('sends address and type only when creating, never when editing', async () => { mockApi(); renderWithProviders(, { route: '/admin/resources' }); await waitFor(() => expect(screen.getByText('لیزر ۱')).toBeInTheDocument()); fireEvent.click(screen.getByText('افزودن منبع')); fireEvent.change(screen.getByPlaceholderText('لیزر آلکساندرایت ۱'), { target: { value: 'لیزر تازه' } }); // شعبه و نوع هنوز انتخاب نشده‌اند → ذخیره غیرفعال است. expect(screen.getByText('ذخیره').closest('button')).toBeDisabled(); }); });