140 lines
5.1 KiB
TypeScript
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();
|
|
});
|
|
});
|