Files
clinicpro/assets/admin/components/appointments/NewAppointmentModal.test.tsx
T

108 lines
4.7 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { screen } from '@testing-library/react';
import { renderWithProviders } from '../../test/utils';
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }));
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 NewAppointmentModal from './NewAppointmentModal';
const get = api.get as ReturnType<typeof vi.fn>;
const slot = {
start: 1_780_000_000,
end: 1_780_001_800,
start_time: '۰۹:۰۰',
end_time: '۰۹:۳۰',
doctor_uuid: 'doc-1',
doctor_name: 'دکتر رضایی',
};
beforeEach(() => {
get.mockReset();
get.mockResolvedValue({ success: true, data: [] });
});
const props = (overrides: Record<string, unknown> = {}) => ({
slot,
onClose: vi.fn(),
onSuccess: vi.fn(),
...overrides,
});
/** عنوان مرحله‌های نوار بالای مودال. */
const stepLabels = () =>
Array.from(document.querySelectorAll('ol[aria-label="مراحل ثبت نوبت"] li'))
.map(li => li.textContent?.replace(/^\d+/, '').trim());
const pickerProps = () => props({ serviceMode: true, date: '1405-05-18', services: [] });
describe('NewAppointmentModal — ویزارد', () => {
/**
* فرم قبلاً یک صفحهٔ بلند بود: خدمت و زمان، بیمار و هزینه با هم. هر بار یک مرحله
* دیده می‌شود تا مودال از ارتفاع صفحه بلندتر نشود.
*/
it('حالت انتخابگر سه مرحله دارد و از مرحلهٔ اول شروع می‌شود', () => {
renderWithProviders(<NewAppointmentModal {...pickerProps()} />);
expect(stepLabels()).toEqual(['خدمت و زمان', 'بیمار', 'تأیید و ثبت']);
expect(screen.getByRole('heading', { name: 'خدمت و زمان' })).toBeInTheDocument();
expect(screen.queryByRole('heading', { name: 'بیمار' })).not.toBeInTheDocument();
});
it('مرحله‌ای که داده‌اش از قبل معلوم است ساخته نمی‌شود', () => {
// نوبت اسلاتی: زمان از خودِ اسلات می‌آید، پس مرحلهٔ «خدمت و زمان» بی‌معناست.
renderWithProviders(<NewAppointmentModal {...props()} />);
expect(stepLabels()).toEqual(['بیمار', 'تأیید و ثبت']);
});
it('بیمارِ از پیش معلوم، مرحلهٔ بیمار را حذف می‌کند', () => {
renderWithProviders(<NewAppointmentModal {...props({
patient: { name: 'علی محمدی', mobile: '09123456789', national_code: '1234567890' },
})} />);
// تنها مرحلهٔ باقی‌مانده «تأیید و ثبت» است، پس نوار مراحل هم لازم نیست.
expect(document.querySelector('ol[aria-label="مراحل ثبت نوبت"]')).toBeNull();
expect(screen.getByRole('heading', { name: 'تأیید و ثبت' })).toBeInTheDocument();
});
it('تا وقتی مرحله کامل نشده، دکمهٔ بعدی غیرفعال است و دلیلش را می‌گوید', () => {
renderWithProviders(<NewAppointmentModal {...pickerProps()} />);
expect(screen.getByRole('button', { name: 'مرحلهٔ بعد' })).toBeDisabled();
expect(screen.getByText(/یک سرویس انتخاب کنید/)).toBeInTheDocument();
// دکمهٔ ثبت فقط در مرحلهٔ آخر وجود دارد.
expect(screen.queryByRole('button', { name: 'ثبت نوبت' })).not.toBeInTheDocument();
});
it('در مرحلهٔ اول دکمهٔ «مرحلهٔ قبل» نیست، ولی «انصراف» همیشه هست', () => {
renderWithProviders(<NewAppointmentModal {...pickerProps()} />);
expect(screen.queryByRole('button', { name: 'مرحلهٔ قبل' })).not.toBeInTheDocument();
expect(screen.getByRole('button', { name: 'انصراف' })).toBeInTheDocument();
});
it('مرحلهٔ آخر خلاصهٔ نوبت را نشان می‌دهد', () => {
renderWithProviders(<NewAppointmentModal {...props({
patient: { name: 'علی محمدی', mobile: '09123456789', national_code: '1234567890' },
})} />);
expect(screen.getByText('علی محمدی')).toBeInTheDocument();
expect(screen.getByText('09123456789')).toBeInTheDocument();
});
it('دکمهٔ اصلی در فوتر ثابت است، نه داخل ناحیهٔ اسکرول', () => {
renderWithProviders(<NewAppointmentModal {...pickerProps()} />);
const next = screen.getByRole('button', { name: 'مرحلهٔ بعد' });
expect(next.closest('.modal-foot')).not.toBeNull();
expect(next.closest('.modal-body')).toBeNull();
});
});