Files
clinicpro/assets/admin/components/InsuranceModal.test.tsx
T
hamed 4f4bce9fe2 feat(migrations): update franchise to percentage in tenant_insurances and tenant_service_coverage
- Changed franchise_rials to franchise_percent in tenant_insurances and tenant_service_coverage tables.
- Reset old rial values to 0/NULL as they are not convertible to percentage.

feat(command): add SeedInsuranceScenarioCommand for seeding insurance data

- Implemented a command to seed supplementary insurance contracts, patients, and claims for a specified doctor.
- Includes functionality for purging existing scenario data and generating new entries with predefined contracts and patient scenarios.
2026-07-29 13:28:59 +03:30

184 lines
8.3 KiB
TypeScript

import { describe, it, expect, vi } from 'vitest';
import { screen, fireEvent } from '@testing-library/react';
import { renderWithProviders } from '../test/utils';
import InsuranceModal, {
buildInsurancePayload, contractToForm, EMPTY_FORM, type Contract, type InsuranceOption,
} from './InsuranceModal';
import type { ServiceCategoryOption } from '../hooks/useServiceCategories';
const mkContract = (over: Partial<Contract> = {}): Contract => ({
uuid: 'c-1', insurance_id: 3, insurance_name: 'بیمه ایران', insurance_kind: 'basic',
version: 1, is_active: true, coverage_percent: 70, franchise_percent: 15,
annual_ceiling_rials: 20_000_000, kind: 'basic', effective_from: 1_700_000_000,
effective_to: null,
category_coverages: { outpatient: 70, inpatient: 30 },
category_coverage_source: { outpatient: 'override', inpatient: 'admin_default' },
...over,
});
const options: InsuranceOption[] = [
{ insurance_id: 3, insurance_name: 'بیمه ایران', type: 'basic', coverage_defaults: { outpatient: 70, inpatient: 30 } },
{ insurance_id: 5, insurance_name: 'بیمه آسیا', type: 'supplementary', coverage_defaults: { outpatient: 40, inpatient: 20 } },
];
const categories: ServiceCategoryOption[] = [
{ key: 'outpatient', label: 'خدمات سرپایی' },
{ key: 'inpatient', label: 'خدمات بستری' },
];
describe('buildInsurancePayload', () => {
it('keeps the franchise a percent, converts ceiling toman → rials and Y-m-d → unix', () => {
const payload = buildInsurancePayload({
...EMPTY_FORM, insuranceId: '3', kind: 'supplementary',
categoryPercents: { outpatient: '80', inpatient: '30' },
franchise: '10', ceiling: '2000000',
effectiveFrom: '2024-01-01', effectiveTo: '2025-01-01',
});
expect(payload.insurance_id).toBe(3);
expect(payload.kind).toBe('supplementary');
expect(payload.coverage_percent).toBe(80); // ستون قدیمی = درصد سرپایی
expect(payload.category_coverages).toEqual([
{ key: 'outpatient', coverage_percent: 80 },
{ key: 'inpatient', coverage_percent: 30 },
]);
expect(payload.franchise_percent).toBe(10); // درصد است، نه مبلغ
expect(payload.annual_ceiling_rials).toBe(20_000_000);
expect(typeof payload.effective_from).toBe('number');
expect(payload.effective_to).toBeGreaterThan(payload.effective_from!);
});
it('empty ceiling → null (بی‌نهایت), empty dates → null', () => {
const payload = buildInsurancePayload({
...EMPTY_FORM, insuranceId: '3', categoryPercents: { outpatient: '50' },
});
expect(payload.annual_ceiling_rials).toBeNull();
expect(payload.effective_from).toBeNull();
expect(payload.effective_to).toBeNull();
});
it('forces franchise to zero on a basic contract', () => {
const payload = buildInsurancePayload({
...EMPTY_FORM, insuranceId: '3', kind: 'basic', franchise: '10',
categoryPercents: { outpatient: '70' },
});
expect(payload.franchise_percent).toBe(0);
});
it('omits category_coverages when the user may not override them', () => {
const payload = buildInsurancePayload(
{ ...EMPTY_FORM, insuranceId: '3', categoryPercents: { outpatient: '70' } },
null,
false,
);
expect(payload).not.toHaveProperty('category_coverages');
});
});
describe('contractToForm', () => {
it('maps the franchise percent, contract kind, and effective category percents', () => {
const form = contractToForm(mkContract({ franchise_percent: 30, kind: 'supplementary' }));
expect(form.franchise).toBe('30');
expect(form.kind).toBe('supplementary');
expect(form.categoryPercents).toEqual({ outpatient: '70', inpatient: '30' });
});
});
describe('InsuranceModal', () => {
it('renders one percent input per category and hides franchise on basic', () => {
renderWithProviders(
<InsuranceModal open editContract={null} options={options} categories={categories} kind="basic" onClose={() => {}} onSubmit={() => {}} />,
);
expect(screen.getByText('افزودن بیمه')).toBeInTheDocument();
expect(screen.getByText('نام بیمه')).toBeInTheDocument();
// Manual "نوع بیمه" select is gone; kind is shown as a read-only chip from the tab.
expect(screen.queryByText('نوع بیمه')).not.toBeInTheDocument();
expect(screen.getByText('پایه')).toBeInTheDocument();
expect(screen.getByText('درصد پوشش — خدمات سرپایی')).toBeInTheDocument();
expect(screen.getByText('درصد پوشش — خدمات بستری')).toBeInTheDocument();
expect(screen.queryByText('فرانشیز (درصد)')).not.toBeInTheDocument();
expect(screen.getByText('سقف تعهد (تومان)')).toBeInTheDocument();
expect(screen.getByText('ثبت بیمه')).toBeInTheDocument();
});
it('shows the franchise field for a supplementary contract', () => {
renderWithProviders(
<InsuranceModal open editContract={null} options={options} categories={categories} kind="supplementary" onClose={() => {}} onSubmit={() => {}} />,
);
expect(screen.getByText('تکمیلی')).toBeInTheDocument();
expect(screen.getByText('فرانشیز (درصد)')).toBeInTheDocument();
});
it('blocks submit until every rendered category has a valid percent', () => {
const onSubmit = vi.fn();
renderWithProviders(
<InsuranceModal
open
editContract={mkContract({ category_coverages: { outpatient: 70 } })}
options={options}
categories={categories}
kind="basic"
onClose={() => {}}
onSubmit={onSubmit}
/>,
);
// بستری بدون درصد مانده: ثبت باید بسته باشد و پیام الزامی دیده شود.
expect(screen.getByText('ثبت بیمه')).toBeDisabled();
expect(screen.getByText('درصد پوشش الزامی است (۱ تا ۱۰۰)')).toBeInTheDocument();
fireEvent.change(screen.getByLabelText('درصد پوشش خدمات بستری'), { target: { value: '30' } });
expect(screen.getByText('ثبت بیمه')).not.toBeDisabled();
fireEvent.click(screen.getByText('ثبت بیمه'));
expect(onSubmit).toHaveBeenCalled();
});
it('renders only the service kinds it is given', () => {
renderWithProviders(
<InsuranceModal
open
editContract={null}
options={options}
categories={[{ key: 'outpatient', label: 'خدمات سرپایی' }]}
kind="basic"
onClose={() => {}}
onSubmit={() => {}}
/>,
);
expect(screen.getByText('درصد پوشش — خدمات سرپایی')).toBeInTheDocument();
expect(screen.queryByText('درصد پوشش — خدمات بستری')).not.toBeInTheDocument();
});
it('prefills the percents of an edited contract and marks admin defaults', () => {
renderWithProviders(
<InsuranceModal open editContract={mkContract()} options={options} categories={categories} kind="basic" onClose={() => {}} onSubmit={() => {}} />,
);
expect(screen.getByLabelText('درصد پوشش خدمات سرپایی')).toHaveValue('70');
expect(screen.getByLabelText('درصد پوشش خدمات بستری')).toHaveValue('30');
expect(screen.getByText('پیش‌فرض ادمین')).toBeInTheDocument();
});
it('makes the percents read-only without the update permission', () => {
renderWithProviders(
<InsuranceModal open editContract={mkContract()} options={options} categories={categories} kind="basic" canUpdate={false} onClose={() => {}} onSubmit={() => {}} />,
);
expect(screen.getByLabelText('درصد پوشش خدمات سرپایی')).toHaveAttribute('readonly');
expect(screen.getAllByText('مقدار پیش‌فرض تنظیمات مرکزی').length).toBe(2);
});
it('submits the built payload for an edited contract', () => {
const onSubmit = vi.fn();
renderWithProviders(
<InsuranceModal open editContract={mkContract()} options={options} categories={categories} kind="basic" onClose={() => {}} onSubmit={onSubmit} />,
);
fireEvent.click(screen.getByText('ثبت بیمه'));
expect(onSubmit).toHaveBeenCalledWith(expect.objectContaining({
insurance_id: 3, coverage_percent: 70, franchise_percent: 0, kind: 'basic',
category_coverages: [
{ key: 'outpatient', coverage_percent: 70 },
{ key: 'inpatient', coverage_percent: 30 },
],
}));
});
});