@openmrs/esm-patient-tests-app 11.3.1-pre.9433 → 11.3.1-pre.9437
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 +3 -3
- package/dist/1477.js +1 -1
- package/dist/1477.js.map +1 -1
- package/dist/1935.js +1 -1
- package/dist/1935.js.map +1 -1
- package/dist/3509.js +1 -1
- package/dist/3509.js.map +1 -1
- package/dist/4300.js +1 -1
- package/dist/6301.js +1 -1
- package/dist/6301.js.map +1 -1
- package/dist/7202.js +1 -1
- package/dist/7202.js.map +1 -1
- package/dist/8555.js +1 -1
- package/dist/8555.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-patient-tests-app.js +1 -1
- package/dist/openmrs-esm-patient-tests-app.js.buildmanifest.json +19 -19
- package/dist/routes.json +1 -1
- package/package.json +2 -2
- package/src/index.ts +1 -1
- package/src/routes.json +1 -1
- package/src/test-orders/add-test-order/add-test-order.test.tsx +1 -1
- package/src/test-orders/add-test-order/add-test-order.workspace.tsx +1 -1
- package/src/test-orders/add-test-order/test-order-form.component.tsx +1 -1
- package/src/test-orders/add-test-order/test-type-search.component.tsx +1 -1
- package/src/test-orders/lab-order-basket-panel/lab-order-basket-panel.extension.tsx +1 -1
- package/src/test-orders/lab-order-basket-panel/lab-order-basket-panel.test.tsx +2 -2
- package/src/test-results/filter/filter-context.test.tsx +556 -0
- package/src/test-results/filter/filter-context.tsx +1 -1
- package/src/test-results/filter/filter-reducer.test.ts +540 -0
- package/src/test-results/filter/filter-reducer.ts +1 -1
- package/src/test-results/filter/filter-set.test.tsx +694 -0
- package/src/test-results/grouped-timeline/grouped-timeline.test.tsx +1 -1
- package/src/test-results/grouped-timeline/useObstreeData.test.ts +471 -0
- package/src/test-results/individual-results-table-tablet/usePanelData.tsx +40 -26
- package/src/test-results/loadPatientTestData/helpers.ts +29 -12
- package/src/test-results/loadPatientTestData/usePatientResultsData.ts +18 -7
- package/src/test-results/overview/external-overview.extension.tsx +1 -2
- package/src/test-results/print-modal/print-modal.extension.tsx +1 -1
- package/src/test-results/results-viewer/results-viewer.extension.tsx +7 -3
- package/src/test-results/tree-view/tree-view.component.tsx +6 -1
- package/src/test-results/tree-view/tree-view.test.tsx +117 -1
- package/src/test-results/trendline/trendline.component.tsx +88 -52
- package/src/test-results/ui-elements/reset-filters-empty-state/filter-empty-data-illustration.tsx +2 -2
- package/translations/en.json +1 -1
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { FilterProvider, FilterContext } from './filter-context';
|
|
5
|
+
import { type TreeNode } from './filter-types';
|
|
6
|
+
|
|
7
|
+
// Test component to access context values
|
|
8
|
+
const TestConsumer = () => {
|
|
9
|
+
const {
|
|
10
|
+
activeTests,
|
|
11
|
+
someChecked,
|
|
12
|
+
totalResultsCount,
|
|
13
|
+
filteredResultsCount,
|
|
14
|
+
timelineData,
|
|
15
|
+
tableData,
|
|
16
|
+
toggleVal,
|
|
17
|
+
updateParent,
|
|
18
|
+
resetTree,
|
|
19
|
+
} = useContext(FilterContext);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div>
|
|
23
|
+
<div data-testid="active-tests">{activeTests.join(',')}</div>
|
|
24
|
+
<div data-testid="some-checked">{someChecked.toString()}</div>
|
|
25
|
+
<div data-testid="total-count">{totalResultsCount}</div>
|
|
26
|
+
<div data-testid="filtered-count">{filteredResultsCount}</div>
|
|
27
|
+
<div data-testid="timeline-loaded">{timelineData?.loaded.toString()}</div>
|
|
28
|
+
<div data-testid="timeline-rows">{timelineData?.data?.rowData?.length || 0}</div>
|
|
29
|
+
<div data-testid="table-groups">{tableData?.length || 0}</div>
|
|
30
|
+
<button onClick={() => toggleVal('Test1')}>Toggle Test1</button>
|
|
31
|
+
<button onClick={() => updateParent('Panel1')}>Toggle Panel1</button>
|
|
32
|
+
<button onClick={resetTree}>Reset</button>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const mockRoots: Array<TreeNode> = [
|
|
38
|
+
{
|
|
39
|
+
display: 'Complete Blood Count',
|
|
40
|
+
flatName: 'CBC',
|
|
41
|
+
hasData: true,
|
|
42
|
+
subSets: [
|
|
43
|
+
{
|
|
44
|
+
display: 'Hemoglobin',
|
|
45
|
+
flatName: 'CBC: Hemoglobin',
|
|
46
|
+
hasData: true,
|
|
47
|
+
obs: [
|
|
48
|
+
{
|
|
49
|
+
obsDatetime: '2024-01-15T10:00:00.000Z',
|
|
50
|
+
value: '12.5',
|
|
51
|
+
interpretation: 'NORMAL',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
obsDatetime: '2024-01-10T10:00:00.000Z',
|
|
55
|
+
value: '12.0',
|
|
56
|
+
interpretation: 'NORMAL',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
subSets: [],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
display: 'White Blood Cell Count',
|
|
63
|
+
flatName: 'CBC: WBC',
|
|
64
|
+
hasData: true,
|
|
65
|
+
obs: [
|
|
66
|
+
{
|
|
67
|
+
obsDatetime: '2024-01-15T10:00:00.000Z',
|
|
68
|
+
value: '7.5',
|
|
69
|
+
interpretation: 'NORMAL',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
subSets: [],
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
display: 'Lipid Panel',
|
|
78
|
+
flatName: 'Lipid',
|
|
79
|
+
hasData: true,
|
|
80
|
+
subSets: [
|
|
81
|
+
{
|
|
82
|
+
display: 'Total Cholesterol',
|
|
83
|
+
flatName: 'Lipid: Cholesterol',
|
|
84
|
+
hasData: true,
|
|
85
|
+
obs: [
|
|
86
|
+
{
|
|
87
|
+
obsDatetime: '2024-01-20T10:00:00.000Z',
|
|
88
|
+
value: '180',
|
|
89
|
+
interpretation: 'NORMAL',
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
subSets: [],
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
describe('FilterContext', () => {
|
|
99
|
+
describe('Initialization', () => {
|
|
100
|
+
it('should initialize with roots data', async () => {
|
|
101
|
+
render(
|
|
102
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
103
|
+
<TestConsumer />
|
|
104
|
+
</FilterProvider>,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
await waitFor(() => {
|
|
108
|
+
expect(screen.getByTestId('timeline-loaded')).toHaveTextContent('true');
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should compute totalResultsCount from all observations', async () => {
|
|
113
|
+
render(
|
|
114
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
115
|
+
<TestConsumer />
|
|
116
|
+
</FilterProvider>,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
await waitFor(() => {
|
|
120
|
+
// 2 hemoglobin + 1 WBC + 1 cholesterol = 4 total
|
|
121
|
+
expect(screen.getByTestId('total-count')).toHaveTextContent('4');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should show filteredResultsCount equal to totalResultsCount when no filters applied', async () => {
|
|
126
|
+
render(
|
|
127
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
128
|
+
<TestConsumer />
|
|
129
|
+
</FilterProvider>,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
await waitFor(() => {
|
|
133
|
+
expect(screen.getByTestId('filtered-count')).toHaveTextContent('4');
|
|
134
|
+
});
|
|
135
|
+
expect(screen.getByTestId('some-checked')).toHaveTextContent('false');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('Active tests tracking', () => {
|
|
140
|
+
it('should track activeTests when checkboxes are toggled', async () => {
|
|
141
|
+
const user = userEvent.setup();
|
|
142
|
+
|
|
143
|
+
render(
|
|
144
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
145
|
+
<TestConsumer />
|
|
146
|
+
</FilterProvider>,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
await waitFor(() => {
|
|
150
|
+
expect(screen.getByTestId('timeline-loaded')).toHaveTextContent('true');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const toggleButton = screen.getByRole('button', { name: /toggle test1/i });
|
|
154
|
+
await user.click(toggleButton);
|
|
155
|
+
|
|
156
|
+
expect(screen.getByTestId('active-tests')).toHaveTextContent('Test1');
|
|
157
|
+
expect(screen.getByTestId('some-checked')).toHaveTextContent('true');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should update someChecked when tests are selected', async () => {
|
|
161
|
+
const user = userEvent.setup();
|
|
162
|
+
|
|
163
|
+
render(
|
|
164
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
165
|
+
<TestConsumer />
|
|
166
|
+
</FilterProvider>,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
await waitFor(() => {
|
|
170
|
+
expect(screen.getByTestId('timeline-loaded')).toHaveTextContent('true');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
expect(screen.getByTestId('some-checked')).toHaveTextContent('false');
|
|
174
|
+
|
|
175
|
+
await user.click(screen.getByRole('button', { name: /toggle test1/i }));
|
|
176
|
+
|
|
177
|
+
expect(screen.getByTestId('some-checked')).toHaveTextContent('true');
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe('Filtered results count', () => {
|
|
182
|
+
it('should update filteredResultsCount when filters are applied', async () => {
|
|
183
|
+
const user = userEvent.setup();
|
|
184
|
+
|
|
185
|
+
const FilterTestComponent = () => {
|
|
186
|
+
const { toggleVal, filteredResultsCount, totalResultsCount, someChecked } = useContext(FilterContext);
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<div>
|
|
190
|
+
<div data-testid="total-count">{totalResultsCount}</div>
|
|
191
|
+
<div data-testid="filtered-count">{filteredResultsCount}</div>
|
|
192
|
+
<div data-testid="some-checked">{someChecked.toString()}</div>
|
|
193
|
+
<button onClick={() => toggleVal('CBC: Hemoglobin')}>Toggle Hemoglobin</button>
|
|
194
|
+
</div>
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
render(
|
|
199
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
200
|
+
<FilterTestComponent />
|
|
201
|
+
</FilterProvider>,
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
await waitFor(() => {
|
|
205
|
+
expect(screen.getByTestId('total-count')).toHaveTextContent('4');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
expect(screen.getByTestId('filtered-count')).toHaveTextContent('4');
|
|
209
|
+
expect(screen.getByTestId('some-checked')).toHaveTextContent('false');
|
|
210
|
+
|
|
211
|
+
await user.click(screen.getByRole('button', { name: /toggle hemoglobin/i }));
|
|
212
|
+
await waitFor(() => {
|
|
213
|
+
expect(screen.getByTestId('filtered-count')).toHaveTextContent('2');
|
|
214
|
+
});
|
|
215
|
+
expect(screen.getByTestId('some-checked')).toHaveTextContent('true');
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('Timeline data', () => {
|
|
220
|
+
it('should generate timeline data with all tests when no filters applied', async () => {
|
|
221
|
+
render(
|
|
222
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
223
|
+
<TestConsumer />
|
|
224
|
+
</FilterProvider>,
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
await waitFor(() => {
|
|
228
|
+
expect(screen.getByTestId('timeline-loaded')).toHaveTextContent('true');
|
|
229
|
+
});
|
|
230
|
+
expect(screen.getByTestId('timeline-rows')).toHaveTextContent('3');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should filter timeline data when tests are selected', async () => {
|
|
234
|
+
const user = userEvent.setup();
|
|
235
|
+
|
|
236
|
+
const TimelineTestComponent = () => {
|
|
237
|
+
const { toggleVal, timelineData } = useContext(FilterContext);
|
|
238
|
+
|
|
239
|
+
return (
|
|
240
|
+
<div>
|
|
241
|
+
<div data-testid="timeline-rows">{timelineData?.data?.rowData?.length || 0}</div>
|
|
242
|
+
<button onClick={() => toggleVal('CBC: Hemoglobin')}>Toggle Hemoglobin</button>
|
|
243
|
+
</div>
|
|
244
|
+
);
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
render(
|
|
248
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
249
|
+
<TimelineTestComponent />
|
|
250
|
+
</FilterProvider>,
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
await waitFor(() => {
|
|
254
|
+
expect(screen.getByTestId('timeline-rows')).toHaveTextContent('3');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
await user.click(screen.getByRole('button', { name: /toggle hemoglobin/i }));
|
|
258
|
+
|
|
259
|
+
await waitFor(() => {
|
|
260
|
+
expect(screen.getByTestId('timeline-rows')).toHaveTextContent('1');
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('should sort timeline observations by date descending', async () => {
|
|
265
|
+
const TimelineDetailsComponent = () => {
|
|
266
|
+
const { timelineData } = useContext(FilterContext);
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<div>
|
|
270
|
+
<div data-testid="timeline-loaded">{timelineData?.loaded.toString()}</div>
|
|
271
|
+
{timelineData?.data?.parsedTime?.sortedTimes && (
|
|
272
|
+
<div data-testid="first-time">{timelineData.data.parsedTime.sortedTimes[0]}</div>
|
|
273
|
+
)}
|
|
274
|
+
</div>
|
|
275
|
+
);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
render(
|
|
279
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
280
|
+
<TimelineDetailsComponent />
|
|
281
|
+
</FilterProvider>,
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
await waitFor(() => {
|
|
285
|
+
expect(screen.getByTestId('timeline-loaded')).toHaveTextContent('true');
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const firstTime = screen.getByTestId('first-time').textContent;
|
|
289
|
+
expect(firstTime).toContain('2024-01-20');
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
describe('Table data', () => {
|
|
294
|
+
it('should generate grouped table data', async () => {
|
|
295
|
+
render(
|
|
296
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
297
|
+
<TestConsumer />
|
|
298
|
+
</FilterProvider>,
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
await waitFor(() => {
|
|
302
|
+
const tableGroups = screen.getByTestId('table-groups');
|
|
303
|
+
expect(parseInt(tableGroups.textContent || '0')).toBeGreaterThan(0);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should group observations by panel and date', async () => {
|
|
308
|
+
const TableTestComponent = () => {
|
|
309
|
+
const { tableData } = useContext(FilterContext);
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
<div>
|
|
313
|
+
<div data-testid="table-groups">{tableData?.length || 0}</div>
|
|
314
|
+
{tableData?.map((group, index) => (
|
|
315
|
+
<div key={index} data-testid={`group-${index}`}>
|
|
316
|
+
{group.key} - {group.date} - {group.entries.length}
|
|
317
|
+
</div>
|
|
318
|
+
))}
|
|
319
|
+
</div>
|
|
320
|
+
);
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
render(
|
|
324
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
325
|
+
<TableTestComponent />
|
|
326
|
+
</FilterProvider>,
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
await waitFor(() => {
|
|
330
|
+
expect(screen.getByTestId('table-groups')).not.toHaveTextContent('0');
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const groups = screen.getAllByTestId(/^group-/);
|
|
334
|
+
expect(groups.length).toBeGreaterThan(0);
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
describe('Actions', () => {
|
|
339
|
+
it('should toggle individual test via toggleVal', async () => {
|
|
340
|
+
const user = userEvent.setup();
|
|
341
|
+
|
|
342
|
+
const ActionTestComponent = () => {
|
|
343
|
+
const { toggleVal, checkboxes } = useContext(FilterContext);
|
|
344
|
+
|
|
345
|
+
return (
|
|
346
|
+
<div>
|
|
347
|
+
<div data-testid="checkbox-state">{checkboxes['CBC: Hemoglobin']?.toString() || 'false'}</div>
|
|
348
|
+
<button onClick={() => toggleVal('CBC: Hemoglobin')}>Toggle</button>
|
|
349
|
+
</div>
|
|
350
|
+
);
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
render(
|
|
354
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
355
|
+
<ActionTestComponent />
|
|
356
|
+
</FilterProvider>,
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
await waitFor(() => {
|
|
360
|
+
expect(screen.getByTestId('checkbox-state')).toBeInTheDocument();
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
expect(screen.getByTestId('checkbox-state')).toHaveTextContent('false');
|
|
364
|
+
|
|
365
|
+
await user.click(screen.getByRole('button', { name: /toggle/i }));
|
|
366
|
+
|
|
367
|
+
await waitFor(() => {
|
|
368
|
+
expect(screen.getByTestId('checkbox-state')).toHaveTextContent('true');
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('should reset all filters via resetTree', async () => {
|
|
373
|
+
const user = userEvent.setup();
|
|
374
|
+
|
|
375
|
+
const ResetTestComponent = () => {
|
|
376
|
+
const { toggleVal, resetTree, checkboxes, someChecked } = useContext(FilterContext);
|
|
377
|
+
|
|
378
|
+
return (
|
|
379
|
+
<div>
|
|
380
|
+
<div data-testid="some-checked">{someChecked.toString()}</div>
|
|
381
|
+
<div data-testid="hemoglobin-state">{checkboxes['CBC: Hemoglobin']?.toString() || 'false'}</div>
|
|
382
|
+
<button onClick={() => toggleVal('CBC: Hemoglobin')}>Toggle Hemoglobin</button>
|
|
383
|
+
<button onClick={resetTree}>Reset</button>
|
|
384
|
+
</div>
|
|
385
|
+
);
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
render(
|
|
389
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
390
|
+
<ResetTestComponent />
|
|
391
|
+
</FilterProvider>,
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
await waitFor(() => {
|
|
395
|
+
expect(screen.getByTestId('some-checked')).toBeInTheDocument();
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
await user.click(screen.getByRole('button', { name: /toggle hemoglobin/i }));
|
|
399
|
+
|
|
400
|
+
await waitFor(() => {
|
|
401
|
+
expect(screen.getByTestId('some-checked')).toHaveTextContent('true');
|
|
402
|
+
});
|
|
403
|
+
expect(screen.getByTestId('hemoglobin-state')).toHaveTextContent('true');
|
|
404
|
+
|
|
405
|
+
await user.click(screen.getByRole('button', { name: /reset/i }));
|
|
406
|
+
|
|
407
|
+
await waitFor(() => {
|
|
408
|
+
expect(screen.getByTestId('some-checked')).toHaveTextContent('false');
|
|
409
|
+
});
|
|
410
|
+
expect(screen.getByTestId('hemoglobin-state')).toHaveTextContent('false');
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
describe('Edge cases', () => {
|
|
415
|
+
it('should handle empty roots gracefully', () => {
|
|
416
|
+
render(
|
|
417
|
+
<FilterProvider roots={[]} isLoading={false}>
|
|
418
|
+
<TestConsumer />
|
|
419
|
+
</FilterProvider>,
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
expect(screen.getByTestId('total-count')).toHaveTextContent('0');
|
|
423
|
+
expect(screen.getByTestId('filtered-count')).toHaveTextContent('0');
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('should handle tests with no observations', async () => {
|
|
427
|
+
const emptyRoots: Array<TreeNode> = [
|
|
428
|
+
{
|
|
429
|
+
display: 'Empty Panel',
|
|
430
|
+
flatName: 'Empty',
|
|
431
|
+
hasData: false,
|
|
432
|
+
subSets: [
|
|
433
|
+
{
|
|
434
|
+
display: 'Empty Test',
|
|
435
|
+
flatName: 'Empty: Test',
|
|
436
|
+
hasData: false,
|
|
437
|
+
obs: [],
|
|
438
|
+
subSets: [],
|
|
439
|
+
},
|
|
440
|
+
],
|
|
441
|
+
},
|
|
442
|
+
];
|
|
443
|
+
|
|
444
|
+
render(
|
|
445
|
+
<FilterProvider roots={emptyRoots} isLoading={false}>
|
|
446
|
+
<TestConsumer />
|
|
447
|
+
</FilterProvider>,
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
await waitFor(() => {
|
|
451
|
+
expect(screen.getByTestId('total-count')).toHaveTextContent('0');
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('should reflect isLoading prop', () => {
|
|
456
|
+
const LoadingTestComponent = () => {
|
|
457
|
+
const { isLoading } = useContext(FilterContext);
|
|
458
|
+
return <div data-testid="is-loading">{isLoading.toString()}</div>;
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
const { rerender } = render(
|
|
462
|
+
<FilterProvider roots={mockRoots} isLoading={true}>
|
|
463
|
+
<LoadingTestComponent />
|
|
464
|
+
</FilterProvider>,
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
expect(screen.getByTestId('is-loading')).toHaveTextContent('true');
|
|
468
|
+
|
|
469
|
+
rerender(
|
|
470
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
471
|
+
<LoadingTestComponent />
|
|
472
|
+
</FilterProvider>,
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
expect(screen.getByTestId('is-loading')).toHaveTextContent('false');
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
describe('Computed values reactivity', () => {
|
|
480
|
+
it('should recompute timelineData when checkboxes change', async () => {
|
|
481
|
+
const user = userEvent.setup();
|
|
482
|
+
|
|
483
|
+
const ReactivityTestComponent = () => {
|
|
484
|
+
const { toggleVal, timelineData } = useContext(FilterContext);
|
|
485
|
+
|
|
486
|
+
return (
|
|
487
|
+
<div>
|
|
488
|
+
<div data-testid="row-count">{timelineData?.data?.rowData?.length || 0}</div>
|
|
489
|
+
<button onClick={() => toggleVal('CBC: Hemoglobin')}>Toggle Hemoglobin</button>
|
|
490
|
+
<button onClick={() => toggleVal('CBC: WBC')}>Toggle WBC</button>
|
|
491
|
+
</div>
|
|
492
|
+
);
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
render(
|
|
496
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
497
|
+
<ReactivityTestComponent />
|
|
498
|
+
</FilterProvider>,
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
await waitFor(() => {
|
|
502
|
+
expect(screen.getByTestId('row-count')).toHaveTextContent('3');
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
await user.click(screen.getByRole('button', { name: /toggle hemoglobin/i }));
|
|
506
|
+
|
|
507
|
+
await waitFor(() => {
|
|
508
|
+
expect(screen.getByTestId('row-count')).toHaveTextContent('1');
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
await user.click(screen.getByRole('button', { name: /toggle wbc/i }));
|
|
512
|
+
|
|
513
|
+
await waitFor(() => {
|
|
514
|
+
expect(screen.getByTestId('row-count')).toHaveTextContent('2');
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it('should recompute filteredResultsCount when selections change', async () => {
|
|
519
|
+
const user = userEvent.setup();
|
|
520
|
+
|
|
521
|
+
const CountTestComponent = () => {
|
|
522
|
+
const { toggleVal, filteredResultsCount } = useContext(FilterContext);
|
|
523
|
+
|
|
524
|
+
return (
|
|
525
|
+
<div>
|
|
526
|
+
<div data-testid="filtered-count">{filteredResultsCount}</div>
|
|
527
|
+
<button onClick={() => toggleVal('CBC: Hemoglobin')}>Toggle Hemoglobin</button>
|
|
528
|
+
<button onClick={() => toggleVal('Lipid: Cholesterol')}>Toggle Cholesterol</button>
|
|
529
|
+
</div>
|
|
530
|
+
);
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
render(
|
|
534
|
+
<FilterProvider roots={mockRoots} isLoading={false}>
|
|
535
|
+
<CountTestComponent />
|
|
536
|
+
</FilterProvider>,
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
await waitFor(() => {
|
|
540
|
+
expect(screen.getByTestId('filtered-count')).toHaveTextContent('4');
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
await user.click(screen.getByRole('button', { name: /toggle hemoglobin/i }));
|
|
544
|
+
|
|
545
|
+
await waitFor(() => {
|
|
546
|
+
expect(screen.getByTestId('filtered-count')).toHaveTextContent('2');
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
await user.click(screen.getByRole('button', { name: /toggle cholesterol/i }));
|
|
550
|
+
|
|
551
|
+
await waitFor(() => {
|
|
552
|
+
expect(screen.getByTestId('filtered-count')).toHaveTextContent('3');
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
});
|
|
@@ -195,7 +195,7 @@ const FilterProvider = ({ roots, isLoading, children }: FilterProviderProps) =>
|
|
|
195
195
|
if (roots.length && !Object.keys(state.parents).length) {
|
|
196
196
|
actions.initialize(roots);
|
|
197
197
|
}
|
|
198
|
-
}, [actions, state, roots]);
|
|
198
|
+
}, [actions, state.parents, roots]);
|
|
199
199
|
|
|
200
200
|
const totalResultsCount: number = useMemo(() => {
|
|
201
201
|
let count = 0;
|