Files
clinicpro/assets/admin/pages/PatientRecordFormPage.recordNumber.test.tsx
hamed 85a27812c7 feat(patient): implement record number pattern management
- Add RecordNumberSettingsController for managing patient record number patterns.
- Create RecordNumberPattern entity to represent the pattern configuration.
- Implement RecordNumberPatternRepository for database interactions.
- Develop RecordNumberGenerator service for generating and validating record numbers.
- Add tests for record number generation, backfilling, and API interactions.
- Ensure proper access control for viewing and updating patterns based on user roles.
2026-08-04 12:30:17 +03:30

92 lines
4.6 KiB
TypeScript

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 PatientRecordFormPage from './PatientRecordFormPage';
const get = api.get as ReturnType<typeof vi.fn>;
const post = api.post as ReturnType<typeof vi.fn>;
/** الگوی شمارهٔ پرونده؛ `enabled=false` یعنی رفتار قدیمیِ ورود دستی. */
function mockPattern(settings: Record<string, unknown>) {
get.mockImplementation((url: string) =>
url.includes('patient-record-number-settings')
? Promise.resolve({ success: true, data: { pattern: '{YY}-{SEQ:4}', reset_policy: 'none', counter: 0, next_preview: '05-0001', ...settings } })
: Promise.resolve({ success: true, data: [] }),
);
}
/**
* پرکردن فیلدهای الزامیِ دیگر تا فقط شمارهٔ پرونده متغیر بماند.
*
* اول منتظر رسیدن تنظیمات می‌ماند: رندرِ بعد از پاسخ، منوی باز react-select را
* می‌بندد و انتخاب جنسیت را می‌خورد.
*/
async function fillRequiredFields() {
await waitFor(() => expect(get).toHaveBeenCalledWith(expect.stringContaining('patient-record-number-settings')));
await waitFor(() => expect(screen.getByRole('button', { name: /ثبت|ذخیره/ })).toBeEnabled());
fireEvent.change(await screen.findByPlaceholderText('نام و نام خانوادگی را وارد نمایید'), { target: { value: 'علی محمدی' } });
fireEvent.change(screen.getByPlaceholderText('کد ملی را وارد نمایید'), { target: { value: '0012345675' } });
fireEvent.change(screen.getByPlaceholderText('شماره تماس را وارد نمایید'), { target: { value: '09121234567' } });
const gender = document.getElementById('patient-gender-select') as HTMLInputElement;
fireEvent.focus(gender);
fireEvent.keyDown(gender, { key: 'ArrowDown' });
fireEvent.click(await screen.findByText('مرد'));
}
beforeEach(() => {
get.mockReset();
post.mockReset();
post.mockResolvedValue({ success: true, data: { uuid: 'rec-new' } });
(api.patch as ReturnType<typeof vi.fn>).mockResolvedValue({ success: true, data: {} });
});
describe('PatientRecordFormPage — شمارهٔ پرونده', () => {
/** ⚠️ مرزی: بدون الگو، فیلد الزامی می‌ماند (رفتار امروز). */
it('بدون الگو شماره پرونده الزامی است', async () => {
mockPattern({ enabled: false, can_edit: true });
renderWithProviders(<PatientRecordFormPage />, { route: '/admin/patients/new' });
await fillRequiredFields();
fireEvent.click(screen.getByText('ثبت اطلاعات') ?? screen.getByRole('button', { name: /ثبت|ذخیره/ }));
expect(await screen.findByText('شماره پرونده الزامی است')).toBeInTheDocument();
expect(post).not.toHaveBeenCalled();
});
/** ✅ موفق: با الگوی فعال و بدون اجازهٔ دستی، فیلد read-only است و ارسال نمی‌شود. */
it('با الگوی فعال و بدون can_edit، شماره فرستاده نمی‌شود', async () => {
mockPattern({ enabled: true, can_edit: false });
renderWithProviders(<PatientRecordFormPage />, { route: '/admin/patients/new' });
expect(await screen.findByText('شماره از الگوی مجموعه ساخته می‌شود')).toBeInTheDocument();
await fillRequiredFields();
fireEvent.click(screen.getByRole('button', { name: /ثبت|ذخیره/ }));
await waitFor(() => expect(post).toHaveBeenCalled());
expect(post.mock.calls[0][1]).not.toHaveProperty('record_number');
});
/** ✅ موفق: صاحب مجموعه می‌تواند خالی بگذارد تا سرور بسازد. */
it('صاحب مجموعه می‌تواند شماره را خالی بگذارد', async () => {
mockPattern({ enabled: true, can_edit: true });
renderWithProviders(<PatientRecordFormPage />, { route: '/admin/patients/new' });
expect(await screen.findByText(/خالی بماند تا از الگو ساخته شود/)).toBeInTheDocument();
await fillRequiredFields();
fireEvent.click(screen.getByRole('button', { name: /ثبت|ذخیره/ }));
await waitFor(() => expect(post).toHaveBeenCalled());
expect(post.mock.calls[0][1].record_number).toBe('');
});
});