77 lines
2.9 KiB
TypeScript
77 lines
2.9 KiB
TypeScript
import { render, screen, fireEvent } from '@testing-library/react';
|
|
import { ThemeProvider } from '../../theme';
|
|
import type { NursePayoutHistoryItem, PayoutStatus } from '@/services/payouts/types';
|
|
|
|
jest.mock('next-intl', () => ({
|
|
useTranslations: () => (key: string) => key,
|
|
useLocale: () => 'en',
|
|
}));
|
|
|
|
import PayoutHistoryRow from './PayoutHistoryRow';
|
|
|
|
function makeItem(status: PayoutStatus, overrides: Partial<NursePayoutHistoryItem> = {}): NursePayoutHistoryItem {
|
|
return {
|
|
id: 9001,
|
|
batchId: 7001,
|
|
status,
|
|
grossEarningsIrr: '4250000',
|
|
clawbackAppliedIrr: '0',
|
|
netAmountIrr: '4250000',
|
|
maskedIban: 'IR••••4821',
|
|
transferReference: status === 'paid' ? 'PAYA-1' : null,
|
|
paidAt: status === 'paid' ? '2026-06-10T00:00:00Z' : null,
|
|
periodStart: '2026-06-01',
|
|
periodEnd: '2026-06-07',
|
|
failureReason: status === 'failed' ? 'invalid_sheba' : null,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function renderRow(item: NursePayoutHistoryItem, onOpen = jest.fn()) {
|
|
const utils = render(
|
|
<ThemeProvider>
|
|
<PayoutHistoryRow item={item} onOpen={onOpen} />
|
|
</ThemeProvider>,
|
|
);
|
|
return { ...utils, onOpen };
|
|
}
|
|
|
|
const STATUS_KIND: Array<{ status: PayoutStatus; kind: string }> = [
|
|
{ status: 'pending', kind: 'pending' },
|
|
{ status: 'submitted', kind: 'info' },
|
|
{ status: 'paid', kind: 'verified' },
|
|
{ status: 'failed', kind: 'rejected' },
|
|
];
|
|
|
|
describe('<PayoutHistoryRow/> component', () => {
|
|
it.each(STATUS_KIND)('maps the $status payout status to the $kind chip kind', ({ status, kind }) => {
|
|
const { container } = renderRow(makeItem(status));
|
|
expect(container.querySelector(`[data-payout-status="${status}"]`)).toBeInTheDocument();
|
|
expect(container.querySelector(`[data-status="${kind}"]`)).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders the net amount transferred in Toman and the masked IBAN', () => {
|
|
renderRow(makeItem('paid'));
|
|
expect(screen.getByText(/425,000/)).toBeInTheDocument(); // 4,250,000 ÷ 10
|
|
expect(screen.getByText('IR••••4821')).toBeInTheDocument();
|
|
});
|
|
|
|
it('surfaces a mapped failure label as the headline and the raw code as a secondary LTR caption', () => {
|
|
renderRow(makeItem('failed'));
|
|
expect(screen.getByText('failure_title')).toBeInTheDocument();
|
|
// The headline is the mapped Persian/English label (never the raw vendor string).
|
|
expect(screen.getByText('failure_code_invalid_sheba')).toBeInTheDocument();
|
|
// The raw code still appears, demoted to a secondary dir="ltr" caption.
|
|
expect(screen.getByText('invalid_sheba')).toHaveAttribute('dir', 'ltr');
|
|
// No retry affordance for the nurse — only the "view detail" link exists.
|
|
expect(screen.queryByText('retry')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('opens the payout detail on click', () => {
|
|
const onOpen = jest.fn();
|
|
renderRow(makeItem('paid', { id: 9002 }), onOpen);
|
|
fireEvent.click(screen.getByText('view_payout_detail'));
|
|
expect(onOpen).toHaveBeenCalledWith(9002);
|
|
});
|
|
});
|