@openmrs/esm-stock-management-app 1.0.1-pre.783 → 1.0.1-pre.788

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.
Files changed (128) hide show
  1. package/__mocks__/index.ts +1 -0
  2. package/__mocks__/operation-type.mock.ts +532 -0
  3. package/dist/155.js +1 -0
  4. package/dist/155.js.map +1 -0
  5. package/dist/172.js +1 -1
  6. package/dist/20.js +1 -1
  7. package/dist/290.js +1 -1
  8. package/dist/493.js +2 -0
  9. package/dist/493.js.map +1 -0
  10. package/dist/606.js +1 -1
  11. package/dist/627.js +1 -1
  12. package/dist/922.js +1 -0
  13. package/dist/922.js.map +1 -0
  14. package/dist/main.js +1 -1
  15. package/dist/main.js.map +1 -1
  16. package/dist/openmrs-esm-stock-management-app.js +1 -1
  17. package/dist/openmrs-esm-stock-management-app.js.buildmanifest.json +75 -51
  18. package/dist/openmrs-esm-stock-management-app.js.map +1 -1
  19. package/dist/routes.json +1 -1
  20. package/package.json +1 -1
  21. package/src/config-schema.ts +6 -0
  22. package/src/core/utils/utils.ts +29 -0
  23. package/src/index.ts +4 -0
  24. package/src/routes.json +9 -0
  25. package/src/stock-items/add-stock-item/transactions/printout/transactions-stockcard-printout.component.tsx +8 -12
  26. package/src/stock-items/add-stock-item/transactions/transactions.component.tsx +8 -12
  27. package/src/stock-items/stock-items.resource.ts +5 -5
  28. package/src/stock-lookups/stock-lookups.resource.ts +2 -2
  29. package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-items-table.scss +34 -0
  30. package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-items-table.tsx +111 -0
  31. package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-operation-expanded-row.component.tsx +87 -0
  32. package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-operation-expanded-row.scss +31 -0
  33. package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-operations-status.tsx +45 -0
  34. package/src/stock-operations/edit-stock-operation/edit-stock-operation-action-menu.component.tsx +41 -16
  35. package/src/stock-operations/stock-operation-reference.component.tsx +64 -0
  36. package/src/stock-operations/stock-operation-status/stock-operation-status-row.tsx +77 -0
  37. package/src/stock-operations/stock-operation-status/stock-operation-status.scss +32 -0
  38. package/src/stock-operations/stock-operation-status/stock-operation-status.tsx +45 -0
  39. package/src/stock-operations/stock-operation-types-selector/stock-operation-types-selector.component.tsx +30 -29
  40. package/src/stock-operations/stock-operation.utils.tsx +16 -79
  41. package/src/stock-operations/stock-operations-dialog/stock-operations-issue-stock-button.component.tsx +27 -39
  42. package/src/stock-operations/stock-operations-dialog/stock-operations-print-button.component.tsx +51 -59
  43. package/src/stock-operations/{stock-item-selector/stock-item-selector.resource.tsx → stock-operations-forms/hooks/useFilterableStockItems.ts} +4 -4
  44. package/src/stock-operations/stock-operations-forms/hooks/useFilteredOperationTypesByRoles.ts +30 -0
  45. package/src/stock-operations/stock-operations-forms/hooks/useOperationTypePermisions.ts +29 -0
  46. package/src/stock-operations/stock-operations-forms/hooks/useParties.ts +73 -0
  47. package/src/stock-operations/{users-selector/users-selector.resource.tsx → stock-operations-forms/hooks/useSearchUser.ts} +9 -7
  48. package/src/stock-operations/{batch-no-selector/batch-no-selector.resource.tsx → stock-operations-forms/hooks/useStockItemBatchNumbers.ts} +3 -3
  49. package/src/stock-operations/stock-operations-forms/hooks/useStockOperationLinks.ts +20 -0
  50. package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.component.tsx +72 -0
  51. package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.test.tsx +90 -0
  52. package/src/stock-operations/{add-stock-operation/stock-item-search/stock-item-search.scss → stock-operations-forms/input-components/input-components-styles.scss} +2 -2
  53. package/src/stock-operations/stock-operations-forms/input-components/qty-uim-selector.test.tsx +157 -0
  54. package/src/stock-operations/stock-operations-forms/input-components/quantity-uom-selector.component.tsx +53 -0
  55. package/src/stock-operations/stock-operations-forms/input-components/stock-item-search.component.tsx +79 -0
  56. package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.component.tsx +59 -0
  57. package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.test.tsx +216 -0
  58. package/src/stock-operations/{batch-no-selector → stock-operations-forms/input-components}/unique-batch-no-entry-input.component.tsx +12 -7
  59. package/src/stock-operations/stock-operations-forms/input-components/user-selector.test.tsx +110 -0
  60. package/src/stock-operations/stock-operations-forms/input-components/users-selector.component.tsx +111 -0
  61. package/src/stock-operations/stock-operations-forms/step1.test.tsx +303 -0
  62. package/src/stock-operations/stock-operations-forms/step2.test.tsx +254 -0
  63. package/src/stock-operations/stock-operations-forms/step3.test.tsx +223 -0
  64. package/src/stock-operations/stock-operations-forms/steps/base-operation-details-form-step.tsx +241 -0
  65. package/src/stock-operations/stock-operations-forms/steps/quantity-uom-cell.component.tsx +33 -0
  66. package/src/stock-operations/stock-operations-forms/steps/received-items.component.tsx +110 -0
  67. package/src/stock-operations/stock-operations-forms/steps/stock-availability-cell.component.tsx +51 -0
  68. package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-batch-no-cell.component.tsx +40 -0
  69. package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-cell.component.tsx +50 -0
  70. package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-expiry-cell.component.tsx +41 -0
  71. package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.component.tsx +281 -0
  72. package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.scc.scss +64 -0
  73. package/src/stock-operations/stock-operations-forms/steps/stock-operation-submission-form-step.component.tsx +243 -0
  74. package/src/stock-operations/stock-operations-forms/stock-issue-form-initializer-with-related-requisition-operation.component.tsx +55 -0
  75. package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.scss +41 -0
  76. package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.workspace.tsx +211 -0
  77. package/src/stock-operations/stock-operations-forms/stock-operation-form-header.component.tsx +166 -0
  78. package/src/stock-operations/stock-operations-forms/stock-operation-form.component.tsx +205 -0
  79. package/src/stock-operations/stock-operations-forms/stock-operation-form.scss +111 -0
  80. package/src/stock-operations/stock-operations-forms/stock-operation-related-link.component.tsx +45 -0
  81. package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stepper.scss +41 -0
  82. package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stock-operation-stepper.component.tsx +52 -0
  83. package/src/stock-operations/stock-operations-forms/stock-operations-form-utils.ts +32 -0
  84. package/src/stock-operations/stock-operations-table.component.tsx +57 -92
  85. package/src/stock-operations/stock-operations.resource.ts +16 -13
  86. package/src/stock-operations/validation-schema.ts +72 -14
  87. package/dist/766.js +0 -2
  88. package/dist/766.js.map +0 -1
  89. package/dist/822.js +0 -1
  90. package/dist/822.js.map +0 -1
  91. package/src/stock-operations/add-stock-operation/add-stock-operation.component.tsx +0 -349
  92. package/src/stock-operations/add-stock-operation/add-stock-operation.resource.tsx +0 -27
  93. package/src/stock-operations/add-stock-operation/add-stock-operation.scss +0 -60
  94. package/src/stock-operations/add-stock-operation/add-stock-operation.test.tsx +0 -192
  95. package/src/stock-operations/add-stock-operation/add-stock-operation.utils.tsx +0 -152
  96. package/src/stock-operations/add-stock-operation/add-stock-utils.ts +0 -103
  97. package/src/stock-operations/add-stock-operation/base-operation-details.component.tsx +0 -439
  98. package/src/stock-operations/add-stock-operation/base-operation-details.scss +0 -30
  99. package/src/stock-operations/add-stock-operation/received-items.component.tsx +0 -93
  100. package/src/stock-operations/add-stock-operation/stock-item-search/stock-item-search.component.tsx +0 -70
  101. package/src/stock-operations/add-stock-operation/stock-items-addition-row.component.tsx +0 -357
  102. package/src/stock-operations/add-stock-operation/stock-items-addition-row.resource.tsx +0 -0
  103. package/src/stock-operations/add-stock-operation/stock-items-addition-row.scss +0 -12
  104. package/src/stock-operations/add-stock-operation/stock-items-addition-row.test.tsx +0 -10
  105. package/src/stock-operations/add-stock-operation/stock-items-addition.component.scss +0 -17
  106. package/src/stock-operations/add-stock-operation/stock-items-addition.component.tsx +0 -254
  107. package/src/stock-operations/add-stock-operation/stock-operation-context/useStockOperationContext.tsx +0 -16
  108. package/src/stock-operations/add-stock-operation/stock-operation-reference.component.tsx +0 -39
  109. package/src/stock-operations/add-stock-operation/stock-operation-related-link.component.tsx +0 -38
  110. package/src/stock-operations/add-stock-operation/stock-operation-status.component.tsx +0 -170
  111. package/src/stock-operations/add-stock-operation/stock-operation-submission.component.tsx +0 -189
  112. package/src/stock-operations/add-stock-operation/stock-operation-submission.test.tsx +0 -138
  113. package/src/stock-operations/add-stock-operation/types.ts +0 -55
  114. package/src/stock-operations/add-stock-operation/validationSchema.ts +0 -54
  115. package/src/stock-operations/batch-no-selector/batch-no-selector.component.tsx +0 -114
  116. package/src/stock-operations/batch-no-selector/batch-no-selector.scss +0 -0
  117. package/src/stock-operations/batch-no-selector/batch-no-selector.test.tsx +0 -101
  118. package/src/stock-operations/party-selector/party-selector.component.tsx +0 -59
  119. package/src/stock-operations/qty-uom-selector/qty-uom-selector.component.tsx +0 -65
  120. package/src/stock-operations/qty-uom-selector/qty-uom-selector.resource.tsx +0 -0
  121. package/src/stock-operations/qty-uom-selector/qty-uom-selector.scss +0 -0
  122. package/src/stock-operations/qty-uom-selector/qty-uom-selector.test.tsx +0 -10
  123. package/src/stock-operations/stock-item-selector/stock-item-selector.component.tsx +0 -69
  124. package/src/stock-operations/stock-item-selector/stock-item-selector.scss +0 -0
  125. package/src/stock-operations/stock-item-selector/stock-item-selector.test.tsx +0 -10
  126. package/src/stock-operations/stock-operation-reason-selector/stock-operation-reason-selector.component.tsx +0 -62
  127. package/src/stock-operations/users-selector/users-selector.component.tsx +0 -75
  128. /package/dist/{766.js.LICENSE.txt → 493.js.LICENSE.txt} +0 -0
@@ -0,0 +1,303 @@
1
+ import { useConfig } from '@openmrs/esm-framework';
2
+ import { render, screen } from '@testing-library/react';
3
+ import React from 'react';
4
+ import { StockOperationType } from '../../core/api/types/stockOperation/StockOperationType';
5
+ import { useStockOperationTypes } from '../../stock-lookups/stock-lookups.resource';
6
+ import { useStockOperations } from '../stock-operations.resource';
7
+ import useParties from './hooks/useParties';
8
+ import StockOperationForm from './stock-operation-form.component';
9
+ import { MAIN_STORE_LOCATION_TAG } from '../../constants';
10
+ import {
11
+ adjustmentOpeationTypeMock,
12
+ disposalOperationTypeMock,
13
+ openingStockOperationTypeMock,
14
+ receiptOperationTypeMock,
15
+ requisitionOperationTypeMock,
16
+ returnOperationTypeMock,
17
+ stockIssueOperationtypeMock,
18
+ stockTakeOperationTypeMock,
19
+ tranferOutOperationTypeMock,
20
+ } from '../../../__mocks__';
21
+ jest.mock('react-i18next', () => ({
22
+ useTranslation: jest.fn().mockReturnValue({ t: (key) => key }),
23
+ }));
24
+
25
+ jest.mock('@openmrs/esm-framework', () => ({
26
+ ActionMenu: jest.fn(() => null),
27
+ showSnackbar: jest.fn(),
28
+ useDebounce: jest.fn((x) => x),
29
+ getGlobalStore: jest.fn(() => ({
30
+ getState: jest.fn(),
31
+ subscribe: jest.fn(),
32
+ setState: jest.fn(),
33
+ })),
34
+ parseDate: jest.fn((date) => new Date(date)),
35
+ showNotification: jest.fn(),
36
+ usePagination: jest.fn(() => ({ currentPage: 1, setPage: jest.fn() })),
37
+ useSession: jest.fn(() => ({ user: { display: 'Test User' } })),
38
+ useConfig: jest.fn(),
39
+ ErrorState: jest.fn(({ error }: { error: any }) => <div>{error}</div>),
40
+ launchWorkspace: jest.fn(),
41
+ }));
42
+
43
+ jest.mock('../../stock-lookups/stock-lookups.resource', () => ({
44
+ useStockOperationTypes: jest.fn(),
45
+ useUsers: jest.fn().mockReturnValue({ items: { results: [] }, isLoading: false }),
46
+ useUser: jest.fn().mockReturnValue({ data: { display: 'Test User' }, isLoading: false, error: null }),
47
+ useConcept: jest.fn().mockReturnValue({ items: { answers: [] }, isLoading: false, error: null }),
48
+ }));
49
+
50
+ jest.mock('../stock-operations.resource', () => ({
51
+ operationStatusColor: jest.fn(() => 'some-color'),
52
+ getStockOperationLinks: jest.fn(),
53
+ useStockOperations: jest.fn().mockReturnValue({
54
+ items: { results: [] },
55
+ isLoading: false,
56
+ error: null,
57
+ }),
58
+ useStockOperation: jest.fn().mockReturnValue({
59
+ items: undefined,
60
+ isLoading: false,
61
+ error: null,
62
+ }),
63
+ }));
64
+
65
+ jest.mock('./hooks/useParties', () => jest.fn());
66
+
67
+ describe('Stock Operation step 1 (baseoperation details)', () => {
68
+ beforeEach(() => {
69
+ const mockStockOperationTypes = { results: [] };
70
+ (useStockOperationTypes as jest.Mock).mockReturnValue(mockStockOperationTypes);
71
+ (useStockOperations as jest.Mock).mockReturnValue({ items: { results: [] }, isLoading: false, error: null });
72
+ (useConfig as jest.Mock).mockReturnValue({ autoPopulateResponsiblePerson: true });
73
+ });
74
+
75
+ it('should render loading state when loading parties info', async () => {
76
+ (useParties as jest.Mock).mockReturnValue({
77
+ destinationParties: [],
78
+ sourceParties: [],
79
+ isLoading: true,
80
+ error: null,
81
+ sourceTags: [],
82
+ destinationTags: [],
83
+ });
84
+ render(<StockOperationForm stockOperationType={receiptOperationTypeMock as any} />);
85
+ expect(screen.getByRole('progressbar')).toBeInTheDocument();
86
+ });
87
+
88
+ it('should render error state when parties loading fails', async () => {
89
+ (useParties as jest.Mock).mockReturnValue({
90
+ destinationParties: [],
91
+ sourceParties: [],
92
+ isLoading: false,
93
+ error: 'error',
94
+ sourceTags: [],
95
+ destinationTags: [],
96
+ });
97
+ render(<StockOperationForm stockOperationType={receiptOperationTypeMock as any} />);
98
+ expect(screen.getByText('error')).toBeInTheDocument();
99
+ });
100
+
101
+ it('should have only next btn and not previous btn', async () => {
102
+ (useParties as jest.Mock).mockReturnValue({
103
+ destinationParties: [],
104
+ sourceParties: [],
105
+ isLoading: false,
106
+ error: undefined,
107
+ sourceTags: [],
108
+ destinationTags: [],
109
+ });
110
+ render(<StockOperationForm stockOperationType={receiptOperationTypeMock as any} />);
111
+ expect(screen.getByRole('button', { name: /next/i })).toBeInTheDocument();
112
+ expect(screen.queryByRole('button', { name: /previous/i })).not.toBeInTheDocument();
113
+ });
114
+ it('should render operation type in title', async () => {
115
+ (useParties as jest.Mock).mockReturnValue({
116
+ destinationParties: [],
117
+ sourceParties: [],
118
+ isLoading: false,
119
+ error: undefined,
120
+ sourceTags: [],
121
+ destinationTags: [],
122
+ });
123
+ render(<StockOperationForm stockOperationType={receiptOperationTypeMock as any} />);
124
+ expect(screen.getByText(`${receiptOperationTypeMock.name} details`)).toBeInTheDocument();
125
+ });
126
+
127
+ it("should render combobox with 'from' name and 'chooseAsource' placeholder for receipt operation", async () => {
128
+ (useParties as jest.Mock).mockReturnValue({
129
+ destinationParties: [],
130
+ sourceParties: [],
131
+ isLoading: false,
132
+ error: undefined,
133
+ sourceTags: [],
134
+ destinationTags: [],
135
+ });
136
+ render(<StockOperationForm stockOperationType={receiptOperationTypeMock as any} />);
137
+ const sourceInput = screen.getByRole('combobox', {
138
+ name: (_, element) =>
139
+ element.getAttribute('placeholder') === 'chooseASource' && element.getAttribute('name') === 'sourceUuid',
140
+ });
141
+ expect(sourceInput).toBeInTheDocument();
142
+ expect(screen.getByLabelText('from')).toBeInTheDocument();
143
+ });
144
+ it("should render combobox with 'to' name and defaulted to 'main store' location in receipt operation", async () => {
145
+ (useParties as jest.Mock).mockReturnValue({
146
+ destinationParties: [],
147
+ sourceParties: [],
148
+ isLoading: false,
149
+ error: undefined,
150
+ sourceTags: [],
151
+ destinationTags: [],
152
+ });
153
+ render(<StockOperationForm stockOperationType={receiptOperationTypeMock as any} />);
154
+ expect(screen.getByLabelText('to')).toBeInTheDocument();
155
+ });
156
+
157
+ it("should render combobox with 'destinationUuid' name and 'chooseADestination' placeholder", async () => {
158
+ (useParties as jest.Mock).mockReturnValue({
159
+ destinationParties: [],
160
+ sourceParties: [],
161
+ isLoading: false,
162
+ error: undefined,
163
+ sourceTags: [],
164
+ destinationTags: [],
165
+ });
166
+ render(<StockOperationForm stockOperationType={receiptOperationTypeMock as any} />);
167
+ expect(
168
+ screen.getByRole('combobox', {
169
+ name: (_, element) =>
170
+ element.getAttribute('placeholder') === 'chooseADestination' &&
171
+ element.getAttribute('name') === 'destinationUuid',
172
+ }),
173
+ ).toBeInTheDocument();
174
+ expect(screen.getByLabelText('to')).toBeInTheDocument();
175
+ });
176
+ it("should render combobox with 'sourceUuid' name and 'chooseALocation' placeholder for disposal opertaion", async () => {
177
+ (useParties as jest.Mock).mockReturnValue({
178
+ destinationParties: [],
179
+ sourceParties: [],
180
+ isLoading: false,
181
+ error: undefined,
182
+ sourceTags: [],
183
+ destinationTags: [],
184
+ });
185
+ render(<StockOperationForm stockOperationType={disposalOperationTypeMock as any} />);
186
+ expect(
187
+ screen.getByRole('combobox', {
188
+ name: (_, element) =>
189
+ element.getAttribute('placeholder') === 'chooseALocation' && element.getAttribute('name') === 'sourceUuid',
190
+ }),
191
+ ).toBeInTheDocument();
192
+ expect(screen.getByLabelText('location')).toBeInTheDocument();
193
+ });
194
+
195
+ it('should not render reason input field for receipt operation', async () => {
196
+ (useParties as jest.Mock).mockReturnValue({
197
+ destinationParties: [],
198
+ sourceParties: [],
199
+ isLoading: false,
200
+ error: undefined,
201
+ sourceTags: [],
202
+ destinationTags: [],
203
+ });
204
+ render(<StockOperationForm stockOperationType={receiptOperationTypeMock as any} />);
205
+ expect(screen.queryByLabelText(/.*reason.*/i)).not.toBeInTheDocument();
206
+ });
207
+ it('should render reason input field for adjustment operation', async () => {
208
+ (useParties as jest.Mock).mockReturnValue({
209
+ destinationParties: [],
210
+ sourceParties: [],
211
+ isLoading: false,
212
+ error: undefined,
213
+ sourceTags: [],
214
+ destinationTags: [],
215
+ });
216
+ render(<StockOperationForm stockOperationType={adjustmentOpeationTypeMock as any} />);
217
+ expect(screen.getByLabelText(/.*reason.*/i)).toBeInTheDocument();
218
+ });
219
+ it('should not render reason input field for opening stock operation', async () => {
220
+ (useParties as jest.Mock).mockReturnValue({
221
+ destinationParties: [],
222
+ sourceParties: [],
223
+ isLoading: false,
224
+ error: undefined,
225
+ sourceTags: [],
226
+ destinationTags: [],
227
+ });
228
+ render(<StockOperationForm stockOperationType={openingStockOperationTypeMock as any} />);
229
+ expect(screen.queryByLabelText(/.*reason.*/i)).not.toBeInTheDocument();
230
+ });
231
+ it('should not render reason input field for requisition operation', async () => {
232
+ (useParties as jest.Mock).mockReturnValue({
233
+ destinationParties: [],
234
+ sourceParties: [],
235
+ isLoading: false,
236
+ error: undefined,
237
+ sourceTags: [],
238
+ destinationTags: [],
239
+ });
240
+ render(<StockOperationForm stockOperationType={requisitionOperationTypeMock as any} />);
241
+ expect(screen.queryByLabelText(/.*reason.*/i)).not.toBeInTheDocument();
242
+ });
243
+ it('should not render reason input field for return operation', async () => {
244
+ (useParties as jest.Mock).mockReturnValue({
245
+ destinationParties: [],
246
+ sourceParties: [],
247
+ isLoading: false,
248
+ error: undefined,
249
+ sourceTags: [],
250
+ destinationTags: [],
251
+ });
252
+ render(<StockOperationForm stockOperationType={returnOperationTypeMock as any} />);
253
+ expect(screen.queryByLabelText(/.*reason.*/i)).not.toBeInTheDocument();
254
+ });
255
+ it('should not render reason input field for issue operation', async () => {
256
+ (useParties as jest.Mock).mockReturnValue({
257
+ destinationParties: [],
258
+ sourceParties: [],
259
+ isLoading: false,
260
+ error: undefined,
261
+ sourceTags: [],
262
+ destinationTags: [],
263
+ });
264
+ render(<StockOperationForm stockOperationType={stockIssueOperationtypeMock as any} />);
265
+ expect(screen.queryByLabelText(/.*reason.*/i)).not.toBeInTheDocument();
266
+ });
267
+ it('should not render reason input field for tranfer out operation', async () => {
268
+ (useParties as jest.Mock).mockReturnValue({
269
+ destinationParties: [],
270
+ sourceParties: [],
271
+ isLoading: false,
272
+ error: undefined,
273
+ sourceTags: [],
274
+ destinationTags: [],
275
+ });
276
+ render(<StockOperationForm stockOperationType={tranferOutOperationTypeMock as any} />);
277
+ expect(screen.queryByLabelText(/.*reason.*/i)).not.toBeInTheDocument();
278
+ });
279
+ it('should render reason input field for disposal operation', async () => {
280
+ (useParties as jest.Mock).mockReturnValue({
281
+ destinationParties: [],
282
+ sourceParties: [],
283
+ isLoading: false,
284
+ error: undefined,
285
+ sourceTags: [],
286
+ destinationTags: [],
287
+ });
288
+ render(<StockOperationForm stockOperationType={disposalOperationTypeMock as any} />);
289
+ expect(screen.getByLabelText(/.*reason.*/i)).toBeInTheDocument();
290
+ });
291
+ it('should render reason input field for stock take operation', async () => {
292
+ (useParties as jest.Mock).mockReturnValue({
293
+ destinationParties: [],
294
+ sourceParties: [],
295
+ isLoading: false,
296
+ error: undefined,
297
+ sourceTags: [],
298
+ destinationTags: [],
299
+ });
300
+ render(<StockOperationForm stockOperationType={stockTakeOperationTypeMock as any} />);
301
+ expect(screen.getByLabelText(/.*reason.*/i)).toBeInTheDocument();
302
+ });
303
+ });
@@ -0,0 +1,254 @@
1
+ import React from 'react';
2
+ import { render, waitFor, screen, fireEvent } from '@testing-library/react';
3
+ import { showSnackbar, useConfig, ErrorState, launchWorkspace } from '@openmrs/esm-framework';
4
+ import { useStockOperationTypes, useUser } from '../../stock-lookups/stock-lookups.resource';
5
+ import { getStockOperationLinks } from '../stock-operations.resource';
6
+ import { StockOperationDTO } from '../../core/api/types/stockOperation/StockOperationDTO';
7
+ import { StockOperationType } from '../../core/api/types/stockOperation/StockOperationType';
8
+ import { useStockOperations } from '../stock-operations.resource';
9
+ import { closeOverlay } from '../../core/components/overlay/hook';
10
+ import StockOperationForm from './stock-operation-form.component';
11
+ import useParties from './hooks/useParties';
12
+ import userEvent from '@testing-library/user-event';
13
+ import { StockItemDTO } from '../../core/api/types/stockItem/StockItem';
14
+ import { useStockItem, useStockItems, useStockBatches } from '../../stock-items/stock-items.resource';
15
+ import { initialStockOperationValue } from '../../core/utils/utils';
16
+ import { useForm, useFormContext, Controller, FormProvider } from 'react-hook-form';
17
+ import { BaseStockOperationItemFormData, StockOperationItemFormData } from '../validation-schema';
18
+ import { useStockItemBatchInformationHook } from '../../stock-items/add-stock-item/batch-information/batch-information.resource';
19
+ import { useFilterableStockItems } from './hooks/useFilterableStockItems';
20
+ import { formatForDatePicker } from '../../constants';
21
+ import { receiptOperationTypeMock } from '../../../__mocks__';
22
+ jest.mock('react-i18next', () => ({
23
+ useTranslation: jest.fn().mockReturnValue({ t: (key) => key }),
24
+ }));
25
+
26
+ jest.mock('@openmrs/esm-framework', () => ({
27
+ ActionMenu: jest.fn(() => null),
28
+ showSnackbar: jest.fn(),
29
+ useDebounce: jest.fn((x) => x),
30
+ getGlobalStore: jest.fn(() => ({
31
+ getState: jest.fn(),
32
+ subscribe: jest.fn(),
33
+ setState: jest.fn(),
34
+ })),
35
+ parseDate: jest.fn((date) => new Date(date)),
36
+ showNotification: jest.fn(),
37
+ usePagination: jest.fn(() => ({ currentPage: 1, setPage: jest.fn() })),
38
+ useSession: jest.fn(() => ({ user: { display: 'Test User' } })),
39
+ useConfig: jest.fn(),
40
+ ErrorState: jest.fn(({ error }: { error: any }) => <div>{error}</div>),
41
+ launchWorkspace: jest.fn(),
42
+ }));
43
+
44
+ jest.mock('../../stock-lookups/stock-lookups.resource', () => ({
45
+ useStockOperationTypes: jest.fn(),
46
+ useUsers: jest.fn().mockReturnValue({ items: { results: [] }, isLoading: false }),
47
+ useUser: jest.fn().mockReturnValue({ data: { display: 'Test User' }, isLoading: false, error: null }),
48
+ }));
49
+
50
+ jest.mock('../stock-operations.resource', () => ({
51
+ operationStatusColor: jest.fn(() => 'some-color'),
52
+ getStockOperationLinks: jest.fn(),
53
+ useStockOperations: jest.fn().mockReturnValue({
54
+ items: { results: [] },
55
+ isLoading: false,
56
+ error: null,
57
+ }),
58
+ }));
59
+
60
+ jest.mock('../../core/components/overlay/hook', () => ({
61
+ closeOverlay: jest.fn(),
62
+ }));
63
+
64
+ jest.mock('../../stock-items/stock-items.resource', () => ({
65
+ useStockItem: jest.fn(),
66
+ useStockItems: jest.fn().mockReturnValue({
67
+ isLoading: false,
68
+ error: null,
69
+ items: {},
70
+ }),
71
+ useStockBatches: jest.fn().mockReturnValue({
72
+ isLoading: false,
73
+ error: null,
74
+
75
+ items: {},
76
+ }),
77
+ }));
78
+ jest.mock('./hooks/useFilterableStockItems', () => ({
79
+ useFilterableStockItems: jest.fn().mockReturnValue({
80
+ stockItemsList: [],
81
+ setLimit: jest.fn(),
82
+ setRepresentation: jest.fn(),
83
+ setSearchString: jest.fn(),
84
+ isLoading: false,
85
+ }),
86
+ }));
87
+ jest.mock('./hooks/useParties', () => jest.fn());
88
+ jest.mock('react-hook-form', () => ({
89
+ useForm: jest.fn().mockReturnValue({
90
+ watch: jest.fn(),
91
+ formState: {
92
+ errors: {},
93
+ },
94
+ resetField: jest.fn(),
95
+ getValues: jest.fn(),
96
+ setValue: jest.fn(),
97
+ handleSubmit: jest.fn(),
98
+ }),
99
+ useFormContext: jest.fn().mockReturnValue({
100
+ watch: jest.fn(),
101
+ formState: {
102
+ errors: {},
103
+ },
104
+ resetField: jest.fn(),
105
+ getValues: jest.fn(),
106
+ setValue: jest.fn(),
107
+ handleSubmit: jest.fn(),
108
+ }),
109
+ Controller: ({ render }) => render({ field: {}, fieldState: {} }),
110
+ FormProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
111
+ }));
112
+
113
+ jest.mock('../../stock-items/add-stock-item/batch-information/batch-information.resource', () => ({
114
+ useStockItemBatchInformationHook: jest.fn().mockReturnValue({
115
+ items: [],
116
+ totalCount: 0,
117
+ currentPage: 1,
118
+ currentPageSize: 10,
119
+ setCurrentPage: jest.fn(),
120
+ setPageSize: jest.fn(),
121
+ pageSizes: [],
122
+ isLoading: false,
123
+ error: undefined,
124
+ setSearchString: jest.fn(),
125
+ setStockItemUuid: jest.fn(),
126
+ setLocationUuid: jest.fn(),
127
+ setPartyUuid: jest.fn(),
128
+ setStockBatchUuid: jest.fn(),
129
+ }),
130
+ }));
131
+
132
+ describe('Stock Operation step 2 (stock operation items details)', () => {
133
+ beforeEach(() => {
134
+ const mockStockOperationTypes = { results: [] };
135
+ (useStockOperationTypes as jest.Mock).mockReturnValue(mockStockOperationTypes);
136
+ (useStockOperations as jest.Mock).mockReturnValue({ items: { results: [] }, isLoading: false, error: null });
137
+ (useConfig as jest.Mock).mockReturnValue({ autoPopulateResponsiblePerson: true });
138
+ (useParties as jest.Mock).mockReturnValue({
139
+ destinationParties: [],
140
+ sourceParties: [],
141
+ isLoading: false,
142
+ error: undefined,
143
+ sourceTags: [],
144
+ destinationTags: [],
145
+ });
146
+ });
147
+
148
+ it('should have both previous and next btns', async () => {
149
+ render(<StockOperationForm stockOperationType={receiptOperationTypeMock as any} />);
150
+ // MOVE TO STEP 2
151
+ await userEvent.click(screen.getByRole('button', { name: /Next/i }));
152
+
153
+ expect(screen.getByRole('button', { name: /Next/i })).toBeInTheDocument();
154
+ expect(screen.getByRole('button', { name: /previous/i })).toBeInTheDocument();
155
+ });
156
+
157
+ it('should render stock operation items table with item search component', async () => {
158
+ render(<StockOperationForm stockOperationType={receiptOperationTypeMock as any} />);
159
+ const nextButton = screen.getByRole('button', { name: /Next/i });
160
+ expect(nextButton).toBeInTheDocument();
161
+ await userEvent.click(nextButton);
162
+ expect(screen.getByRole('table')).toBeInTheDocument();
163
+ expect(
164
+ screen.getByRole('searchbox', {
165
+ name(accessibleName, element) {
166
+ return (
167
+ element.getAttribute('id') === 'search-stock-operation-item' &&
168
+ element.getAttribute('placeholder') === 'findItems' &&
169
+ element.getAttribute('name') === 'search-stock-operation-item'
170
+ );
171
+ },
172
+ }),
173
+ ).toBeInTheDocument();
174
+ });
175
+
176
+ it('should search stock operation item and render results', async () => {
177
+ const mocksetSearchString = jest.fn();
178
+ (useFilterableStockItems as jest.Mock).mockReturnValue({
179
+ stockItemsList: [
180
+ { uuid: 'mock-uuid', commonName: 'mock-common-name', drugName: 'mock-common-name' },
181
+ ] as Array<StockItemDTO>,
182
+ setLimit: jest.fn(),
183
+ setRepresentation: jest.fn(),
184
+ isLoading: false,
185
+ setSearchString: mocksetSearchString,
186
+ });
187
+ render(<StockOperationForm stockOperationType={receiptOperationTypeMock as any} />);
188
+ // ----- CLICK NEXT TO MOVE TO STEP 2 ---------
189
+ await userEvent.click(screen.getByRole('button', { name: /Next/i }));
190
+ // -------------------------------
191
+ const searchInput = screen.getByRole('searchbox', {
192
+ name: (_, element) => element.getAttribute('id') === 'search-stock-operation-item',
193
+ });
194
+ expect(searchInput).toBeInTheDocument();
195
+ await userEvent.click(searchInput);
196
+ await userEvent.type(searchInput, 'stock');
197
+ expect(mocksetSearchString).toHaveBeenCalledWith('stock');
198
+ expect(screen.getByText('mock-common-name'));
199
+ });
200
+
201
+ it('should properly handle stock operation item selection', async () => {
202
+ (useFilterableStockItems as jest.Mock).mockReturnValue({
203
+ stockItemsList: [
204
+ { uuid: 'mock-uuid', commonName: 'mock-common-name', drugName: 'mock-common-name' },
205
+ ] as Array<StockItemDTO>,
206
+ setLimit: jest.fn(),
207
+ setRepresentation: jest.fn(),
208
+ isLoading: false,
209
+ setSearchString: jest.fn(),
210
+ });
211
+ render(<StockOperationForm stockOperationType={receiptOperationTypeMock as any} />);
212
+ // ----- CLICK NEXT TO MOVE TO STEP 2 ---------
213
+ await userEvent.click(screen.getByRole('button', { name: /Next/i }));
214
+ // -------------------------------
215
+ const searchInput = screen.getByRole('searchbox', {
216
+ name: (_, element) => element.getAttribute('id') === 'search-stock-operation-item',
217
+ });
218
+ await userEvent.click(searchInput);
219
+ await userEvent.type(searchInput, 'stock');
220
+ await userEvent.click(screen.getByText('mock-common-name'));
221
+ expect(launchWorkspace).toHaveBeenCalled();
222
+ });
223
+
224
+ it('should render stock operation items in data table', async () => {
225
+ (useStockItem as jest.Mock).mockReturnValue({
226
+ isLoading: false,
227
+ error: null,
228
+ item: { commonName: 'mock-stock-item-common name', uuid: 'mock-uuid' } as StockItemDTO,
229
+ });
230
+ const mockQuantity = 99999;
231
+ const mockExpiration = new Date();
232
+ (useFormContext as jest.Mock).mockReturnValue({
233
+ watch: jest.fn().mockReturnValue([
234
+ {
235
+ quantity: mockQuantity,
236
+ expiration: mockExpiration,
237
+ },
238
+ ] as BaseStockOperationItemFormData),
239
+ resetField: jest.fn(),
240
+ formState: {
241
+ errors: {},
242
+ },
243
+ getValues: jest.fn(),
244
+ setValue: jest.fn(),
245
+ handleSubmit: jest.fn(),
246
+ });
247
+ render(<StockOperationForm stockOperationType={receiptOperationTypeMock as any} />);
248
+ // ----- CLICK NEXT TO MOVE TO STEP 2 ---------
249
+ await userEvent.click(screen.getByRole('button', { name: /Next/i }));
250
+ // -------------------------------
251
+ expect(screen.getByText(mockQuantity.toLocaleString())).toBeInTheDocument(); //Find by quentity
252
+ expect(screen.getByText(formatForDatePicker(mockExpiration))).toBeInTheDocument(); //Find by quentity
253
+ });
254
+ });