Files
clinicpro/assets/admin/pages/ClinicServicesPage.test.tsx
T
hamedandClaude Opus 4.8 c79720ee79 refactor(services): merge insurance coverage into the service edit modal
- extract ContractCard + contracts list from ServiceInsuranceModal into a
  new inline ServiceInsuranceSection (no dialog wrapper; buttons type=button
  so they don't submit the parent service form)
- ClinicServicesPage: drop the separate 'پوشش بیمه' menu action and its
  standalone modal; render coverage inline under the service fields in the
  edit modal (edit only — needs the item uuid; create shows a hint)
- delete now-unused ServiceInsuranceModal.tsx
- fix undefined --primary-subtle token → --primary-soft
- test: coverage renders inline inside the service edit modal

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 15:58:32 +03:30

79 lines
4.1 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { screen, fireEvent } 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 { useAuthStore } from '../stores/authStore';
import ClinicServicesPage from './ClinicServicesPage';
const get = api.get as ReturnType<typeof vi.fn>;
beforeEach(() => {
// FeatureGate → useSubscription only fetches for doctor/clinic/secretary roles
useAuthStore.setState({ primaryRole: 'doctor' });
get.mockReset();
get.mockImplementation((url: string) => {
if (url.includes('/subscription/my')) return Promise.resolve({ success: true, data: {
subscription: null, used_trial: false,
effective_plan: { name: 'professional', level: 2, max_secretaries: 10, features: { services: true } },
} });
if (url.includes('/payment/config')) return Promise.resolve({ success: true, data: { test_mode: true, gateways: [] } });
if (url.includes('/service-sections')) return Promise.resolve({ success: true, data: [
{ uuid: 'sec1', name: 'کندلا ۲۰۲۱', active: true, items_count: 10 },
] });
if (url.includes('/service-items/sec1')) return Promise.resolve({ success: true, data: [
{ uuid: 'it1', name: 'فول بادی', price_rials: 35_000_000, active: true, duration_minutes: 50,
staff: { uuid: 'st1', full_name: 'مریم امینی' },
staff_members: [{ uuid: 'st1', full_name: 'مریم امینی' }, { uuid: 'st2', full_name: 'سحر رحمانی' }] },
] });
if (url.includes('/staff')) return Promise.resolve({ success: true, data: [
{ uuid: 'st1', full_name: 'مریم امینی', active: true },
{ uuid: 'st2', full_name: 'سحر رحمانی', active: true },
] });
// coverage before tenant-insurances (coverage URL also contains "tenant-insurances")
if (url.includes('/service-coverage')) return Promise.resolve({ success: true, data: { data: [] } });
if (url.includes('/tenant-insurances')) return Promise.resolve({ success: true, data: { data: [
{ uuid: 'ins1', insurance_name: 'تأمین اجتماعی', insurance_kind: 'basic', coverage_percent: 70 },
] } });
return Promise.resolve({ success: true, data: [] });
});
});
describe('ClinicServicesPage (خدمات)', () => {
it('renders section cards with the service count inside the settings shell', async () => {
renderWithProviders(<ClinicServicesPage />, { route: '/admin/clinic-services' });
// settings shell menu + section card
expect(await screen.findByText('کندلا ۲۰۲۱')).toBeInTheDocument();
expect(screen.getByText(/۱۰ سرویس/)).toBeInTheDocument();
expect(screen.getByText('بخش جدید')).toBeInTheDocument();
});
it('drills into a section and shows a service with all its personnel chips', async () => {
renderWithProviders(<ClinicServicesPage />, { route: '/admin/clinic-services' });
fireEvent.click(await screen.findByText('کندلا ۲۰۲۱'));
expect(await screen.findByText('فول بادی')).toBeInTheDocument();
expect(screen.getByText('مریم امینی')).toBeInTheDocument();
expect(screen.getByText('سحر رحمانی')).toBeInTheDocument();
expect(screen.getByText('سرویس جدید')).toBeInTheDocument();
});
it('embeds per-insurer coverage inline inside the service edit modal', async () => {
renderWithProviders(<ClinicServicesPage />, { route: '/admin/clinic-services' });
fireEvent.click(await screen.findByText('کندلا ۲۰۲۱'));
// open the ⋮ actions menu, then ویرایش
fireEvent.click(await screen.findByTitle('عملیات'));
fireEvent.click(await screen.findByText('ویرایش'));
// insurance section + contract render inside the same (edit) modal
expect(await screen.findByText('پوشش بیمه')).toBeInTheDocument();
expect(await screen.findByText('تأمین اجتماعی')).toBeInTheDocument();
});
});