@openmrs/esm-billing-app 1.0.2-pre.90 → 1.0.2-pre.901
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/.eslintrc +16 -2
- package/README.md +54 -9
- package/__mocks__/bills.mock.ts +12 -0
- package/__mocks__/react-i18next.js +6 -5
- package/dist/1119.js +1 -1
- package/dist/1146.js +1 -2
- package/dist/1146.js.map +1 -1
- package/dist/1197.js +1 -1
- package/dist/1537.js +1 -0
- package/dist/1537.js.map +1 -0
- package/dist/1856.js +1 -0
- package/dist/1856.js.map +1 -0
- package/dist/2146.js +1 -1
- package/dist/2524.js +1 -0
- package/dist/2524.js.map +1 -0
- package/dist/2690.js +1 -1
- package/dist/3099.js +1 -1
- package/dist/3584.js +1 -1
- package/dist/3717.js +2 -0
- package/dist/3717.js.map +1 -0
- package/dist/4055.js +1 -1
- package/dist/4132.js +1 -1
- package/dist/4300.js +1 -1
- package/dist/4335.js +1 -1
- package/dist/4618.js +1 -1
- package/dist/4652.js +1 -1
- package/dist/4692.js +1 -0
- package/dist/4692.js.map +1 -0
- package/dist/4724.js +1 -0
- package/dist/4724.js.map +1 -0
- package/dist/4739.js +1 -1
- package/dist/4739.js.map +1 -1
- package/dist/4944.js +1 -1
- package/dist/5173.js +1 -1
- package/dist/5241.js +1 -1
- package/dist/5442.js +1 -1
- package/dist/5661.js +1 -1
- package/dist/6022.js +1 -1
- package/dist/6468.js +1 -1
- package/dist/6540.js +1 -1
- package/dist/6540.js.map +1 -1
- package/dist/6679.js +1 -1
- package/dist/6840.js +1 -1
- package/dist/6859.js +1 -1
- package/dist/7097.js +1 -1
- package/dist/7159.js +1 -1
- package/dist/723.js +1 -1
- package/dist/7255.js +1 -1
- package/dist/7255.js.map +1 -1
- package/dist/7617.js +1 -1
- package/dist/795.js +1 -1
- package/dist/8163.js +1 -1
- package/dist/8349.js +1 -1
- package/dist/8618.js +1 -1
- package/dist/8708.js +2 -0
- package/dist/{6557.js.LICENSE.txt → 8708.js.LICENSE.txt} +22 -0
- package/dist/8708.js.map +1 -0
- package/dist/890.js +1 -1
- package/dist/9214.js +1 -1
- package/dist/9538.js +1 -1
- package/dist/9569.js +1 -1
- package/dist/961.js +1 -1
- package/dist/961.js.map +1 -1
- package/dist/986.js +1 -1
- package/dist/9879.js +1 -1
- package/dist/9895.js +1 -1
- package/dist/9900.js +1 -1
- package/dist/9913.js +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 +282 -296
- package/dist/openmrs-esm-billing-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/e2e/README.md +19 -18
- package/e2e/core/test.ts +1 -1
- package/e2e/fixtures/api.ts +1 -1
- package/e2e/specs/sample-test.spec.ts +0 -1
- package/e2e/support/github/Dockerfile +1 -1
- package/package.json +18 -15
- package/src/bill-history/bill-history.component.tsx +20 -28
- package/src/bill-history/bill-history.scss +4 -94
- package/src/bill-history/bill-history.test.tsx +37 -78
- package/src/bill-item-actions/bill-item-actions.scss +21 -5
- package/src/bill-item-actions/edit-bill-item.modal.tsx +225 -0
- package/src/bill-item-actions/edit-bill-item.test.tsx +214 -40
- package/src/billable-services/bill-waiver/bill-selection.component.tsx +5 -5
- package/src/billable-services/bill-waiver/bill-waiver-form.component.tsx +28 -32
- package/src/billable-services/bill-waiver/patient-bills.component.tsx +7 -7
- package/src/billable-services/bill-waiver/utils.ts +13 -3
- package/src/billable-services/{create-edit/add-billable-service.scss → billable-service-form/billable-service-form.scss} +32 -64
- package/src/billable-services/billable-service-form/billable-service-form.test.tsx +893 -0
- package/src/billable-services/billable-service-form/billable-service-form.workspace.tsx +504 -0
- package/src/billable-services/billable-service.resource.ts +42 -26
- package/src/billable-services/billable-services-home.component.tsx +13 -42
- package/src/billable-services/billable-services-left-panel-link.component.tsx +48 -0
- package/src/billable-services/billable-services-left-panel-menu.component.tsx +46 -0
- package/src/billable-services/billable-services-menu-item/item.component.tsx +5 -4
- package/src/billable-services/billable-services.component.tsx +156 -152
- package/src/billable-services/billable-services.scss +29 -0
- package/src/billable-services/billable-services.test.tsx +6 -49
- package/src/billable-services/cash-point/add-cash-point.modal.tsx +170 -0
- package/src/billable-services/cash-point/cash-point-configuration.component.tsx +19 -193
- package/src/billable-services/cash-point/cash-point-configuration.scss +1 -5
- package/src/billable-services/dashboard/dashboard.component.tsx +0 -2
- package/src/billable-services/payment-modes/add-payment-mode.modal.tsx +121 -0
- package/src/billable-services/payment-modes/delete-payment-mode.modal.tsx +74 -0
- package/src/billable-services/payment-modes/payment-modes-config.component.tsx +125 -0
- package/src/billable-services/{payyment-modes → payment-modes}/payment-modes-config.scss +5 -4
- package/src/billable-services-admin-card-link.component.test.tsx +2 -2
- package/src/billable-services-admin-card-link.component.tsx +1 -1
- package/src/billing-dashboard/billing-dashboard.scss +1 -1
- package/src/billing-form/billing-checkin-form.component.tsx +21 -17
- package/src/billing-form/billing-checkin-form.test.tsx +99 -26
- package/src/billing-form/billing-form.component.tsx +222 -292
- package/src/billing-form/billing-form.scss +143 -0
- package/src/billing-form/visit-attributes/visit-attributes-form.component.tsx +1 -1
- package/src/billing.resource.ts +69 -74
- package/src/bills-table/bills-table.component.tsx +3 -3
- package/src/bills-table/bills-table.test.tsx +98 -54
- package/src/config-schema.ts +52 -24
- package/src/dashboard.meta.ts +4 -2
- package/src/helpers/functions.ts +5 -4
- package/src/index.ts +71 -9
- package/src/invoice/invoice-table.component.tsx +36 -70
- package/src/invoice/invoice-table.scss +8 -5
- package/src/invoice/invoice-table.test.tsx +273 -62
- package/src/invoice/invoice.component.tsx +39 -32
- package/src/invoice/invoice.scss +11 -4
- package/src/invoice/invoice.test.tsx +324 -120
- package/src/invoice/payments/invoice-breakdown/invoice-breakdown.scss +9 -9
- package/src/invoice/payments/payment-form/payment-form.component.tsx +43 -34
- package/src/invoice/payments/payment-form/payment-form.scss +5 -6
- package/src/invoice/payments/payment-form/payment-form.test.tsx +216 -66
- 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.tsx +55 -67
- package/src/invoice/payments/payments.scss +4 -3
- package/src/invoice/payments/payments.test.tsx +282 -0
- package/src/invoice/payments/utils.ts +15 -27
- package/src/invoice/printable-invoice/print-receipt.component.tsx +3 -2
- 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 +12 -11
- package/src/invoice/printable-invoice/printable-invoice-header.test.tsx +16 -14
- package/src/invoice/printable-invoice/printable-invoice.component.tsx +20 -34
- package/src/left-panel-link.test.tsx +1 -4
- package/src/metrics-cards/metrics-cards.component.tsx +16 -6
- package/src/metrics-cards/metrics-cards.scss +4 -0
- package/src/metrics-cards/metrics-cards.test.tsx +18 -5
- package/src/modal/require-payment-modal.test.tsx +27 -22
- package/src/modal/{require-payment-modal.component.tsx → require-payment.modal.tsx} +18 -19
- package/src/routes.json +44 -20
- package/src/types/index.ts +81 -23
- package/translations/am.json +132 -75
- package/translations/ar.json +133 -76
- package/translations/ar_SY.json +133 -76
- package/translations/bn.json +135 -78
- package/translations/de.json +133 -76
- package/translations/en.json +133 -78
- package/translations/en_US.json +133 -76
- package/translations/es.json +132 -75
- package/translations/es_MX.json +133 -76
- package/translations/fr.json +138 -81
- package/translations/he.json +132 -75
- package/translations/hi.json +133 -76
- package/translations/hi_IN.json +133 -76
- package/translations/id.json +133 -76
- package/translations/it.json +159 -102
- package/translations/ka.json +133 -76
- package/translations/km.json +132 -75
- package/translations/ku.json +133 -76
- package/translations/ky.json +133 -76
- package/translations/lg.json +133 -76
- package/translations/ne.json +133 -76
- package/translations/pl.json +133 -76
- package/translations/pt.json +133 -76
- package/translations/pt_BR.json +133 -76
- package/translations/qu.json +133 -76
- package/translations/ro_RO.json +222 -165
- package/translations/ru_RU.json +133 -76
- package/translations/si.json +133 -76
- package/translations/sw.json +133 -76
- package/translations/sw_KE.json +133 -76
- package/translations/tr.json +133 -76
- package/translations/tr_TR.json +133 -76
- package/translations/uk.json +133 -76
- package/translations/uz.json +133 -76
- package/translations/uz@Latn.json +133 -76
- package/translations/uz_UZ.json +133 -76
- package/translations/vi.json +133 -76
- package/translations/zh.json +133 -76
- package/translations/zh_CN.json +164 -107
- package/dist/1146.js.LICENSE.txt +0 -21
- package/dist/2352.js +0 -1
- package/dist/2352.js.map +0 -1
- package/dist/246.js +0 -1
- package/dist/246.js.map +0 -1
- package/dist/4689.js +0 -2
- package/dist/4689.js.map +0 -1
- package/dist/6557.js +0 -2
- package/dist/6557.js.map +0 -1
- package/dist/8638.js +0 -1
- package/dist/8638.js.map +0 -1
- package/dist/9968.js +0 -1
- package/dist/9968.js.map +0 -1
- package/src/bill-item-actions/edit-bill-item.component.tsx +0 -221
- package/src/billable-services/create-edit/add-billable-service.component.tsx +0 -401
- package/src/billable-services/create-edit/add-billable-service.test.tsx +0 -154
- package/src/billable-services/dashboard/service-metrics.component.tsx +0 -41
- package/src/billable-services/payyment-modes/payment-modes-config.component.tsx +0 -280
- package/src/invoice/payments/payments.component.test.tsx +0 -121
- /package/dist/{4689.js.LICENSE.txt → 3717.js.LICENSE.txt} +0 -0
|
@@ -1,71 +1,51 @@
|
|
|
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 { render, screen, waitFor } from '@testing-library/react';
|
|
4
4
|
import { useReactToPrint } from 'react-to-print';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { getDefaultsFromConfigSchema, useConfig, usePatient } from '@openmrs/esm-framework';
|
|
6
|
+
import { configSchema, type BillingConfig } from '../config-schema';
|
|
7
|
+
import { mockBill, mockPatient } from '../../__mocks__/bills.mock';
|
|
8
|
+
import { useBill } from '../billing.resource';
|
|
7
9
|
import { usePaymentModes } from './payments/payment.resource';
|
|
8
10
|
import Invoice from './invoice.component';
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
|
|
13
|
+
const mockUseBill = jest.mocked(useBill);
|
|
14
|
+
const mockUsePatient = jest.mocked(usePatient);
|
|
15
|
+
const mockUsePaymentModes = jest.mocked(usePaymentModes);
|
|
16
|
+
const mockUseReactToPrint = jest.mocked(useReactToPrint);
|
|
17
|
+
|
|
11
18
|
jest.mock('../helpers/functions', () => ({
|
|
12
19
|
convertToCurrency: jest.fn((amount) => `USD ${amount}`),
|
|
13
20
|
}));
|
|
14
21
|
|
|
15
|
-
// Mock i18next
|
|
16
|
-
jest.mock('react-i18next', () => ({
|
|
17
|
-
useTranslation: () => ({
|
|
18
|
-
t: (key: string) => key,
|
|
19
|
-
}),
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
// Set window.i18next
|
|
23
22
|
window.i18next = {
|
|
24
23
|
language: 'en',
|
|
25
24
|
} as any;
|
|
26
25
|
|
|
27
|
-
// Mock InvoiceTable component
|
|
28
|
-
jest.mock('./invoice-table.component', () =>
|
|
29
|
-
jest.fn(({ bill }) => <div data-testid="mock-invoice-table">Invoice Table Mock</div>),
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
// Mock payments component
|
|
33
|
-
jest.mock('./payments/payments.component', () =>
|
|
34
|
-
jest.fn(({ bill, mutate, selectedLineItems }) => (
|
|
35
|
-
<div data-testid="mock-payments">
|
|
36
|
-
<h2>Payments</h2>
|
|
37
|
-
<button>Add payment option</button>
|
|
38
|
-
</div>
|
|
39
|
-
)),
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
// Mock PrintReceipt component
|
|
43
26
|
jest.mock('./printable-invoice/print-receipt.component', () =>
|
|
44
|
-
jest.fn((
|
|
27
|
+
jest.fn(() => <div data-testid="mock-print-receipt">Print Receipt Mock</div>),
|
|
45
28
|
);
|
|
46
29
|
|
|
47
|
-
// Mock PrintableInvoice component
|
|
48
30
|
jest.mock('./printable-invoice/printable-invoice.component', () =>
|
|
49
|
-
jest.fn((
|
|
31
|
+
jest.fn(() => <div data-testid="mock-printable-invoice">Printable Invoice Mock</div>),
|
|
50
32
|
);
|
|
51
33
|
|
|
52
|
-
// Mock payment resource
|
|
53
34
|
jest.mock('./payments/payment.resource', () => ({
|
|
54
35
|
usePaymentModes: jest.fn(),
|
|
55
36
|
updateBillVisitAttribute: jest.fn(),
|
|
56
37
|
}));
|
|
57
38
|
|
|
58
|
-
// Mock billing resource
|
|
59
39
|
jest.mock('../billing.resource', () => ({
|
|
60
40
|
useBill: jest.fn(),
|
|
61
|
-
processBillPayment: jest.fn(),
|
|
62
41
|
useDefaultFacility: jest.fn().mockReturnValue({
|
|
63
|
-
|
|
64
|
-
|
|
42
|
+
data: {
|
|
43
|
+
uuid: '54065383-b4d4-42d2-af4d-d250a1fd2590',
|
|
44
|
+
display: 'MTRH',
|
|
45
|
+
},
|
|
65
46
|
}),
|
|
66
47
|
}));
|
|
67
48
|
|
|
68
|
-
// Mock react-router-dom
|
|
69
49
|
jest.mock('react-router-dom', () => ({
|
|
70
50
|
useParams: jest.fn().mockReturnValue({
|
|
71
51
|
patientUuid: 'patientUuid',
|
|
@@ -73,49 +53,11 @@ jest.mock('react-router-dom', () => ({
|
|
|
73
53
|
}),
|
|
74
54
|
}));
|
|
75
55
|
|
|
76
|
-
// Mock react-to-print
|
|
77
56
|
jest.mock('react-to-print', () => ({
|
|
78
57
|
useReactToPrint: jest.fn(),
|
|
79
58
|
}));
|
|
80
59
|
|
|
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
60
|
describe('Invoice', () => {
|
|
114
|
-
const mockedBill = useBill as jest.Mock;
|
|
115
|
-
const mockedProcessBillPayment = processBillPayment as jest.Mock;
|
|
116
|
-
const mockedUsePaymentModes = usePaymentModes as jest.Mock;
|
|
117
|
-
const mockedUseReactToPrint = useReactToPrint as jest.Mock;
|
|
118
|
-
|
|
119
61
|
const defaultBillData = {
|
|
120
62
|
...mockBill,
|
|
121
63
|
uuid: 'test-uuid',
|
|
@@ -131,12 +73,20 @@ describe('Invoice', () => {
|
|
|
131
73
|
quantity: 1,
|
|
132
74
|
price: 1000,
|
|
133
75
|
paymentStatus: 'PENDING',
|
|
76
|
+
billableService: 'Test Service',
|
|
77
|
+
display: '',
|
|
78
|
+
voided: false,
|
|
79
|
+
voidReason: '',
|
|
80
|
+
priceName: '',
|
|
81
|
+
priceUuid: '',
|
|
82
|
+
lineItemOrder: 0,
|
|
83
|
+
resourceVersion: '',
|
|
134
84
|
},
|
|
135
85
|
],
|
|
136
86
|
};
|
|
137
87
|
|
|
138
88
|
beforeEach(() => {
|
|
139
|
-
|
|
89
|
+
mockUseBill.mockReturnValue({
|
|
140
90
|
bill: defaultBillData,
|
|
141
91
|
isLoading: false,
|
|
142
92
|
error: null,
|
|
@@ -144,7 +94,14 @@ describe('Invoice', () => {
|
|
|
144
94
|
mutate: jest.fn(),
|
|
145
95
|
});
|
|
146
96
|
|
|
147
|
-
|
|
97
|
+
mockUsePatient.mockReturnValue({
|
|
98
|
+
patient: mockPatient as any,
|
|
99
|
+
isLoading: false,
|
|
100
|
+
error: null,
|
|
101
|
+
patientUuid: 'patientUuid',
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
mockUsePaymentModes.mockReturnValue({
|
|
148
105
|
paymentModes: [
|
|
149
106
|
{ uuid: 'cash-uuid', name: 'Cash', description: 'Cash Method', retired: false },
|
|
150
107
|
{ uuid: 'mpesa-uuid', name: 'MPESA', description: 'MPESA Method', retired: false },
|
|
@@ -154,46 +111,77 @@ describe('Invoice', () => {
|
|
|
154
111
|
mutate: jest.fn(),
|
|
155
112
|
});
|
|
156
113
|
|
|
157
|
-
|
|
114
|
+
mockUseConfig.mockReturnValue({ ...getDefaultsFromConfigSchema(configSchema), defaultCurrency: 'USD' });
|
|
115
|
+
|
|
158
116
|
const printHandler = jest.fn();
|
|
159
|
-
|
|
117
|
+
mockUseReactToPrint.mockReturnValue(printHandler);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should render loading state when bill is loading', () => {
|
|
121
|
+
mockUseBill.mockReturnValue({
|
|
122
|
+
bill: null,
|
|
123
|
+
isLoading: true,
|
|
124
|
+
error: null,
|
|
125
|
+
isValidating: false,
|
|
126
|
+
mutate: jest.fn(),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
render(<Invoice />);
|
|
130
|
+
expect(screen.getByText(/loading bill information/i)).toBeInTheDocument();
|
|
160
131
|
});
|
|
161
132
|
|
|
162
|
-
|
|
163
|
-
|
|
133
|
+
it('should render loading state when patient is loading', () => {
|
|
134
|
+
mockUsePatient.mockReturnValue({
|
|
135
|
+
patient: null as any,
|
|
136
|
+
isLoading: true,
|
|
137
|
+
error: null,
|
|
138
|
+
patientUuid: 'patientUuid',
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
render(<Invoice />);
|
|
142
|
+
expect(screen.getByText(/loading bill information/i)).toBeInTheDocument();
|
|
164
143
|
});
|
|
165
144
|
|
|
166
|
-
it('should render error state
|
|
167
|
-
|
|
145
|
+
it('should render error state when bill fails to load', () => {
|
|
146
|
+
mockUseBill.mockReturnValue({
|
|
168
147
|
bill: null,
|
|
169
148
|
isLoading: false,
|
|
170
|
-
error: new Error('
|
|
149
|
+
error: new Error('Failed to load bill'),
|
|
171
150
|
isValidating: false,
|
|
172
151
|
mutate: jest.fn(),
|
|
173
152
|
});
|
|
174
153
|
|
|
175
154
|
render(<Invoice />);
|
|
176
|
-
expect(screen.
|
|
177
|
-
expect(screen.getByText(/Test error/i)).toBeInTheDocument();
|
|
155
|
+
expect(screen.getByText(/invoice error/i)).toBeInTheDocument();
|
|
178
156
|
});
|
|
179
157
|
|
|
180
158
|
it('should render invoice details correctly', () => {
|
|
181
159
|
render(<Invoice />);
|
|
182
160
|
|
|
183
|
-
|
|
184
|
-
expect(screen.
|
|
185
|
-
expect(screen.getByText(/
|
|
186
|
-
expect(screen.getByText(/
|
|
187
|
-
expect(screen.getByText(/
|
|
188
|
-
expect(screen.
|
|
161
|
+
expect(screen.getAllByText(/total amount/i).length).toBeGreaterThan(0);
|
|
162
|
+
expect(screen.getAllByText(/amount tendered/i).length).toBeGreaterThan(0);
|
|
163
|
+
expect(screen.getByText(/invoice number/i)).toBeInTheDocument();
|
|
164
|
+
expect(screen.getByText(/date and time/i)).toBeInTheDocument();
|
|
165
|
+
expect(screen.getByText(/invoice status/i)).toBeInTheDocument();
|
|
166
|
+
expect(screen.getAllByText('RCPT-001').length).toBeGreaterThan(0);
|
|
167
|
+
expect(screen.getAllByText('PENDING').length).toBeGreaterThan(0);
|
|
168
|
+
});
|
|
189
169
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
170
|
+
it('should render invoice table with line items', () => {
|
|
171
|
+
render(<Invoice />);
|
|
172
|
+
|
|
173
|
+
expect(screen.getByText(/line items/i)).toBeInTheDocument();
|
|
174
|
+
expect(screen.getByText('Test Service')).toBeInTheDocument();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should render payments section', () => {
|
|
178
|
+
render(<Invoice />);
|
|
179
|
+
|
|
180
|
+
expect(screen.getByText(/payments/i)).toBeInTheDocument();
|
|
193
181
|
});
|
|
194
182
|
|
|
195
183
|
it('should show print receipt button for paid bills', () => {
|
|
196
|
-
|
|
184
|
+
mockUseBill.mockReturnValue({
|
|
197
185
|
bill: {
|
|
198
186
|
...defaultBillData,
|
|
199
187
|
status: 'PAID',
|
|
@@ -209,64 +197,280 @@ describe('Invoice', () => {
|
|
|
209
197
|
expect(screen.getByTestId('mock-print-receipt')).toBeInTheDocument();
|
|
210
198
|
});
|
|
211
199
|
|
|
212
|
-
it('should
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
200
|
+
it('should show print receipt button for bills with tendered amount', () => {
|
|
201
|
+
mockUseBill.mockReturnValue({
|
|
202
|
+
bill: {
|
|
203
|
+
...defaultBillData,
|
|
204
|
+
status: 'PENDING',
|
|
205
|
+
tenderedAmount: 500,
|
|
206
|
+
},
|
|
218
207
|
isLoading: false,
|
|
219
208
|
error: null,
|
|
220
209
|
isValidating: false,
|
|
221
|
-
mutate:
|
|
210
|
+
mutate: jest.fn(),
|
|
222
211
|
});
|
|
223
212
|
|
|
224
|
-
|
|
213
|
+
render(<Invoice />);
|
|
214
|
+
expect(screen.getByTestId('mock-print-receipt')).toBeInTheDocument();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should not show print receipt button for unpaid bills', () => {
|
|
218
|
+
render(<Invoice />);
|
|
219
|
+
expect(screen.queryByTestId('mock-print-receipt')).not.toBeInTheDocument();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should handle print button click', async () => {
|
|
223
|
+
const handlePrintMock = jest.fn();
|
|
224
|
+
const user = userEvent.setup();
|
|
225
|
+
mockUseReactToPrint.mockReturnValue(handlePrintMock);
|
|
225
226
|
|
|
226
227
|
render(<Invoice />);
|
|
227
228
|
|
|
228
|
-
|
|
229
|
-
|
|
229
|
+
const printButton = screen.getByRole('button', { name: /print bill/i });
|
|
230
|
+
await user.click(printButton);
|
|
230
231
|
|
|
231
|
-
|
|
232
|
+
await waitFor(() => {
|
|
233
|
+
expect(handlePrintMock).toHaveBeenCalled();
|
|
234
|
+
});
|
|
232
235
|
});
|
|
233
236
|
|
|
234
|
-
it('should
|
|
235
|
-
|
|
237
|
+
it('should disable print button while printing', () => {
|
|
238
|
+
render(<Invoice />);
|
|
236
239
|
|
|
237
|
-
|
|
238
|
-
|
|
240
|
+
const printButton = screen.getByRole('button', { name: /print bill/i });
|
|
241
|
+
expect(printButton).toBeEnabled();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('should render patient header when patient data is available', () => {
|
|
245
|
+
render(<Invoice />);
|
|
246
|
+
|
|
247
|
+
// Patient header is rendered via ExtensionSlot
|
|
248
|
+
expect(screen.getByText(/line items/i)).toBeInTheDocument();
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should search and filter line items in the table', async () => {
|
|
252
|
+
const billWithMultipleItems = {
|
|
239
253
|
...defaultBillData,
|
|
240
254
|
lineItems: [
|
|
241
|
-
|
|
255
|
+
{
|
|
256
|
+
uuid: 'item-1',
|
|
257
|
+
item: 'Lab Test',
|
|
258
|
+
quantity: 1,
|
|
259
|
+
price: 500,
|
|
260
|
+
paymentStatus: 'PENDING',
|
|
261
|
+
billableService: 'Lab Test',
|
|
262
|
+
display: '',
|
|
263
|
+
voided: false,
|
|
264
|
+
voidReason: '',
|
|
265
|
+
priceName: '',
|
|
266
|
+
priceUuid: '',
|
|
267
|
+
lineItemOrder: 0,
|
|
268
|
+
resourceVersion: '',
|
|
269
|
+
},
|
|
242
270
|
{
|
|
243
271
|
uuid: 'item-2',
|
|
244
|
-
item: '
|
|
272
|
+
item: 'X-Ray',
|
|
245
273
|
quantity: 1,
|
|
246
274
|
price: 500,
|
|
247
275
|
paymentStatus: 'PENDING',
|
|
276
|
+
billableService: 'X-Ray',
|
|
277
|
+
display: '',
|
|
278
|
+
voided: false,
|
|
279
|
+
voidReason: '',
|
|
280
|
+
priceName: '',
|
|
281
|
+
priceUuid: '',
|
|
282
|
+
lineItemOrder: 1,
|
|
283
|
+
resourceVersion: '',
|
|
248
284
|
},
|
|
249
285
|
],
|
|
250
286
|
};
|
|
251
287
|
|
|
252
|
-
|
|
253
|
-
bill:
|
|
288
|
+
mockUseBill.mockReturnValue({
|
|
289
|
+
bill: billWithMultipleItems,
|
|
254
290
|
isLoading: false,
|
|
255
291
|
error: null,
|
|
256
292
|
isValidating: false,
|
|
257
293
|
mutate: jest.fn(),
|
|
258
294
|
});
|
|
259
295
|
|
|
296
|
+
const user = userEvent.setup();
|
|
297
|
+
render(<Invoice />);
|
|
298
|
+
|
|
299
|
+
expect(screen.getByText('Lab Test')).toBeInTheDocument();
|
|
300
|
+
expect(screen.getByText('X-Ray')).toBeInTheDocument();
|
|
301
|
+
|
|
302
|
+
const searchInput = screen.getByPlaceholderText(/search this table/i);
|
|
303
|
+
await user.type(searchInput, 'Lab Test');
|
|
304
|
+
|
|
305
|
+
await waitFor(() => {
|
|
306
|
+
expect(screen.getByText('Lab Test')).toBeInTheDocument();
|
|
307
|
+
expect(screen.queryByText('X-Ray')).not.toBeInTheDocument();
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should handle bill data updates via mutate', () => {
|
|
312
|
+
const mockMutate = jest.fn();
|
|
313
|
+
mockUseBill.mockReturnValue({
|
|
314
|
+
bill: defaultBillData,
|
|
315
|
+
isLoading: false,
|
|
316
|
+
error: null,
|
|
317
|
+
isValidating: false,
|
|
318
|
+
mutate: mockMutate,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
const { rerender } = render(<Invoice />);
|
|
322
|
+
|
|
323
|
+
const updatedBill = {
|
|
324
|
+
...defaultBillData,
|
|
325
|
+
status: 'PAID',
|
|
326
|
+
tenderedAmount: 1000,
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
mockUseBill.mockReturnValue({
|
|
330
|
+
bill: updatedBill,
|
|
331
|
+
isLoading: false,
|
|
332
|
+
error: null,
|
|
333
|
+
isValidating: false,
|
|
334
|
+
mutate: mockMutate,
|
|
335
|
+
});
|
|
336
|
+
|
|
260
337
|
rerender(<Invoice />);
|
|
261
338
|
|
|
262
|
-
|
|
263
|
-
|
|
339
|
+
expect(screen.getByText('PAID')).toBeInTheDocument();
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('should display correct currency formatting', () => {
|
|
343
|
+
render(<Invoice />);
|
|
344
|
+
|
|
345
|
+
// convertToCurrency is mocked to return "USD ${amount}"
|
|
346
|
+
expect(screen.getAllByText('USD 1000').length).toBeGreaterThan(0);
|
|
347
|
+
expect(screen.getAllByText('USD 0').length).toBeGreaterThan(0);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('should disable print button when isPrinting state is true', () => {
|
|
351
|
+
// Mock isPrinting state by checking the button's disabled state when loading
|
|
352
|
+
mockUseBill.mockReturnValue({
|
|
353
|
+
bill: defaultBillData,
|
|
354
|
+
isLoading: true,
|
|
355
|
+
error: null,
|
|
356
|
+
isValidating: false,
|
|
357
|
+
mutate: jest.fn(),
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
render(<Invoice />);
|
|
361
|
+
// When bill is loading, component shows loading state, not the button
|
|
362
|
+
expect(screen.getByText(/loading bill information/i)).toBeInTheDocument();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('should not render PrintableInvoice when bill is missing', () => {
|
|
366
|
+
mockUseBill.mockReturnValue({
|
|
367
|
+
bill: null,
|
|
368
|
+
isLoading: false,
|
|
369
|
+
error: null,
|
|
370
|
+
isValidating: false,
|
|
371
|
+
mutate: jest.fn(),
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
mockUsePatient.mockReturnValue({
|
|
375
|
+
patient: mockPatient as any,
|
|
376
|
+
isLoading: false,
|
|
377
|
+
error: null,
|
|
378
|
+
patientUuid: 'patientUuid',
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
render(<Invoice />);
|
|
382
|
+
|
|
383
|
+
// PrintableInvoice should not be rendered when bill is null
|
|
384
|
+
// Since it's in a hidden div, we can't easily assert its absence
|
|
385
|
+
// but we can verify the main content doesn't have the print container
|
|
386
|
+
expect(screen.queryByTestId('mock-printable-invoice')).not.toBeInTheDocument();
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('should not render PrintableInvoice when patient is missing', () => {
|
|
390
|
+
mockUsePatient.mockReturnValue({
|
|
391
|
+
patient: null as any,
|
|
392
|
+
isLoading: false,
|
|
393
|
+
error: null,
|
|
394
|
+
patientUuid: 'patientUuid',
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
render(<Invoice />);
|
|
398
|
+
|
|
399
|
+
// PrintableInvoice requires both bill and patient
|
|
400
|
+
expect(screen.queryByTestId('mock-printable-invoice')).not.toBeInTheDocument();
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('should render PrintableInvoice when both bill and patient exist', () => {
|
|
404
|
+
render(<Invoice />);
|
|
405
|
+
|
|
406
|
+
// PrintableInvoice should be rendered with both bill and patient
|
|
407
|
+
expect(screen.getByTestId('mock-printable-invoice')).toBeInTheDocument();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('should pass correct props to InvoiceTable', () => {
|
|
411
|
+
render(<Invoice />);
|
|
412
|
+
|
|
413
|
+
// Verify InvoiceTable is rendered with line items
|
|
414
|
+
expect(screen.getByText('Test Service')).toBeInTheDocument();
|
|
415
|
+
expect(screen.getByText(/line items/i)).toBeInTheDocument();
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('should pass mutate function to Payments component', () => {
|
|
419
|
+
const mockMutate = jest.fn();
|
|
420
|
+
mockUseBill.mockReturnValue({
|
|
421
|
+
bill: defaultBillData,
|
|
422
|
+
isLoading: false,
|
|
423
|
+
error: null,
|
|
424
|
+
isValidating: false,
|
|
425
|
+
mutate: mockMutate,
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
render(<Invoice />);
|
|
429
|
+
|
|
430
|
+
// Payments component should be rendered
|
|
431
|
+
expect(screen.getByText(/payments/i)).toBeInTheDocument();
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('should show print receipt for bills with partial payment', () => {
|
|
435
|
+
mockUseBill.mockReturnValue({
|
|
436
|
+
bill: {
|
|
437
|
+
...defaultBillData,
|
|
438
|
+
status: 'PENDING',
|
|
439
|
+
totalAmount: 1000,
|
|
440
|
+
tenderedAmount: 500, // Partial payment
|
|
441
|
+
},
|
|
442
|
+
isLoading: false,
|
|
443
|
+
error: null,
|
|
444
|
+
isValidating: false,
|
|
445
|
+
mutate: jest.fn(),
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
render(<Invoice />);
|
|
449
|
+
expect(screen.getByTestId('mock-print-receipt')).toBeInTheDocument();
|
|
264
450
|
});
|
|
265
451
|
|
|
266
|
-
it('should
|
|
452
|
+
it('should render ExtensionSlot when patient and patientUuid exist', () => {
|
|
267
453
|
render(<Invoice />);
|
|
268
|
-
|
|
454
|
+
|
|
455
|
+
// The component renders, which includes the ExtensionSlot
|
|
456
|
+
// We can verify this indirectly by checking the main content is present
|
|
457
|
+
expect(screen.getByText(/invoice number/i)).toBeInTheDocument();
|
|
269
458
|
});
|
|
270
459
|
|
|
271
|
-
|
|
460
|
+
it('should not show print receipt for bills with zero tendered amount', () => {
|
|
461
|
+
mockUseBill.mockReturnValue({
|
|
462
|
+
bill: {
|
|
463
|
+
...defaultBillData,
|
|
464
|
+
status: 'PENDING',
|
|
465
|
+
tenderedAmount: 0,
|
|
466
|
+
},
|
|
467
|
+
isLoading: false,
|
|
468
|
+
error: null,
|
|
469
|
+
isValidating: false,
|
|
470
|
+
mutate: jest.fn(),
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
render(<Invoice />);
|
|
474
|
+
expect(screen.queryByTestId('mock-print-receipt')).not.toBeInTheDocument();
|
|
475
|
+
});
|
|
272
476
|
});
|
|
@@ -5,25 +5,25 @@
|
|
|
5
5
|
.invoiceBreakdown {
|
|
6
6
|
display: grid;
|
|
7
7
|
grid-template-columns: 1fr 1fr;
|
|
8
|
-
align-items:
|
|
9
|
-
margin: layout.$spacing-
|
|
8
|
+
align-items: center;
|
|
9
|
+
margin: layout.$spacing-03 0;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
.label {
|
|
13
|
-
@include type.type-style('
|
|
13
|
+
@include type.type-style('body-compact-01');
|
|
14
14
|
color: colors.$gray-100;
|
|
15
|
-
text-align:
|
|
15
|
+
text-align: left;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
.value {
|
|
19
|
-
@
|
|
20
|
-
|
|
19
|
+
@include type.type-style('body-compact-01');
|
|
20
|
+
color: colors.$gray-100;
|
|
21
|
+
text-align: left;
|
|
21
22
|
margin-left: layout.$spacing-03;
|
|
22
|
-
text-align: start;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.extendedLabel {
|
|
26
|
-
@
|
|
27
|
-
font-weight: bold;
|
|
26
|
+
@include type.type-style('heading-compact-01');
|
|
28
27
|
color: crimson;
|
|
28
|
+
text-align: left;
|
|
29
29
|
}
|