Files
clinicpro/assets/admin/components/ClinicDoctorsManager.test.tsx
hamed 60ccd5cc1d feat: implement OTP login flow and enhance role-based access control
- Added OTP login functionality in driver.mjs to handle user authentication with a fixed code in dev environment.
- Enhanced RoleRoute component in App.tsx to support clinic-scoped doctor roles and permissions.
- Updated ClinicDoctorsManager component to include pagination and search functionality for better user experience.
- Refactored tests for ClinicDoctorsManager to cover new features and ensure proper API mocking.
- Adjusted permissions in settingsMenu.ts and PracticeDomainSettingsPage.tsx to align with updated backend requirements.
- Created RoleRoute.test.tsx to validate role-based access logic for different user scenarios.
2026-08-09 12:41:11 +03:30

130 lines
5.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { screen, waitFor } from '@testing-library/react';
import { renderWithProviders } from '../test/utils';
import { api } from '../lib/api';
import ClinicDoctorsManager from './ClinicDoctorsManager';
vi.mock('../lib/api', () => ({ api: { get: vi.fn(), post: vi.fn(), patch: vi.fn(), delete: vi.fn() } }));
const DOCTOR = {
id: '1',
uuid: 'doc-1',
name: 'پزشک دعوت‌شده',
gender: null,
degree: null,
img: [],
specialties: [],
active: false, // نوبت‌دهی آنلاینِ خودِ پزشک خاموش است
};
/** پاسخ‌ها بر اساس مسیر تفکیک می‌شوند، چون سه کوئری موازی می‌روند. */
function mockApi({ doctors = [DOCTOR], total = 1, permissions = [] as any[], failDoctors = false } = {}) {
(api.get as any).mockImplementation((url: string) => {
if (url.includes('/doctor-permissions')) return Promise.resolve({ data: permissions });
if (url.includes('/invitations')) return Promise.resolve({ data: [] });
if (url.includes('/doctor-list/')) {
return failDoctors
? Promise.reject(new Error('boom'))
: Promise.resolve({ data: doctors, meta: { totalRecords: total, totalPages: 1, currentPage: 1 } });
}
return Promise.resolve({ data: [] });
});
}
describe('ClinicDoctorsManager', () => {
beforeEach(() => vi.clearAllMocks());
it('صفحه و سقف را به سرور می‌فرستد — وگرنه backend سر ۱۰ پزشک بی‌صدا می‌برد', async () => {
mockApi();
renderWithProviders(<ClinicDoctorsManager clinicUuid="cl-1" />);
await waitFor(() => expect(api.get).toHaveBeenCalled());
const listCall = (api.get as any).mock.calls.find((c: any[]) => c[0].includes('/doctor-list/'));
expect(listCall[0]).toContain('page=1');
expect(listCall[0]).toContain('limit=10');
});
it('شمارندهٔ تب از meta می‌آید، نه از طول صفحهٔ جاری', async () => {
mockApi({ doctors: [DOCTOR], total: 25 });
renderWithProviders(<ClinicDoctorsManager clinicUuid="cl-1" />);
expect(await screen.findByText(/پزشکان \(۲۵\)/)).toBeInTheDocument();
});
it('«نوبت‌دهی آنلاین» و «دسترسی در کلینیک» دو ستون جدا هستند', async () => {
mockApi();
renderWithProviders(<ClinicDoctorsManager clinicUuid="cl-1" />);
expect(await screen.findByText('دسترسی در کلینیک')).toBeInTheDocument();
expect(screen.getByText('نوبت‌دهی آنلاین')).toBeInTheDocument();
});
it('شمار مجوزها زیر همان بجِ دسترسی می‌آید، نه در ستون جدا', async () => {
mockApi({
permissions: [{
doctor_uuid: 'doc-1',
active: true,
permissions: { resources: { appointments: { view: true, create: false } } },
}],
});
renderWithProviders(<ClinicDoctorsManager clinicUuid="cl-1" />);
expect(await screen.findByText('۱ از ۲ مجوز')).toBeInTheDocument();
expect(screen.queryByText('مجوزها')).not.toBeInTheDocument();
});
it('پزشکِ بدون ردیف مجوز، «پیش‌فرض» و دسترسی فعال می‌گیرد', async () => {
mockApi({ permissions: [] });
renderWithProviders(<ClinicDoctorsManager clinicUuid="cl-1" />);
expect(await screen.findByText('پیش‌فرض')).toBeInTheDocument();
});
it('ردیف مجوزِ خاموش، «غیرفعال» نشان می‌دهد و شمار مجوزها را می‌آورد', async () => {
mockApi({
permissions: [{
doctor_uuid: 'doc-1',
active: false,
permissions: { resources: { appointments: { view: true, create: false } } },
}],
});
renderWithProviders(<ClinicDoctorsManager clinicUuid="cl-1" />);
expect(await screen.findByText('۱ از ۲ مجوز')).toBeInTheDocument();
// دو «غیرفعال»: یکی دسترسیِ کلینیک، یکی نوبت‌دهی آنلاین. پیش از این یک بج جای
// هر دو می‌نشست و همین ابهام، پزشکِ سالم را «غیرفعال» نشان می‌داد.
expect(screen.getAllByText('غیرفعال')).toHaveLength(2);
});
it('دسترسیِ فعال با نوبت‌دهیِ خاموش قاطی نمی‌شود', async () => {
mockApi({
permissions: [{
doctor_uuid: 'doc-1',
active: true,
permissions: { resources: { appointments: { view: true } } },
}],
});
renderWithProviders(<ClinicDoctorsManager clinicUuid="cl-1" />);
// دسترسی فعال است…
expect(await screen.findByText('فعال')).toBeInTheDocument();
// …ولی نوبت‌دهی آنلاینِ خودِ پزشک همچنان خاموش.
expect(screen.getByText('غیرفعال')).toBeInTheDocument();
});
it('خطای فهرست، پیام خطا می‌دهد نه حالت خالی', async () => {
mockApi({ failDoctors: true });
renderWithProviders(<ClinicDoctorsManager clinicUuid="cl-1" />);
expect(await screen.findByText('فهرست پزشکان بارگذاری نشد')).toBeInTheDocument();
expect(screen.queryByText('هیچ پزشکی به این کلینیک متصل نیست')).not.toBeInTheDocument();
});
it('فهرست خالی، پیام خالی می‌دهد', async () => {
mockApi({ doctors: [], total: 0 });
renderWithProviders(<ClinicDoctorsManager clinicUuid="cl-1" />);
expect(await screen.findByText('هیچ پزشکی به این کلینیک متصل نیست')).toBeInTheDocument();
});
});