@openmrs/esm-bed-management-app 9.2.1-pre.7327 → 9.2.1-pre.7339
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/.turbo/turbo-build.log +4 -4
- package/dist/1224.js +1 -1
- package/dist/1224.js.map +1 -1
- package/dist/3114.js +1 -1
- package/dist/3114.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/openmrs-esm-bed-management-app.js +1 -1
- package/dist/openmrs-esm-bed-management-app.js.buildmanifest.json +8 -8
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/bed-administration/bed-administration-table.component.tsx +5 -14
- package/src/bed-administration/form/bed-form.workspace.test.tsx +337 -0
- package/src/bed-administration/form/bed-form.workspace.tsx +191 -200
- package/src/routes.json +18 -5
- package/src/types.ts +30 -0
- package/src/ward-with-beds/ward-with-beds.component.tsx +2 -3
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { showSnackbar, useSession, type Workspace2DefinitionProps } from '@openmrs/esm-framework';
|
|
5
|
+
import { renderWithSwr } from 'tools';
|
|
6
|
+
import { type BedFormWorkspaceConfig, type BedWorkspaceData } from '../../types';
|
|
7
|
+
import { useBedTags, useLocationsWithAdmissionTag } from '../../summary/summary.resource';
|
|
8
|
+
import { editBed, saveBed, useBedType, useBedTagMappings } from './bed-form.resource';
|
|
9
|
+
import BedFormWorkspace from './bed-form.workspace';
|
|
10
|
+
|
|
11
|
+
jest.mock('./bed-form.resource', () => ({
|
|
12
|
+
saveBed: jest.fn(),
|
|
13
|
+
editBed: jest.fn(),
|
|
14
|
+
useBedType: jest.fn(),
|
|
15
|
+
useBedTagMappings: jest.fn(),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
jest.mock('../../summary/summary.resource', () => ({
|
|
19
|
+
useBedTags: jest.fn(),
|
|
20
|
+
useLocationsWithAdmissionTag: jest.fn(),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
const mockUseSession = jest.mocked(useSession);
|
|
24
|
+
const mockShowSnackbar = jest.mocked(showSnackbar);
|
|
25
|
+
const mockUseBedTags = jest.mocked(useBedTags);
|
|
26
|
+
const mockUseLocationsWithAdmissionTag = jest.mocked(useLocationsWithAdmissionTag);
|
|
27
|
+
const mockUseBedType = jest.mocked(useBedType);
|
|
28
|
+
const mockUseBedTagMappings = jest.mocked(useBedTagMappings);
|
|
29
|
+
const mockSaveBed = jest.mocked(saveBed);
|
|
30
|
+
const mockEditBed = jest.mocked(editBed);
|
|
31
|
+
|
|
32
|
+
const mockCloseWorkspace = jest.fn().mockResolvedValue(true);
|
|
33
|
+
const mockMutateBeds = jest.fn();
|
|
34
|
+
|
|
35
|
+
const mockBedData: BedWorkspaceData = {
|
|
36
|
+
uuid: 'bed-uuid-123',
|
|
37
|
+
bedNumber: 'BED-001',
|
|
38
|
+
status: 'AVAILABLE',
|
|
39
|
+
row: 1,
|
|
40
|
+
column: 1,
|
|
41
|
+
bedType: { name: 'Standard' },
|
|
42
|
+
location: { display: 'Ward A', uuid: 'location-uuid-123' },
|
|
43
|
+
bedTags: [{ uuid: 'tag-uuid-1', name: 'ICU' }],
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const mockLocations = [
|
|
47
|
+
{ display: 'Ward A', uuid: 'location-uuid-123', name: 'Ward A' },
|
|
48
|
+
{ display: 'Ward B', uuid: 'location-uuid-456', name: 'Ward B' },
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const mockBedTypes = [
|
|
52
|
+
{ name: 'Standard', uuid: 'bed-type-uuid-1' },
|
|
53
|
+
{ name: 'ICU', uuid: 'bed-type-uuid-2' },
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const mockBedTags = [
|
|
57
|
+
{ name: 'ICU', uuid: 'tag-uuid-1' },
|
|
58
|
+
{ name: 'Emergency', uuid: 'tag-uuid-2' },
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// Helper to create complete workspace props
|
|
62
|
+
const createWorkspaceProps = (config: BedFormWorkspaceConfig): Workspace2DefinitionProps<BedFormWorkspaceConfig> => ({
|
|
63
|
+
workspaceProps: config,
|
|
64
|
+
closeWorkspace: mockCloseWorkspace,
|
|
65
|
+
launchChildWorkspace: jest.fn(),
|
|
66
|
+
windowProps: null,
|
|
67
|
+
groupProps: null,
|
|
68
|
+
workspaceName: 'bed-form-workspace',
|
|
69
|
+
windowName: 'bed-management-window',
|
|
70
|
+
isRootWorkspace: true,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Helper to render workspace with default config
|
|
74
|
+
const renderBedFormWorkspace = (config: Partial<BedFormWorkspaceConfig> = {}) => {
|
|
75
|
+
const fullConfig = { mutateBeds: mockMutateBeds, ...config };
|
|
76
|
+
return renderWithSwr(<BedFormWorkspace {...createWorkspaceProps(fullConfig)} />);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Helper to fill out bed form with valid data
|
|
80
|
+
const fillBedForm = async (user: ReturnType<typeof userEvent.setup>, bedNumber: string, bedType = 'Standard') => {
|
|
81
|
+
const bedNumberInput = screen.getByLabelText(/bed number/i);
|
|
82
|
+
await user.type(bedNumberInput, bedNumber);
|
|
83
|
+
|
|
84
|
+
const bedTypeSelect = screen.getByLabelText(/bed type/i);
|
|
85
|
+
await user.selectOptions(bedTypeSelect, bedType);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Helper to submit form
|
|
89
|
+
const submitForm = async (user: ReturnType<typeof userEvent.setup>) => {
|
|
90
|
+
const saveButton = screen.getByRole('button', { name: /save/i });
|
|
91
|
+
await user.click(saveButton);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
describe('BedFormWorkspace', () => {
|
|
95
|
+
beforeEach(() => {
|
|
96
|
+
mockUseSession.mockReturnValue({
|
|
97
|
+
authenticated: true,
|
|
98
|
+
sessionId: 'test-session',
|
|
99
|
+
sessionLocation: { uuid: 'location-uuid-123', display: 'Ward A' },
|
|
100
|
+
currentProvider: { uuid: 'provider-uuid', identifier: 'provider-1' },
|
|
101
|
+
} as ReturnType<typeof useSession>);
|
|
102
|
+
|
|
103
|
+
mockUseLocationsWithAdmissionTag.mockReturnValue({
|
|
104
|
+
admissionLocations: mockLocations as any,
|
|
105
|
+
errorLoadingAdmissionLocations: null,
|
|
106
|
+
isLoadingAdmissionLocations: false,
|
|
107
|
+
isValidatingAdmissionLocations: false,
|
|
108
|
+
mutateAdmissionLocations: jest.fn(),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
mockUseBedType.mockReturnValue({
|
|
112
|
+
bedTypes: mockBedTypes as any,
|
|
113
|
+
isLoading: false,
|
|
114
|
+
error: null,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
mockUseBedTags.mockReturnValue({
|
|
118
|
+
bedTags: mockBedTags as any,
|
|
119
|
+
errorLoadingBedTags: null,
|
|
120
|
+
isLoadingBedTags: false,
|
|
121
|
+
isValidatingBedTags: false,
|
|
122
|
+
mutateBedTags: jest.fn(),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
mockUseBedTagMappings.mockReturnValue({
|
|
126
|
+
bedTagMappings: [],
|
|
127
|
+
isLoading: false,
|
|
128
|
+
error: null,
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('renders the add bed form correctly', () => {
|
|
133
|
+
renderBedFormWorkspace();
|
|
134
|
+
|
|
135
|
+
expect(screen.getByLabelText(/bed number/i)).toBeInTheDocument();
|
|
136
|
+
expect(screen.getByLabelText(/bed row/i)).toBeInTheDocument();
|
|
137
|
+
expect(screen.getByLabelText(/bed column/i)).toBeInTheDocument();
|
|
138
|
+
expect(screen.getByText(/location/i)).toBeInTheDocument();
|
|
139
|
+
expect(screen.getByLabelText(/occupancy status/i)).toBeInTheDocument();
|
|
140
|
+
expect(screen.getByLabelText(/bed type/i)).toBeInTheDocument();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('renders the edit bed form with pre-filled values', () => {
|
|
144
|
+
renderBedFormWorkspace({ bed: mockBedData });
|
|
145
|
+
|
|
146
|
+
expect(screen.getByDisplayValue('BED-001')).toBeInTheDocument();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('calls closeWorkspace when cancel is clicked', async () => {
|
|
150
|
+
const user = userEvent.setup();
|
|
151
|
+
renderBedFormWorkspace();
|
|
152
|
+
|
|
153
|
+
const cancelButton = screen.getByRole('button', { name: /cancel/i });
|
|
154
|
+
await user.click(cancelButton);
|
|
155
|
+
|
|
156
|
+
expect(mockCloseWorkspace).toHaveBeenCalled();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('handles form changes correctly', async () => {
|
|
160
|
+
const user = userEvent.setup();
|
|
161
|
+
renderBedFormWorkspace();
|
|
162
|
+
|
|
163
|
+
const bedNumberInput = screen.getByLabelText(/bed number/i);
|
|
164
|
+
await user.type(bedNumberInput, 'BED-01');
|
|
165
|
+
|
|
166
|
+
expect(bedNumberInput).toHaveValue('BED-01');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('submits a new bed successfully', async () => {
|
|
170
|
+
const user = userEvent.setup();
|
|
171
|
+
mockSaveBed.mockResolvedValue({ data: { uuid: 'new-bed-uuid' } } as any);
|
|
172
|
+
|
|
173
|
+
renderBedFormWorkspace();
|
|
174
|
+
await fillBedForm(user, 'BED-001');
|
|
175
|
+
await submitForm(user);
|
|
176
|
+
|
|
177
|
+
await waitFor(() => {
|
|
178
|
+
expect(mockSaveBed).toHaveBeenCalled();
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('submits an edited bed successfully', async () => {
|
|
183
|
+
const user = userEvent.setup();
|
|
184
|
+
mockEditBed.mockResolvedValue({ data: { uuid: 'bed-uuid-123' } } as any);
|
|
185
|
+
|
|
186
|
+
renderBedFormWorkspace({ bed: mockBedData });
|
|
187
|
+
|
|
188
|
+
const bedNumberInput = screen.getByLabelText(/bed number/i);
|
|
189
|
+
await user.clear(bedNumberInput);
|
|
190
|
+
await user.type(bedNumberInput, 'UPD-001');
|
|
191
|
+
|
|
192
|
+
await submitForm(user);
|
|
193
|
+
|
|
194
|
+
await waitFor(() => {
|
|
195
|
+
expect(mockEditBed).toHaveBeenCalled();
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('shows error snackbar when submission fails', async () => {
|
|
200
|
+
const user = userEvent.setup();
|
|
201
|
+
mockSaveBed.mockRejectedValue(new Error('Network error'));
|
|
202
|
+
|
|
203
|
+
renderBedFormWorkspace();
|
|
204
|
+
await fillBedForm(user, 'BED-001');
|
|
205
|
+
await submitForm(user);
|
|
206
|
+
|
|
207
|
+
await waitFor(() => {
|
|
208
|
+
expect(mockShowSnackbar).toHaveBeenCalledWith(
|
|
209
|
+
expect.objectContaining({
|
|
210
|
+
kind: 'error',
|
|
211
|
+
}),
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('prevents submission when form has not been modified', async () => {
|
|
217
|
+
const user = userEvent.setup();
|
|
218
|
+
mockSaveBed.mockResolvedValue({ data: { uuid: 'new-bed-uuid' } } as any);
|
|
219
|
+
renderBedFormWorkspace();
|
|
220
|
+
|
|
221
|
+
const saveButton = screen.getByRole('button', { name: /save/i });
|
|
222
|
+
await user.click(saveButton);
|
|
223
|
+
|
|
224
|
+
// Should not submit when form is pristine
|
|
225
|
+
expect(mockSaveBed).not.toHaveBeenCalled();
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('uses defaultLocation when provided', () => {
|
|
229
|
+
const defaultLocation = { display: 'Default Ward', uuid: 'default-location-uuid' };
|
|
230
|
+
|
|
231
|
+
renderBedFormWorkspace({ defaultLocation });
|
|
232
|
+
|
|
233
|
+
// Form should render successfully with default location
|
|
234
|
+
expect(screen.getByLabelText(/bed number/i)).toBeInTheDocument();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('prevents user from submitting bed number longer than 10 characters', async () => {
|
|
238
|
+
const user = userEvent.setup();
|
|
239
|
+
renderBedFormWorkspace();
|
|
240
|
+
|
|
241
|
+
await fillBedForm(user, 'VERYLONGBED123'); // 14 chars
|
|
242
|
+
await submitForm(user);
|
|
243
|
+
|
|
244
|
+
expect(mockSaveBed).not.toHaveBeenCalled();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('prevents user from submitting without required bed number', async () => {
|
|
248
|
+
const user = userEvent.setup();
|
|
249
|
+
mockSaveBed.mockResolvedValue({ data: { uuid: 'new-bed-uuid' } } as any);
|
|
250
|
+
renderBedFormWorkspace();
|
|
251
|
+
|
|
252
|
+
// Fill only bed type (not bed number)
|
|
253
|
+
const bedTypeSelect = screen.getByLabelText(/bed type/i);
|
|
254
|
+
await user.selectOptions(bedTypeSelect, 'Standard');
|
|
255
|
+
|
|
256
|
+
const saveButton = screen.getByRole('button', { name: /save/i });
|
|
257
|
+
await user.click(saveButton);
|
|
258
|
+
|
|
259
|
+
// Form validation should prevent submission
|
|
260
|
+
expect(mockSaveBed).not.toHaveBeenCalled();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('shows success message and refreshes data after creating bed', async () => {
|
|
264
|
+
const user = userEvent.setup();
|
|
265
|
+
mockSaveBed.mockResolvedValue({ data: { uuid: 'new-bed-uuid' } } as any);
|
|
266
|
+
|
|
267
|
+
renderBedFormWorkspace();
|
|
268
|
+
await fillBedForm(user, 'BED-100');
|
|
269
|
+
await submitForm(user);
|
|
270
|
+
|
|
271
|
+
await waitFor(() => {
|
|
272
|
+
expect(mockShowSnackbar).toHaveBeenCalledWith(
|
|
273
|
+
expect.objectContaining({
|
|
274
|
+
kind: 'success',
|
|
275
|
+
title: 'Success',
|
|
276
|
+
}),
|
|
277
|
+
);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
expect(mockMutateBeds).toHaveBeenCalled();
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('shows success message with different text when updating existing bed', async () => {
|
|
284
|
+
const user = userEvent.setup();
|
|
285
|
+
mockEditBed.mockResolvedValue({ data: { uuid: 'bed-uuid-123' } } as any);
|
|
286
|
+
|
|
287
|
+
renderBedFormWorkspace({ bed: mockBedData });
|
|
288
|
+
|
|
289
|
+
const bedNumberInput = screen.getByLabelText(/bed number/i);
|
|
290
|
+
await user.clear(bedNumberInput);
|
|
291
|
+
await user.type(bedNumberInput, 'BED-NEW');
|
|
292
|
+
|
|
293
|
+
await submitForm(user);
|
|
294
|
+
|
|
295
|
+
await waitFor(() => {
|
|
296
|
+
expect(mockShowSnackbar).toHaveBeenCalledWith(
|
|
297
|
+
expect.objectContaining({
|
|
298
|
+
kind: 'success',
|
|
299
|
+
}),
|
|
300
|
+
);
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('allows user to submit form after making changes', async () => {
|
|
305
|
+
const user = userEvent.setup();
|
|
306
|
+
mockSaveBed.mockResolvedValue({ data: { uuid: 'new-bed-uuid' } } as any);
|
|
307
|
+
renderBedFormWorkspace();
|
|
308
|
+
|
|
309
|
+
// Make changes to the form
|
|
310
|
+
await fillBedForm(user, 'BED-TEST');
|
|
311
|
+
await submitForm(user);
|
|
312
|
+
|
|
313
|
+
// Form should submit successfully after changes
|
|
314
|
+
await waitFor(() => {
|
|
315
|
+
expect(mockSaveBed).toHaveBeenCalled();
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('shows appropriate error message when network request fails', async () => {
|
|
320
|
+
const user = userEvent.setup();
|
|
321
|
+
const errorMessage = 'Network connection failed';
|
|
322
|
+
mockSaveBed.mockRejectedValue(new Error(errorMessage));
|
|
323
|
+
|
|
324
|
+
renderBedFormWorkspace();
|
|
325
|
+
await fillBedForm(user, 'BED-999');
|
|
326
|
+
await submitForm(user);
|
|
327
|
+
|
|
328
|
+
await waitFor(() => {
|
|
329
|
+
expect(mockShowSnackbar).toHaveBeenCalledWith(
|
|
330
|
+
expect.objectContaining({
|
|
331
|
+
kind: 'error',
|
|
332
|
+
subtitle: errorMessage,
|
|
333
|
+
}),
|
|
334
|
+
);
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
});
|