108 lines
4.7 KiB
TypeScript
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();
|
|
});
|
|
});
|