Files

140 lines
5.1 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('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 { toast } from 'sonner';
import PatientTreatmentTab from './PatientTreatmentTab';
const get = api.get as ReturnType<typeof vi.fn>;
const patch = api.patch as ReturnType<typeof vi.fn>;
const CASE_UUID = 'case-1';
const summary = (over: Record<string, unknown> = {}) => ({
uuid: CASE_UUID,
status: 'active',
total_sessions: 5,
completed_sessions: 3,
service: { uuid: 'svc-1', name: 'لیزر ناحیه ۱' },
...over,
});
/** فهرست دوره‌ها، تقویم دوره، و بقیهٔ GETها. */
function mockApi(caseOver: Record<string, unknown> = {}) {
get.mockImplementation((url: string) => {
if (url.startsWith('/api/v1/treatment-cases')) {
return Promise.resolve({ success: true, data: [summary(caseOver)] });
}
if (url.includes('/plan')) {
return Promise.resolve({ success: true, data: {
case: { ...summary(caseOver), patient_national_code: null },
resource: null,
sessions: [],
} });
}
return Promise.resolve({ success: true, data: [] });
});
}
beforeEach(() => {
get.mockReset();
patch.mockReset();
patch.mockResolvedValue({ success: true, data: {} });
mockApi();
});
/** دورهٔ انتخاب‌شده در URL می‌نشیند؛ تست هم از همان مسیر شروع می‌کند. */
const render = () => renderWithProviders(
<PatientTreatmentTab recordUuid="rec-1" />,
{ route: `/admin/patients/p-1?case=${CASE_UUID}` },
);
const plusBtn = () => screen.getByRole('button', { name: 'افزودن یک جلسه' });
const minusBtn = () => screen.getByRole('button', { name: 'کم کردن یک جلسه' });
/**
* تعداد جلسات سرِ پروندهٔ بیمار تصمیم گرفته می‌شود، ولی تا این تغییر فقط از صفحهٔ
* «دوره‌های درمان» و پشت یک مودال قابل تغییر بود.
*/
describe('PatientTreatmentTab — تعداد جلسات', () => {
it('یک جلسه اضافه می‌کند', async () => {
render();
fireEvent.click(await screen.findByRole('button', { name: 'افزودن یک جلسه' }));
await waitFor(() => expect(patch).toHaveBeenCalledWith(
`/api/v1/treatment-case/${CASE_UUID}`, { total_sessions: 6 },
));
});
it('یک جلسه کم می‌کند', async () => {
render();
fireEvent.click(await screen.findByRole('button', { name: 'کم کردن یک جلسه' }));
await waitFor(() => expect(patch).toHaveBeenCalledWith(
`/api/v1/treatment-case/${CASE_UUID}`, { total_sessions: 4 },
));
});
it('وقتی همهٔ جلسه‌ها انجام شده، کم کردن قفل است', async () => {
mockApi({ total_sessions: 4, completed_sessions: 4 });
render();
await waitFor(() => expect(minusBtn()).toBeDisabled());
expect(screen.getByText(/همهٔ جلسه‌ها انجام شده/)).toBeInTheDocument();
// افزودن همچنان آزاد است.
expect(plusBtn()).not.toBeDisabled();
});
it('کف دو جلسه رعایت می‌شود', async () => {
mockApi({ total_sessions: 2, completed_sessions: 0 });
render();
await waitFor(() => expect(minusBtn()).toBeDisabled());
expect(screen.getByText(/کمتر از ۲ جلسه ممکن نیست/)).toBeInTheDocument();
});
it('سقف شصت جلسه رعایت می‌شود', async () => {
mockApi({ total_sessions: 60, completed_sessions: 0 });
render();
await waitFor(() => expect(plusBtn()).toBeDisabled());
expect(minusBtn()).not.toBeDisabled();
});
it('خطای سرور را همان‌طور که آمده نشان می‌دهد', async () => {
// سرور دقیق‌تر از هر متن ثابتی می‌گوید چند جلسه قفل است.
patch.mockRejectedValue(new Error('۳ جلسه انجام شده یا نوبت دارد؛ تعداد کمتر از آن ممکن نیست'));
render();
fireEvent.click(await screen.findByRole('button', { name: 'کم کردن یک جلسه' }));
await waitFor(() => expect(toast.error).toHaveBeenCalledWith(
'۳ جلسه انجام شده یا نوبت دارد؛ تعداد کمتر از آن ممکن نیست',
));
});
it('پیشرفت دوره با نسبت واقعی اعلام می‌شود', async () => {
render();
const bar = await screen.findByRole('progressbar', { name: /پیشرفت دوره/ });
expect(bar).toHaveAttribute('aria-valuenow', '3');
expect(bar).toHaveAttribute('aria-valuemax', '5');
});
it('دکمهٔ ویرایش دوره برای بقیهٔ فیلدها هست', async () => {
render();
expect(await screen.findByRole('button', { name: /ویرایش دوره/ })).toBeInTheDocument();
});
});