- Implemented BlogBodySanitizer to clean HTML content before saving articles, ensuring security against XSS attacks. - Added tests for BlogBodySanitizer to verify that unsafe tags and attributes are stripped from the content. - Introduced ApiLeastPrivilegeTest to ensure that unauthorized users cannot access sensitive API routes, maintaining strict access control.
97 lines
4.5 KiB
TypeScript
97 lines
4.5 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { screen, fireEvent, waitFor } from '@testing-library/react';
|
|
import { renderWithProviders } from '../test/utils';
|
|
|
|
const navigate = vi.fn();
|
|
vi.mock('react-router', async (orig) => ({
|
|
...(await orig<typeof import('react-router')>()),
|
|
useNavigate: () => navigate,
|
|
}));
|
|
vi.mock('../lib/api', () => ({ api: { get: vi.fn() }, ApiError: class extends Error {} }));
|
|
|
|
import { api } from '../lib/api';
|
|
import MyPaymentsPage from './MyPaymentsPage';
|
|
|
|
const get = api.get as ReturnType<typeof vi.fn>;
|
|
|
|
const ROWS = [
|
|
{ invoice_uuid: 'iv1', patient_uuid: 'p1', patient_name: 'دنیا خلیلی', national_code: '1744023654',
|
|
issued_at: 1717000000, amount_rials: 2350000, paid_rials: 2350000, status: 'paid' },
|
|
{ invoice_uuid: 'iv2', patient_uuid: 'p2', patient_name: 'علی بدیعی', national_code: '2200112233',
|
|
issued_at: 1718000000, amount_rials: 6000000, paid_rials: 0, status: 'unsettled' },
|
|
{ invoice_uuid: 'iv3', patient_uuid: 'p3', patient_name: 'مریم رضایی', national_code: '3300112233',
|
|
issued_at: 1719000000, amount_rials: 1000000, paid_rials: 400000, status: 'partial' },
|
|
];
|
|
|
|
const SUMMARY = { total_rials: 8350000, paid_rials: 2350000, unsettled_rials: 6000000, invoices_count: 2 };
|
|
|
|
/** The page fires two queries; route by URL so each gets its own envelope. */
|
|
const mockApi = (rows = ROWS, total = rows.length) => {
|
|
get.mockImplementation((url: string) =>
|
|
url.includes('/payments/summary')
|
|
? Promise.resolve({ success: true, data: SUMMARY })
|
|
: Promise.resolve({ success: true, data: rows, meta: { totalRecords: total, totalPages: 1, currentPage: 1 } }),
|
|
);
|
|
};
|
|
|
|
beforeEach(() => {
|
|
navigate.mockReset();
|
|
get.mockReset();
|
|
mockApi();
|
|
});
|
|
|
|
describe('MyPaymentsPage (لیست پرداختها)', () => {
|
|
it('renders a flat row per invoice with patient name and national code', async () => {
|
|
renderWithProviders(<MyPaymentsPage />, { route: '/admin/my-payments' });
|
|
expect(await screen.findByText('دنیا خلیلی')).toBeInTheDocument();
|
|
expect(screen.getByText('علی بدیعی')).toBeInTheDocument();
|
|
expect(screen.getByText('1744023654')).toBeInTheDocument();
|
|
expect(screen.getByText('2200112233')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders the derived payment status badge for each state', async () => {
|
|
renderWithProviders(<MyPaymentsPage />, { route: '/admin/my-payments' });
|
|
expect(await screen.findByText('پرداخت شده')).toBeInTheDocument();
|
|
expect(screen.getByText('تسویه نشده')).toBeInTheDocument();
|
|
expect(screen.getByText('پرداخت ناقص')).toBeInTheDocument();
|
|
});
|
|
|
|
it('shows how much was collected on a partially paid invoice', async () => {
|
|
renderWithProviders(<MyPaymentsPage />, { route: '/admin/my-payments' });
|
|
expect(await screen.findByText(/پرداختشده:/)).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders the summary stat cards', async () => {
|
|
renderWithProviders(<MyPaymentsPage />, { route: '/admin/my-payments' });
|
|
expect(await screen.findByText('مجموع صورتحسابها')).toBeInTheDocument();
|
|
expect(screen.getByText('پرداختشده')).toBeInTheDocument();
|
|
expect(screen.getByText('تسویهنشده')).toBeInTheDocument();
|
|
expect(screen.getByText('تعداد صورتحساب')).toBeInTheDocument();
|
|
});
|
|
|
|
it('navigates to the patient detail on جزئیات', async () => {
|
|
renderWithProviders(<MyPaymentsPage />, { route: '/admin/my-payments' });
|
|
await screen.findByText('دنیا خلیلی');
|
|
fireEvent.click(screen.getAllByRole('button', { name: /جزئیات/ })[0]);
|
|
expect(navigate).toHaveBeenCalledWith('/admin/my-payments/p1');
|
|
});
|
|
|
|
it('shows an empty state when there are no payments', async () => {
|
|
mockApi([], 0);
|
|
renderWithProviders(<MyPaymentsPage />, { route: '/admin/my-payments' });
|
|
expect(await screen.findByText('پرداختی ثبت نشده است.')).toBeInTheDocument();
|
|
});
|
|
|
|
it('sends the national_code filter to both the list and the summary', async () => {
|
|
renderWithProviders(<MyPaymentsPage />, { route: '/admin/my-payments' });
|
|
await screen.findByText('دنیا خلیلی');
|
|
fireEvent.change(screen.getByPlaceholderText('کد ملی بیمار'), { target: { value: '1744' } });
|
|
|
|
await waitFor(() => {
|
|
const urls = get.mock.calls.map(([u]) => String(u)).filter((u) => u.includes('national_code=1744'));
|
|
expect(urls.some((u) => u.includes('/payments?'))).toBe(true);
|
|
expect(urls.some((u) => u.includes('/payments/summary?'))).toBe(true);
|
|
});
|
|
});
|
|
});
|