@kenyaemr/esm-admin-app 5.4.5-pre.795 → 5.4.5-pre.798
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 +1 -1
- package/dist/4124.js +1 -1
- package/dist/4124.js.map +1 -1
- package/dist/5117.js +1 -1
- package/dist/5117.js.map +1 -1
- package/dist/5767.js +1 -1
- package/dist/5767.js.map +1 -1
- package/dist/kenyaemr-esm-admin-app.js.buildmanifest.json +9 -9
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/components/facility-setup/facility-info.component.test.tsx +116 -0
- package/src/components/global-property/dashboard/global-property-dashboard.test.tsx +112 -0
- package/src/components/locations/auto-suggest/autosuggest.component.test.tsx +90 -0
- package/src/components/locations/auto-suggest/location-autosuggest.component.test.tsx +76 -0
- package/src/components/locations/common/results-tile.component.test.tsx +65 -0
- package/src/components/modal/hwr-empty.modal.test.tsx +67 -0
- package/src/components/provider-banner/provider-banner.component.tsx +35 -23
- package/src/components/provider-banner/provider-banner.test.tsx +109 -0
- package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-list/user-role-scope-list.component.test.tsx +154 -0
- package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-workspace/user-role-scope-fields.component.test.tsx +161 -0
- package/src/components/users/manage-users/user-details/user-details.component.test.tsx +135 -0
- package/src/components/users/manage-users/user-list/user-list.component.test.tsx +172 -0
- package/src/components/users/manage-users/user-list/user-list.component.tsx +9 -14
- package/src/left-pannel-link.test.tsx +69 -0
- package/src/root.component.test.tsx +394 -0
- package/src/testing/openmrs-fetch-mock.tsx +253 -0
- package/translations/en.json +2 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import { SWRConfig } from 'swr';
|
|
4
|
+
import { vi, type Mock } from 'vitest';
|
|
5
|
+
import { openmrsFetch as frameworkOpenmrsFetch } from '@openmrs/esm-framework';
|
|
6
|
+
import dayjs from 'dayjs';
|
|
7
|
+
|
|
8
|
+
import Root from '../root.component';
|
|
9
|
+
import { configSchema } from '../config-schema';
|
|
10
|
+
|
|
11
|
+
const configDefaults = {
|
|
12
|
+
licenseNumberUuid: configSchema.licenseNumberUuid._default,
|
|
13
|
+
licenseExpiryDateUuid: configSchema.licenseExpiryDateUuid._default,
|
|
14
|
+
providerNationalIdUuid: configSchema.providerNationalIdUuid._default,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type FetchRoute = {
|
|
18
|
+
match: string | RegExp;
|
|
19
|
+
/** Resolved value for the fetch (a FetchResponse-like object, usually `{ data: <body> }`), or a function of (url, init) returning one. */
|
|
20
|
+
response?: unknown;
|
|
21
|
+
error?: unknown;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Installs one URL-dispatching implementation on BOTH openmrsFetch instances in the
|
|
26
|
+
* test module graph: the framework mock's spy (used by the app's own hooks) and the
|
|
27
|
+
* partially mocked `@openmrs/esm-api` spy (used by the real framework hooks such as
|
|
28
|
+
* useFhirFetchAll/useOpenmrsPagination that run inside the official mock).
|
|
29
|
+
* The api-side mock is passed in because `vi.mock('@openmrs/esm-api', ...)` is hoisted
|
|
30
|
+
* and must live in the test file itself.
|
|
31
|
+
* First matching route wins; unmatched URLs reject loudly so fixture gaps surface fast.
|
|
32
|
+
*/
|
|
33
|
+
export function installFetchDispatcher(apiFetchMock: Mock, routes: Array<FetchRoute>) {
|
|
34
|
+
const dispatch = (url: string, init?: unknown) => {
|
|
35
|
+
const route = routes.find((r) => (typeof r.match === 'string' ? url.includes(r.match) : r.match.test(url)));
|
|
36
|
+
if (!route) {
|
|
37
|
+
return Promise.reject(new Error(`Unstubbed request: ${url}`));
|
|
38
|
+
}
|
|
39
|
+
if (route.error !== undefined) {
|
|
40
|
+
return Promise.reject(route.error);
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const value = typeof route.response === 'function' ? (route.response as Function)(url, init) : route.response;
|
|
44
|
+
return Promise.resolve(value);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
return Promise.reject(err);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
vi.mocked(frameworkOpenmrsFetch).mockImplementation(dispatch as never);
|
|
50
|
+
apiFetchMock.mockImplementation(dispatch as never);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** All [url, init] tuples fetched so far, regardless of which openmrsFetch instance served them. */
|
|
54
|
+
export function allFetchCalls(apiFetchMock: Mock): Array<[string, unknown]> {
|
|
55
|
+
return [...vi.mocked(frameworkOpenmrsFetch).mock.calls, ...apiFetchMock.mock.calls] as Array<[string, unknown]>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function allFetchUrls(apiFetchMock: Mock): Array<string> {
|
|
59
|
+
return allFetchCalls(apiFetchMock).map(([url]) => url);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** pushState under Root's BrowserRouter basename, then render Root with a fresh SWR cache. */
|
|
63
|
+
export function renderAdminApp(route: string) {
|
|
64
|
+
window.history.pushState({}, '', `/openmrs/spa/admin${route}`);
|
|
65
|
+
return render(
|
|
66
|
+
<SWRConfig value={{ provider: () => new Map(), dedupingInterval: 0 }}>
|
|
67
|
+
<Root />
|
|
68
|
+
</SWRConfig>,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Fixtures
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
export const usersFixture = [
|
|
77
|
+
{
|
|
78
|
+
uuid: 'user-1',
|
|
79
|
+
display: 'Alice Wanjiku',
|
|
80
|
+
username: 'awanjiku',
|
|
81
|
+
systemId: 'admin-1',
|
|
82
|
+
person: { uuid: 'person-1', display: 'Alice Wanjiku', gender: 'F' },
|
|
83
|
+
roles: [{ uuid: 'role-inv', display: 'Inventory Manager', description: 'Inventory' }],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
uuid: 'user-2',
|
|
87
|
+
display: 'Bob Otieno',
|
|
88
|
+
username: 'botieno',
|
|
89
|
+
systemId: 'clerk-2',
|
|
90
|
+
person: { uuid: 'person-2', display: 'Bob Otieno', gender: 'M' },
|
|
91
|
+
roles: [{ uuid: 'role-prov', display: 'Provider', description: 'Provider' }],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
uuid: 'user-3',
|
|
95
|
+
display: 'Carol Akinyi',
|
|
96
|
+
username: 'cakinyi',
|
|
97
|
+
systemId: 'nurse-3',
|
|
98
|
+
person: { uuid: 'person-3', display: 'Carol Akinyi', gender: 'F' },
|
|
99
|
+
roles: [],
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
export const providersFixture = [
|
|
104
|
+
{
|
|
105
|
+
uuid: 'provider-1',
|
|
106
|
+
display: 'Alice Wanjiku',
|
|
107
|
+
person: { uuid: 'person-1', display: 'Alice Wanjiku', gender: 'F' },
|
|
108
|
+
attributes: [
|
|
109
|
+
{
|
|
110
|
+
uuid: 'attr-1a',
|
|
111
|
+
attributeType: { uuid: configDefaults.licenseNumberUuid, display: 'License number' },
|
|
112
|
+
value: 'A123',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
uuid: 'attr-1b',
|
|
116
|
+
attributeType: { uuid: configDefaults.licenseExpiryDateUuid, display: 'License expiry' },
|
|
117
|
+
value: dayjs().add(30, 'day').toISOString(),
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
uuid: 'attr-1c',
|
|
121
|
+
attributeType: { uuid: configDefaults.providerNationalIdUuid, display: 'National ID' },
|
|
122
|
+
value: '12345678',
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
uuid: 'provider-2',
|
|
128
|
+
display: 'Bob Otieno',
|
|
129
|
+
person: { uuid: 'person-2', display: 'Bob Otieno', gender: 'M' },
|
|
130
|
+
attributes: [
|
|
131
|
+
{
|
|
132
|
+
uuid: 'attr-2a',
|
|
133
|
+
attributeType: { uuid: configDefaults.licenseNumberUuid, display: 'License number' },
|
|
134
|
+
value: 'B456',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
uuid: 'attr-2b',
|
|
138
|
+
attributeType: { uuid: configDefaults.licenseExpiryDateUuid, display: 'License expiry' },
|
|
139
|
+
value: dayjs().subtract(10, 'day').toISOString(),
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
uuid: 'provider-3',
|
|
145
|
+
display: 'Carol Akinyi',
|
|
146
|
+
person: { uuid: 'person-3', display: 'Carol Akinyi', gender: 'F' },
|
|
147
|
+
attributes: [],
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
export const roleConfigSettingFixture = {
|
|
152
|
+
results: [
|
|
153
|
+
{
|
|
154
|
+
uuid: 'role-config-1',
|
|
155
|
+
property: 'kenyaemr.userRole.config',
|
|
156
|
+
value: JSON.stringify([{ category: 'Core Inventory Roles', roles: ['Inventory Manager'] }]),
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export const locationTagsFixture = {
|
|
162
|
+
results: [
|
|
163
|
+
{ uuid: 'tag-1', display: 'Facility', name: 'Facility', description: 'A facility' },
|
|
164
|
+
{ uuid: 'tag-2', display: 'Clinic', name: 'Clinic', description: 'A clinic' },
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export const fhirLocationBundleFixture = {
|
|
169
|
+
resourceType: 'Bundle',
|
|
170
|
+
total: 2,
|
|
171
|
+
entry: [
|
|
172
|
+
{
|
|
173
|
+
resource: {
|
|
174
|
+
id: 'loc-1',
|
|
175
|
+
name: 'Mbagathi Hospital',
|
|
176
|
+
description: 'Main referral facility',
|
|
177
|
+
meta: { tag: [{ system: 'http://fhir.openmrs.org/ext/location-tag', code: 'Facility' }] },
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
resource: {
|
|
182
|
+
id: 'loc-2',
|
|
183
|
+
name: 'Kibera Clinic',
|
|
184
|
+
description: 'Satellite clinic',
|
|
185
|
+
meta: { tag: [{ system: 'http://fhir.openmrs.org/ext/location-tag', code: 'Clinic' }] },
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
link: [],
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
export const facilityRegistryFixture = {
|
|
193
|
+
official_name: 'Mbagathi County Hospital',
|
|
194
|
+
registration_number: 'REG-001',
|
|
195
|
+
fr_code: 'FR-12345',
|
|
196
|
+
fid_code: 'FID-999',
|
|
197
|
+
keph_level: 'Level 4',
|
|
198
|
+
facility_type: 'Hospital',
|
|
199
|
+
facility_ownership: 'Ministry of Health',
|
|
200
|
+
is_hub: true,
|
|
201
|
+
license_number: 'LIC-777',
|
|
202
|
+
regulatory_body: 'KMPDC',
|
|
203
|
+
license_status: 'ACTIVE',
|
|
204
|
+
license_start_date: '2026-01-01',
|
|
205
|
+
license_end_date: '2026-12-31',
|
|
206
|
+
regulatory_operational_status: 'OPERATIONAL',
|
|
207
|
+
sha_operational_status: 'ACTIVE',
|
|
208
|
+
sha_contract_status: 'CONTRACTED',
|
|
209
|
+
bed_occupancy: { totalBeds: 120 },
|
|
210
|
+
address: {
|
|
211
|
+
country: 'Kenya',
|
|
212
|
+
county: 'Nairobi',
|
|
213
|
+
sub_county: 'Dagoretti',
|
|
214
|
+
town: 'Nairobi',
|
|
215
|
+
physical_location: 'Mbagathi Way',
|
|
216
|
+
postal_address: 'P.O. Box 1',
|
|
217
|
+
},
|
|
218
|
+
facility_phone_number: '+254700000000',
|
|
219
|
+
facility_email: 'info@mbagathi.go.ke',
|
|
220
|
+
last_synced_at: '2026-07-01T08:00:00.000Z',
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export const etlLogFixture = [
|
|
224
|
+
{
|
|
225
|
+
script_name: 'sp_update_etl_tables',
|
|
226
|
+
start_time: '2026-07-01T08:00:00',
|
|
227
|
+
stop_time: '2026-07-01T08:05:00',
|
|
228
|
+
status: 'Success',
|
|
229
|
+
},
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
export const globalPropertiesFixture = [
|
|
233
|
+
{ uuid: 'gp-1', property: 'setting.one', value: 'value1', description: 'First setting' },
|
|
234
|
+
{ uuid: 'gp-2', property: 'setting.two', value: 'value2', description: 'Second setting' },
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
/** Happy-path routes for every endpoint the routed pages hit. Prepend overrides per test. */
|
|
238
|
+
export function defaultRoutes(): Array<FetchRoute> {
|
|
239
|
+
return [
|
|
240
|
+
{ match: '/ws/rest/v1/user?v=', response: { data: { results: usersFixture } } },
|
|
241
|
+
{ match: '/ws/rest/v1/provider?v=', response: { data: { results: providersFixture } } },
|
|
242
|
+
{ match: 'systemsetting?q=kenyaemr.userRole.config', response: { data: roleConfigSettingFixture } },
|
|
243
|
+
{ match: '/ws/rest/v1/locationtag?v=', response: { data: locationTagsFixture } },
|
|
244
|
+
{ match: /fhir2\/R4\/Location/, response: { data: fhirLocationBundleFixture } },
|
|
245
|
+
{ match: 'facility-registry/sync', response: { data: facilityRegistryFixture } },
|
|
246
|
+
{ match: 'virtualclaims/facility-registry', response: { data: facilityRegistryFixture } },
|
|
247
|
+
{ match: /kemrchart\//, response: { data: { data: etlLogFixture } } },
|
|
248
|
+
{
|
|
249
|
+
match: 'systemsetting?v=default',
|
|
250
|
+
response: { data: { results: globalPropertiesFixture, links: [], totalCount: globalPropertiesFixture.length } },
|
|
251
|
+
},
|
|
252
|
+
];
|
|
253
|
+
}
|
package/translations/en.json
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"enterIdentifierNumber": "Enter identifier number",
|
|
55
55
|
"error": "Error",
|
|
56
56
|
"errorDeletingUserScope": "Error deleting a user scope",
|
|
57
|
+
"errorLoadingProvider": "Error loading provider information",
|
|
57
58
|
"errorSyncMsg": "Failed to sync {{identifier}}: {{error}}",
|
|
58
59
|
"etlAdministration": "ETL Administration",
|
|
59
60
|
"etlOperation": "ETL operations",
|
|
@@ -226,6 +227,7 @@
|
|
|
226
227
|
"providerFail": "Failed to save provider",
|
|
227
228
|
"providerFailedSubtitle": "An error occurred while creating provider",
|
|
228
229
|
"providerId": "Provider Id",
|
|
230
|
+
"providerLabel": "Provider:",
|
|
229
231
|
"providerIdentifiers": "Create a Provider account for this user",
|
|
230
232
|
"providerLicense": "License Number",
|
|
231
233
|
"providerLicensePlaceholder": "e.g. GP/2026/638333",
|