@openmrs/esm-billing-app 1.0.2-pre.613 → 1.0.2-pre.619
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -6
- package/__mocks__/react-i18next.js +6 -5
- package/dist/2352.js +1 -1
- package/dist/2352.js.map +1 -1
- package/dist/4300.js +1 -1
- package/dist/4739.js +1 -1
- package/dist/4739.js.map +1 -1
- package/dist/7239.js +1 -1
- package/dist/7239.js.map +1 -1
- package/dist/8638.js +1 -1
- package/dist/8638.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-billing-app.js +1 -1
- package/dist/openmrs-esm-billing-app.js.buildmanifest.json +19 -19
- package/dist/routes.json +1 -1
- package/e2e/README.md +19 -18
- package/package.json +2 -2
- package/src/bill-history/bill-history.test.tsx +37 -77
- package/src/bill-item-actions/edit-bill-item.component.tsx +7 -3
- package/src/bill-item-actions/edit-bill-item.test.tsx +17 -19
- package/src/billable-services/bill-waiver/patient-bills.component.tsx +3 -3
- package/src/billable-services/billable-service.resource.ts +4 -3
- package/src/billable-services/billable-services.component.tsx +5 -4
- package/src/billable-services/billable-services.test.tsx +3 -44
- package/src/billable-services/cash-point/cash-point-configuration.component.tsx +2 -2
- package/src/billable-services/create-edit/add-billable-service.component.tsx +2 -2
- package/src/billable-services/create-edit/add-billable-service.test.tsx +6 -6
- package/src/billable-services/payyment-modes/payment-modes-config.component.tsx +1 -1
- package/src/billing-form/billing-checkin-form.component.tsx +1 -2
- package/src/billing-form/billing-checkin-form.test.tsx +0 -2
- package/src/billing-form/billing-form.component.tsx +8 -4
- package/src/billing.resource.ts +13 -9
- package/src/bills-table/bills-table.test.tsx +97 -53
- package/src/config-schema.ts +52 -18
- package/src/invoice/invoice-table.component.tsx +9 -9
- package/src/invoice/invoice-table.scss +1 -5
- package/src/invoice/invoice-table.test.tsx +24 -39
- package/src/invoice/invoice.component.tsx +8 -6
- package/src/invoice/invoice.scss +7 -4
- package/src/invoice/invoice.test.tsx +12 -47
- package/src/invoice/payments/payment-form/payment-form.test.tsx +8 -10
- package/src/invoice/payments/payment-history/payment-history.component.tsx +6 -4
- package/src/invoice/payments/payment-history/payment-history.test.tsx +9 -14
- package/src/invoice/payments/payments.component.test.tsx +20 -6
- package/src/invoice/printable-invoice/print-receipt.component.tsx +1 -1
- package/src/invoice/printable-invoice/print-receipt.test.tsx +14 -25
- package/src/invoice/printable-invoice/printable-footer.component.tsx +2 -2
- package/src/invoice/printable-invoice/printable-footer.test.tsx +4 -13
- package/src/invoice/printable-invoice/printable-invoice-header.component.tsx +5 -4
- package/src/invoice/printable-invoice/printable-invoice-header.test.tsx +11 -11
- package/src/invoice/printable-invoice/printable-invoice.component.tsx +11 -11
- package/src/metrics-cards/metrics-cards.test.tsx +18 -5
- package/src/modal/require-payment-modal.test.tsx +24 -19
- package/translations/en.json +18 -0
|
@@ -65,13 +65,13 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isSelectable = true,
|
|
|
65
65
|
}, [debouncedSearchTerm, lineItems]);
|
|
66
66
|
|
|
67
67
|
const tableHeaders = [
|
|
68
|
-
{ header: 'No', key: 'no', width: 7 }, // Width as a percentage
|
|
69
|
-
{ header: 'Bill item', key: 'billItem', width: 25 },
|
|
70
|
-
{ header: 'Bill code', key: 'billCode', width: 20 },
|
|
71
|
-
{ header: 'Status', key: 'status', width: 25 },
|
|
72
|
-
{ header: 'Quantity', key: 'quantity', width: 15 },
|
|
73
|
-
{ header: 'Price', key: 'price', width: 24 },
|
|
74
|
-
{ header: 'Total', key: 'total', width: 15 },
|
|
68
|
+
{ header: t('number', 'No'), key: 'no', width: 7 }, // Width as a percentage
|
|
69
|
+
{ header: t('billItem', 'Bill item'), key: 'billItem', width: 25 },
|
|
70
|
+
{ header: t('billCode', 'Bill code'), key: 'billCode', width: 20 },
|
|
71
|
+
{ header: t('status', 'Status'), key: 'status', width: 25 },
|
|
72
|
+
{ header: t('quantity', 'Quantity'), key: 'quantity', width: 15 },
|
|
73
|
+
{ header: t('price', 'Price'), key: 'price', width: 24 },
|
|
74
|
+
{ header: t('total', 'Total'), key: 'total', width: 15 },
|
|
75
75
|
{ header: t('actions', 'Actions'), key: 'actionButton' },
|
|
76
76
|
];
|
|
77
77
|
|
|
@@ -149,7 +149,7 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isSelectable = true,
|
|
|
149
149
|
};
|
|
150
150
|
|
|
151
151
|
return (
|
|
152
|
-
|
|
152
|
+
<>
|
|
153
153
|
<DataTable headers={tableHeaders} isSortable rows={tableRows} size={responsiveSize} useZebraStyles>
|
|
154
154
|
{({ rows, headers, getRowProps, getSelectionProps, getTableProps, getToolbarProps }) => (
|
|
155
155
|
<TableContainer
|
|
@@ -221,7 +221,7 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isSelectable = true,
|
|
|
221
221
|
</Layer>
|
|
222
222
|
</div>
|
|
223
223
|
)}
|
|
224
|
-
|
|
224
|
+
</>
|
|
225
225
|
);
|
|
226
226
|
};
|
|
227
227
|
|
|
@@ -1,43 +1,24 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
3
|
-
import { render, screen,
|
|
4
|
-
import { showModal } from '@openmrs/esm-framework';
|
|
5
|
-
import InvoiceTable from './invoice-table.component';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { render, screen, act } from '@testing-library/react';
|
|
4
|
+
import { getDefaultsFromConfigSchema, showModal, useConfig } from '@openmrs/esm-framework';
|
|
6
5
|
import { type MappedBill } from '../types';
|
|
6
|
+
import { configSchema, type BillingConfig } from '../config-schema';
|
|
7
|
+
import InvoiceTable from './invoice-table.component';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
jest.mock('react-i18next', () => ({
|
|
10
|
-
useTranslation: jest.fn(() => ({
|
|
11
|
-
t: jest.fn((key, fallback) => fallback || key),
|
|
12
|
-
i18n: { language: 'en' },
|
|
13
|
-
})),
|
|
14
|
-
}));
|
|
15
|
-
|
|
16
|
-
jest.mock('@openmrs/esm-framework', () => ({
|
|
17
|
-
showModal: jest.fn(),
|
|
18
|
-
useConfig: jest.fn(() => ({
|
|
19
|
-
defaultCurrency: 'USD',
|
|
20
|
-
showEditBillButton: true,
|
|
21
|
-
})),
|
|
22
|
-
useDebounce: jest.fn((value) => value),
|
|
23
|
-
useLayoutType: jest.fn(() => 'desktop'),
|
|
24
|
-
isDesktop: jest.fn(() => true),
|
|
25
|
-
}));
|
|
9
|
+
const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
|
|
26
10
|
|
|
27
11
|
jest.mock('../helpers', () => ({
|
|
28
12
|
convertToCurrency: jest.fn((price) => `USD ${price}`),
|
|
29
13
|
}));
|
|
30
14
|
|
|
31
15
|
describe('InvoiceTable', () => {
|
|
32
|
-
const mockT = jest.fn((key) => key);
|
|
33
|
-
|
|
34
16
|
beforeEach(() => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
jest.useRealTimers();
|
|
17
|
+
mockUseConfig.mockReturnValue({
|
|
18
|
+
...getDefaultsFromConfigSchema(configSchema),
|
|
19
|
+
defaultCurrency: 'USD',
|
|
20
|
+
showEditBillButton: true,
|
|
21
|
+
});
|
|
41
22
|
});
|
|
42
23
|
|
|
43
24
|
const bill: MappedBill = {
|
|
@@ -103,11 +84,12 @@ describe('InvoiceTable', () => {
|
|
|
103
84
|
expect(screen.getByTestId('receipt-number-0')).toHaveTextContent('12345');
|
|
104
85
|
});
|
|
105
86
|
|
|
106
|
-
it('renders the edit button and calls showModal when clicked', () => {
|
|
87
|
+
it('renders the edit button and calls showModal when clicked', async () => {
|
|
88
|
+
const user = userEvent.setup();
|
|
107
89
|
render(<InvoiceTable bill={bill} />);
|
|
108
90
|
|
|
109
91
|
const editButton = screen.getByTestId('edit-button-1');
|
|
110
|
-
|
|
92
|
+
await user.click(editButton);
|
|
111
93
|
expect(showModal).toHaveBeenCalledWith('edit-bill-line-item-dialog', expect.anything());
|
|
112
94
|
});
|
|
113
95
|
|
|
@@ -117,31 +99,34 @@ describe('InvoiceTable', () => {
|
|
|
117
99
|
expect(screen.getByTestId('loader')).toBeInTheDocument();
|
|
118
100
|
});
|
|
119
101
|
|
|
120
|
-
it('filters line items based on the search term', () => {
|
|
102
|
+
it('filters line items based on the search term', async () => {
|
|
103
|
+
const user = userEvent.setup();
|
|
121
104
|
render(<InvoiceTable bill={bill} />);
|
|
122
|
-
const searchInput = screen.getByPlaceholderText(
|
|
105
|
+
const searchInput = screen.getByPlaceholderText(/search this table/i);
|
|
123
106
|
|
|
124
|
-
|
|
107
|
+
await user.type(searchInput, 'Item 2');
|
|
125
108
|
|
|
126
109
|
expect(screen.queryByText('Item 1')).not.toBeInTheDocument();
|
|
127
110
|
expect(screen.getByText('Item 2')).toBeInTheDocument();
|
|
128
111
|
});
|
|
129
112
|
|
|
130
|
-
it('correctly handles row selection', () => {
|
|
113
|
+
it('correctly handles row selection', async () => {
|
|
114
|
+
const user = userEvent.setup();
|
|
131
115
|
const onSelectItem = jest.fn();
|
|
132
116
|
render(<InvoiceTable bill={bill} onSelectItem={onSelectItem} />);
|
|
133
117
|
|
|
134
118
|
const checkboxes = screen.getAllByLabelText('Select row');
|
|
135
|
-
|
|
119
|
+
await user.click(checkboxes[0]);
|
|
136
120
|
|
|
137
121
|
expect(onSelectItem).toHaveBeenCalledWith([bill.lineItems[0]]);
|
|
138
122
|
});
|
|
139
123
|
|
|
140
|
-
it('resets isRedirecting to false after timeout', () => {
|
|
124
|
+
it('resets isRedirecting to false after timeout', async () => {
|
|
125
|
+
const user = userEvent.setup();
|
|
141
126
|
render(<InvoiceTable bill={bill} />);
|
|
142
127
|
|
|
143
128
|
const button = screen.getByTestId('edit-button-1');
|
|
144
|
-
|
|
129
|
+
await user.click(button);
|
|
145
130
|
act(() => {
|
|
146
131
|
jest.advanceTimersByTime(1000);
|
|
147
132
|
});
|
|
@@ -4,7 +4,7 @@ import { Printer } from '@carbon/react/icons';
|
|
|
4
4
|
import { useParams } from 'react-router-dom';
|
|
5
5
|
import { useReactToPrint } from 'react-to-print';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
|
-
import { ExtensionSlot, useConfig, usePatient } from '@openmrs/esm-framework';
|
|
7
|
+
import { ExtensionSlot, showSnackbar, useConfig, usePatient } from '@openmrs/esm-framework';
|
|
8
8
|
import { ErrorState } from '@openmrs/esm-patient-common-lib';
|
|
9
9
|
import { convertToCurrency } from '../helpers';
|
|
10
10
|
import { type LineItem } from '../types';
|
|
@@ -13,6 +13,7 @@ import InvoiceTable from './invoice-table.component';
|
|
|
13
13
|
import Payments from './payments/payments.component';
|
|
14
14
|
import PrintReceipt from './printable-invoice/print-receipt.component';
|
|
15
15
|
import PrintableInvoice from './printable-invoice/printable-invoice.component';
|
|
16
|
+
import type { BillingConfig } from '../config-schema';
|
|
16
17
|
import styles from './invoice.scss';
|
|
17
18
|
|
|
18
19
|
interface InvoiceDetailsProps {
|
|
@@ -29,7 +30,7 @@ const Invoice: React.FC = () => {
|
|
|
29
30
|
const [selectedLineItems, setSelectedLineItems] = useState<LineItem[]>([]);
|
|
30
31
|
const componentRef = useRef<HTMLDivElement>(null);
|
|
31
32
|
const onBeforeGetContentResolve = useRef<(() => void) | null>(null);
|
|
32
|
-
const { defaultCurrency } = useConfig();
|
|
33
|
+
const { defaultCurrency } = useConfig<BillingConfig>();
|
|
33
34
|
const handleSelectItem = (lineItems: LineItem[]) => {
|
|
34
35
|
setSelectedLineItems(lineItems);
|
|
35
36
|
};
|
|
@@ -51,11 +52,12 @@ const Invoice: React.FC = () => {
|
|
|
51
52
|
}, [bill, patient]);
|
|
52
53
|
|
|
53
54
|
const handlePrint = useReactToPrint({
|
|
54
|
-
content: reactToPrintContent,
|
|
55
55
|
documentTitle: `Invoice ${bill?.receiptNumber} - ${patient?.name?.[0]?.given?.join(' ')} ${patient?.name?.[0].family}`,
|
|
56
|
-
|
|
56
|
+
onBeforePrint: handleOnBeforeGetContent,
|
|
57
57
|
onAfterPrint: handleAfterPrint,
|
|
58
|
-
|
|
58
|
+
preserveAfterPrint: false,
|
|
59
|
+
onPrintError: (_, error) =>
|
|
60
|
+
showSnackbar({ title: t('printError', 'Error printing invoice'), kind: 'error', subtitle: error.message }),
|
|
59
61
|
});
|
|
60
62
|
|
|
61
63
|
useEffect(() => {
|
|
@@ -110,7 +112,7 @@ const Invoice: React.FC = () => {
|
|
|
110
112
|
<div>
|
|
111
113
|
<Button
|
|
112
114
|
disabled={isPrinting}
|
|
113
|
-
onClick={handlePrint}
|
|
115
|
+
onClick={() => handlePrint(reactToPrintContent)}
|
|
114
116
|
renderIcon={(props) => <Printer size={24} {...props} />}
|
|
115
117
|
iconDescription="Print bill"
|
|
116
118
|
size="md">
|
package/src/invoice/invoice.scss
CHANGED
|
@@ -78,16 +78,19 @@
|
|
|
78
78
|
color: colors.$cool-gray-90;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
.printContainer {
|
|
82
|
+
background-color: colors.$white;
|
|
83
|
+
display: none;
|
|
84
|
+
|
|
85
|
+
@media print {
|
|
86
|
+
display: block !important;
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
@media print {
|
|
89
91
|
html,
|
|
90
92
|
body {
|
|
93
|
+
display: block !important;
|
|
91
94
|
background-color: colors.$white !important;
|
|
92
95
|
}
|
|
93
96
|
}
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { screen, render } from '@testing-library/react';
|
|
3
2
|
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { screen, render } from '@testing-library/react';
|
|
4
4
|
import { useReactToPrint } from 'react-to-print';
|
|
5
|
+
import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
|
|
6
|
+
import { configSchema, type BillingConfig } from '../config-schema';
|
|
5
7
|
import { mockBill } from '../../__mocks__/bills.mock';
|
|
6
8
|
import { useBill, processBillPayment } from '../billing.resource';
|
|
7
9
|
import { usePaymentModes } from './payments/payment.resource';
|
|
8
10
|
import Invoice from './invoice.component';
|
|
9
11
|
|
|
12
|
+
const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
|
|
13
|
+
|
|
10
14
|
// Mock convertToCurrency
|
|
11
15
|
jest.mock('../helpers/functions', () => ({
|
|
12
16
|
convertToCurrency: jest.fn((amount) => `USD ${amount}`),
|
|
13
17
|
}));
|
|
14
18
|
|
|
15
|
-
// Mock i18next
|
|
16
|
-
jest.mock('react-i18next', () => ({
|
|
17
|
-
useTranslation: () => ({
|
|
18
|
-
t: (key: string) => key,
|
|
19
|
-
}),
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
19
|
// Set window.i18next
|
|
23
20
|
window.i18next = {
|
|
24
21
|
language: 'en',
|
|
@@ -78,38 +75,6 @@ jest.mock('react-to-print', () => ({
|
|
|
78
75
|
useReactToPrint: jest.fn(),
|
|
79
76
|
}));
|
|
80
77
|
|
|
81
|
-
// Mock OpenMRS framework
|
|
82
|
-
jest.mock('@openmrs/esm-framework', () => ({
|
|
83
|
-
showSnackbar: jest.fn(),
|
|
84
|
-
useLayoutType: jest.fn(() => 'desktop'),
|
|
85
|
-
isDesktop: jest.fn(() => true),
|
|
86
|
-
useConfig: jest.fn(() => ({
|
|
87
|
-
defaultCurrency: 'USD',
|
|
88
|
-
})),
|
|
89
|
-
formatDate: jest.fn((date) => date?.toString() ?? ''),
|
|
90
|
-
ExtensionSlot: jest.fn(({ children }) => <div data-testid="extension-slot">{children}</div>),
|
|
91
|
-
usePatient: jest.fn().mockReturnValue({
|
|
92
|
-
patient: {
|
|
93
|
-
id: 'b2fcf02b-7ee3-4d16-a48f-576be2b103aa',
|
|
94
|
-
name: [{ given: ['John'], family: 'Doe' }],
|
|
95
|
-
},
|
|
96
|
-
patientUuid: 'b2fcf02b-7ee3-4d16-a48f-576be2b103aa',
|
|
97
|
-
isLoading: false,
|
|
98
|
-
error: null,
|
|
99
|
-
}),
|
|
100
|
-
createGlobalStore: jest.fn(),
|
|
101
|
-
getGlobalStore: jest.fn(() => ({
|
|
102
|
-
subscribe: jest.fn(),
|
|
103
|
-
getState: jest.fn(),
|
|
104
|
-
setState: jest.fn(),
|
|
105
|
-
})),
|
|
106
|
-
}));
|
|
107
|
-
|
|
108
|
-
// Mock patient common lib
|
|
109
|
-
jest.mock('@openmrs/esm-patient-common-lib', () => ({
|
|
110
|
-
ErrorState: jest.fn(({ error }) => <div data-testid="error-state">Error: {error?.message || error}</div>),
|
|
111
|
-
}));
|
|
112
|
-
|
|
113
78
|
describe('Invoice', () => {
|
|
114
79
|
const mockedBill = useBill as jest.Mock;
|
|
115
80
|
const mockedProcessBillPayment = processBillPayment as jest.Mock;
|
|
@@ -154,15 +119,13 @@ describe('Invoice', () => {
|
|
|
154
119
|
mutate: jest.fn(),
|
|
155
120
|
});
|
|
156
121
|
|
|
122
|
+
mockUseConfig.mockReturnValue({ ...getDefaultsFromConfigSchema(configSchema), defaultCurrency: 'USD' });
|
|
123
|
+
|
|
157
124
|
// Setup print handler mock
|
|
158
125
|
const printHandler = jest.fn();
|
|
159
126
|
mockedUseReactToPrint.mockReturnValue(printHandler);
|
|
160
127
|
});
|
|
161
128
|
|
|
162
|
-
afterEach(() => {
|
|
163
|
-
jest.clearAllMocks();
|
|
164
|
-
});
|
|
165
|
-
|
|
166
129
|
it('should render error state correctly', () => {
|
|
167
130
|
mockedBill.mockReturnValue({
|
|
168
131
|
bill: null,
|
|
@@ -173,8 +136,8 @@ describe('Invoice', () => {
|
|
|
173
136
|
});
|
|
174
137
|
|
|
175
138
|
render(<Invoice />);
|
|
176
|
-
expect(screen.
|
|
177
|
-
expect(screen.getByText(/
|
|
139
|
+
expect(screen.getByText(/Invoice error/i)).toBeInTheDocument();
|
|
140
|
+
expect(screen.getByText(/Error/)).toBeInTheDocument();
|
|
178
141
|
});
|
|
179
142
|
|
|
180
143
|
it('should render invoice details correctly', () => {
|
|
@@ -265,7 +228,9 @@ describe('Invoice', () => {
|
|
|
265
228
|
|
|
266
229
|
it('should show patient information correctly', () => {
|
|
267
230
|
render(<Invoice />);
|
|
268
|
-
|
|
231
|
+
// Check that the invoice details are rendered
|
|
232
|
+
expect(screen.getByText('Invoice Number')).toBeInTheDocument();
|
|
233
|
+
expect(screen.getByText('RCPT-001')).toBeInTheDocument();
|
|
269
234
|
});
|
|
270
235
|
|
|
271
236
|
// Add more test cases as needed for specific features or edge cases
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
3
4
|
import { FormProvider, useForm } from 'react-hook-form';
|
|
4
5
|
import type { PaymentFormValue } from '../payments.component';
|
|
5
6
|
import PaymentForm from './payment-form.component';
|
|
6
7
|
|
|
7
|
-
// Mock the payment resource
|
|
8
8
|
jest.mock('../payment.resource', () => ({
|
|
9
9
|
usePaymentModes: jest.fn(),
|
|
10
10
|
}));
|
|
@@ -21,10 +21,6 @@ const Wrapper: React.FC<WrapperProps> = ({ children }) => {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
describe('PaymentForm Component', () => {
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
jest.clearAllMocks();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
24
|
test('should render skeleton while loading payment modes', () => {
|
|
29
25
|
usePaymentModes.mockReturnValue({
|
|
30
26
|
paymentModes: [],
|
|
@@ -96,7 +92,8 @@ describe('PaymentForm Component', () => {
|
|
|
96
92
|
expect(screen.getByPlaceholderText(/enter amount/i)).toBeInTheDocument();
|
|
97
93
|
});
|
|
98
94
|
|
|
99
|
-
test('should append a payment field when add payment option button is clicked', () => {
|
|
95
|
+
test('should append a payment field when add payment option button is clicked', async () => {
|
|
96
|
+
const user = userEvent.setup();
|
|
100
97
|
usePaymentModes.mockReturnValue({
|
|
101
98
|
paymentModes: [{ uuid: '1', name: 'Credit Card' }],
|
|
102
99
|
isLoading: false,
|
|
@@ -116,7 +113,7 @@ describe('PaymentForm Component', () => {
|
|
|
116
113
|
);
|
|
117
114
|
|
|
118
115
|
const addButton = screen.getByText(/add payment option/i);
|
|
119
|
-
|
|
116
|
+
await user.click(addButton);
|
|
120
117
|
const paymentMethodElements = screen.getAllByLabelText(/payment method/i);
|
|
121
118
|
expect(paymentMethodElements).toHaveLength(2);
|
|
122
119
|
});
|
|
@@ -144,6 +141,7 @@ describe('PaymentForm Component', () => {
|
|
|
144
141
|
});
|
|
145
142
|
|
|
146
143
|
test('should remove payment field when trash can icon is clicked', async () => {
|
|
144
|
+
const user = userEvent.setup();
|
|
147
145
|
usePaymentModes.mockReturnValue({
|
|
148
146
|
paymentModes: [{ uuid: '1', name: 'Credit Card' }],
|
|
149
147
|
isLoading: false,
|
|
@@ -162,10 +160,10 @@ describe('PaymentForm Component', () => {
|
|
|
162
160
|
</Wrapper>,
|
|
163
161
|
);
|
|
164
162
|
|
|
165
|
-
|
|
163
|
+
await user.click(screen.getByText(/add payment option/i));
|
|
166
164
|
|
|
167
165
|
const trashCanIcon = screen.getByTestId('trash-can-icon');
|
|
168
|
-
|
|
166
|
+
await user.click(trashCanIcon);
|
|
169
167
|
|
|
170
168
|
await waitFor(() => {
|
|
171
169
|
expect(screen.queryByPlaceholderText(/enter amount/i)).not.toBeInTheDocument();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { DataTable, Table, TableHead, TableRow, TableHeader, TableBody, TableCell } from '@carbon/react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
3
4
|
import { type MappedBill } from '../../../types';
|
|
4
5
|
import { formatDate, useConfig } from '@openmrs/esm-framework';
|
|
5
6
|
import { convertToCurrency } from '../../../helpers';
|
|
@@ -9,23 +10,24 @@ type PaymentHistoryProps = {
|
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
const PaymentHistory: React.FC<PaymentHistoryProps> = ({ bill }) => {
|
|
13
|
+
const { t } = useTranslation();
|
|
12
14
|
const { defaultCurrency } = useConfig();
|
|
13
15
|
const headers = [
|
|
14
16
|
{
|
|
15
17
|
key: 'dateCreated',
|
|
16
|
-
header: 'Date of payment',
|
|
18
|
+
header: t('dateOfPayment', 'Date of payment'),
|
|
17
19
|
},
|
|
18
20
|
{
|
|
19
21
|
key: 'amount',
|
|
20
|
-
header: 'Bill amount',
|
|
22
|
+
header: t('billAmount', 'Bill amount'),
|
|
21
23
|
},
|
|
22
24
|
{
|
|
23
25
|
key: 'amountTendered',
|
|
24
|
-
header: 'Amount tendered',
|
|
26
|
+
header: t('amountTendered', 'Amount tendered'),
|
|
25
27
|
},
|
|
26
28
|
{
|
|
27
29
|
key: 'paymentMethod',
|
|
28
|
-
header: 'Payment method',
|
|
30
|
+
header: t('paymentMethod', 'Payment method'),
|
|
29
31
|
},
|
|
30
32
|
];
|
|
31
33
|
const rows = bill?.payments?.map((payment, index) => {
|
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render, screen } from '@testing-library/react';
|
|
3
|
-
import { useConfig } from '@openmrs/esm-framework';
|
|
4
|
-
import
|
|
3
|
+
import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
|
|
4
|
+
import { type BillingConfig, configSchema } from '../../../config-schema';
|
|
5
5
|
import { type MappedBill } from '../../../types';
|
|
6
|
-
|
|
7
|
-
// Mocking useConfig to return a default currency
|
|
8
|
-
jest.mock('@openmrs/esm-framework', () => ({
|
|
9
|
-
useConfig: jest.fn(),
|
|
10
|
-
formatDate: jest.fn((date) => date.toISOString().split('T')[0]),
|
|
11
|
-
}));
|
|
6
|
+
import PaymentHistory from './payment-history.component';
|
|
12
7
|
|
|
13
8
|
jest.mock('../../../helpers', () => ({
|
|
14
9
|
convertToCurrency: jest.fn((amount, currency) => `${currency} ${amount.toFixed(2)}`),
|
|
15
10
|
}));
|
|
16
11
|
|
|
12
|
+
const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
|
|
13
|
+
|
|
17
14
|
describe('PaymentHistory Component', () => {
|
|
18
15
|
beforeEach(() => {
|
|
19
|
-
(
|
|
20
|
-
defaultCurrency: 'USD',
|
|
21
|
-
});
|
|
16
|
+
mockUseConfig.mockReturnValue({ ...getDefaultsFromConfigSchema(configSchema), defaultCurrency: 'USD' });
|
|
22
17
|
});
|
|
23
18
|
|
|
24
19
|
const mockBill: MappedBill = {
|
|
@@ -129,12 +124,12 @@ describe('PaymentHistory Component', () => {
|
|
|
129
124
|
test('renders correct data in the rows', () => {
|
|
130
125
|
render(<PaymentHistory bill={mockBill} />);
|
|
131
126
|
|
|
132
|
-
expect(screen.getByText('
|
|
127
|
+
expect(screen.getByText('01-Sept-2023')).toBeInTheDocument();
|
|
133
128
|
expect(screen.getByText('USD 80.00')).toBeInTheDocument();
|
|
134
129
|
expect(screen.getByText('USD 100.00')).toBeInTheDocument();
|
|
135
130
|
expect(screen.getByText('Credit Card')).toBeInTheDocument();
|
|
136
131
|
|
|
137
|
-
expect(screen.getByText('
|
|
132
|
+
expect(screen.getByText('05-Sept-2023')).toBeInTheDocument();
|
|
138
133
|
expect(screen.getByText('USD 180.00')).toBeInTheDocument();
|
|
139
134
|
expect(screen.getByText('USD 200.00')).toBeInTheDocument();
|
|
140
135
|
expect(screen.getByText('Cash')).toBeInTheDocument();
|
|
@@ -153,7 +148,7 @@ describe('PaymentHistory Component', () => {
|
|
|
153
148
|
test('formats dates and converts amounts correctly', () => {
|
|
154
149
|
render(<PaymentHistory bill={mockBill} />);
|
|
155
150
|
|
|
156
|
-
expect(screen.getByText('
|
|
151
|
+
expect(screen.getByText('01-Sept-2023')).toBeInTheDocument();
|
|
157
152
|
expect(screen.getByText('USD 80.00')).toBeInTheDocument();
|
|
158
153
|
expect(screen.getByText('USD 100.00')).toBeInTheDocument();
|
|
159
154
|
});
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
3
|
import { render, screen } from '@testing-library/react';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
useVisit,
|
|
6
|
+
useConfig,
|
|
7
|
+
navigate,
|
|
8
|
+
getDefaultsFromConfigSchema,
|
|
9
|
+
type VisitReturnType,
|
|
10
|
+
} from '@openmrs/esm-framework';
|
|
5
11
|
import { useBillableServices } from '../../billable-services/billable-service.resource';
|
|
6
12
|
import { type MappedBill, type LineItem } from '../../types';
|
|
13
|
+
import { configSchema, type BillingConfig } from '../../config-schema';
|
|
7
14
|
import Payments from './payments.component';
|
|
8
15
|
|
|
9
|
-
|
|
16
|
+
const mockUseVisit = jest.mocked(useVisit);
|
|
17
|
+
const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
|
|
18
|
+
const mockUseBillableServices = jest.mocked(useBillableServices);
|
|
10
19
|
const mockFormatToParts = jest.fn().mockReturnValue([{ type: 'integer', value: '1000' }]);
|
|
11
20
|
const mockFormat = jest.fn().mockReturnValue('$1000.00');
|
|
12
21
|
global.Intl.NumberFormat = jest.fn().mockImplementation(() => ({
|
|
@@ -87,10 +96,15 @@ describe('Payments', () => {
|
|
|
87
96
|
const mockSelectedLineItems: LineItem[] = [];
|
|
88
97
|
|
|
89
98
|
beforeEach(() => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
mockUseVisit.mockReturnValue({ currentVisit: null } as unknown as VisitReturnType);
|
|
100
|
+
mockUseConfig.mockReturnValue({ ...getDefaultsFromConfigSchema(configSchema), defaultCurrency: 'USD' });
|
|
101
|
+
mockUseBillableServices.mockReturnValue({
|
|
102
|
+
billableServices: [],
|
|
103
|
+
isLoading: false,
|
|
104
|
+
isValidating: false,
|
|
105
|
+
error: null,
|
|
106
|
+
mutate: jest.fn(),
|
|
107
|
+
});
|
|
94
108
|
});
|
|
95
109
|
|
|
96
110
|
it('renders payment form and history', () => {
|
|
@@ -2,8 +2,8 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import { Button } from '@carbon/react';
|
|
3
3
|
import { Printer } from '@carbon/react/icons';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import styles from './print-receipt.scss';
|
|
6
5
|
import { apiBasePath } from '../../constants';
|
|
6
|
+
import styles from './print-receipt.scss';
|
|
7
7
|
|
|
8
8
|
interface PrintReceiptProps {
|
|
9
9
|
billId: number;
|
|
@@ -1,50 +1,39 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
4
|
import PrintReceipt from './print-receipt.component';
|
|
5
5
|
|
|
6
|
-
jest.mock('react-i18next', () => ({
|
|
7
|
-
useTranslation: jest.fn(),
|
|
8
|
-
}));
|
|
9
|
-
|
|
10
|
-
jest.mock('@carbon/react/icons', () => ({
|
|
11
|
-
Printer: jest.fn(() => <div data-testid="printer-icon" />),
|
|
12
|
-
}));
|
|
13
|
-
|
|
14
6
|
describe('PrintReceipt', () => {
|
|
15
|
-
const mockT = jest.fn((key) => key);
|
|
16
|
-
|
|
17
7
|
beforeEach(() => {
|
|
18
|
-
(useTranslation as jest.Mock).mockReturnValue({ t: mockT });
|
|
19
|
-
jest.useFakeTimers();
|
|
20
8
|
window.URL.createObjectURL = jest.fn();
|
|
21
9
|
});
|
|
22
10
|
|
|
23
|
-
afterEach(() => {
|
|
24
|
-
jest.useRealTimers();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
11
|
it('renders button with correct text and icon', () => {
|
|
28
12
|
render(<PrintReceipt billId={123} />);
|
|
29
|
-
expect(screen.getByText('
|
|
30
|
-
expect(screen.getByTestId('printer-icon')).toBeInTheDocument();
|
|
13
|
+
expect(screen.getByText('Print receipt')).toBeInTheDocument();
|
|
31
14
|
});
|
|
32
15
|
|
|
33
|
-
it('displays "Loading" and disables button when isRedirecting is true', () => {
|
|
16
|
+
it('displays "Loading" and disables button when isRedirecting is true', async () => {
|
|
17
|
+
const user = userEvent.setup();
|
|
18
|
+
|
|
34
19
|
render(<PrintReceipt billId={123} />);
|
|
20
|
+
|
|
35
21
|
const button = screen.getByRole('button');
|
|
36
|
-
|
|
37
|
-
|
|
22
|
+
|
|
23
|
+
await user.click(button);
|
|
24
|
+
expect(screen.getByText(/loading/i)).toBeInTheDocument();
|
|
38
25
|
expect(button).toBeDisabled();
|
|
39
26
|
});
|
|
40
27
|
|
|
41
|
-
it('applies correct CSS class to button', () => {
|
|
28
|
+
it('applies correct CSS class to button', async () => {
|
|
29
|
+
const user = userEvent.setup();
|
|
30
|
+
|
|
42
31
|
render(<PrintReceipt billId={123} />);
|
|
43
32
|
expect(screen.getByRole('button')).toHaveClass('button');
|
|
44
33
|
});
|
|
45
34
|
|
|
46
35
|
it('translates button text correctly', () => {
|
|
47
36
|
render(<PrintReceipt billId={123} />);
|
|
48
|
-
expect(
|
|
37
|
+
expect(screen.getByText('Print receipt')).toBeInTheDocument();
|
|
49
38
|
});
|
|
50
39
|
});
|
|
@@ -3,9 +3,9 @@ import { useDefaultFacility } from '../../billing.resource';
|
|
|
3
3
|
import styles from './printable-footer.scss';
|
|
4
4
|
|
|
5
5
|
const PrintableFooter = () => {
|
|
6
|
-
const { data
|
|
6
|
+
const { data } = useDefaultFacility();
|
|
7
7
|
|
|
8
|
-
if (
|
|
8
|
+
if (!data) {
|
|
9
9
|
return <div>--</div>;
|
|
10
10
|
}
|
|
11
11
|
|