@openmrs/esm-fast-data-entry-app 1.4.2-pre.704 → 1.4.2-pre.706

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.
@@ -0,0 +1,94 @@
1
+ import React, { useEffect } from 'react';
2
+ import { render, screen, waitFor } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { useFormContext } from 'react-hook-form';
5
+ import GroupFormWorkflowContext from '../context/GroupFormWorkflowContext';
6
+ import SessionMetaWorkspace from './SessionMetaWorkspace';
7
+
8
+ jest.mock('../CancelModal', () => ({
9
+ __esModule: true,
10
+ default: () => null,
11
+ }));
12
+
13
+ jest.mock('./SessionDetailsForm', () => ({
14
+ __esModule: true,
15
+ default: function MockSessionDetailsForm() {
16
+ const { register, setValue } = useFormContext();
17
+
18
+ useEffect(() => {
19
+ setValue('sessionName', 'April Session');
20
+ setValue('practitionerName', 'Alice');
21
+ setValue('sessionDate', ['2026-04-15']);
22
+ setValue('sessionNotes', 'Bring notebooks');
23
+ }, [setValue]);
24
+
25
+ return (
26
+ <>
27
+ <input aria-label="Session Name" {...register('sessionName', { required: true })} />
28
+ <input aria-label="Practitioner Name" {...register('practitionerName', { required: true })} />
29
+ <input aria-label="Session Notes" {...register('sessionNotes', { required: true })} />
30
+ </>
31
+ );
32
+ },
33
+ }));
34
+
35
+ const renderSessionMetaWorkspace = (contextOverrides = {}) =>
36
+ render(
37
+ <GroupFormWorkflowContext.Provider
38
+ value={
39
+ {
40
+ workflowState: 'NEW_GROUP_SESSION',
41
+ patientUuids: ['patient-a'],
42
+ activeGroupUuid: 'group-1',
43
+ setSessionMeta: jest.fn(),
44
+ ...contextOverrides,
45
+ } as never
46
+ }
47
+ >
48
+ <SessionMetaWorkspace />
49
+ </GroupFormWorkflowContext.Provider>,
50
+ );
51
+
52
+ describe('SessionMetaWorkspace', () => {
53
+ it('submits the session metadata with the normalized session date', async () => {
54
+ const user = userEvent.setup();
55
+ const setSessionMeta = jest.fn();
56
+ renderSessionMetaWorkspace({ setSessionMeta });
57
+
58
+ await user.click(screen.getByRole('button', { name: 'Create New Session' }));
59
+
60
+ await waitFor(() => {
61
+ expect(setSessionMeta).toHaveBeenCalledWith(
62
+ expect.objectContaining({
63
+ groupUuid: 'group-1',
64
+ sessionName: 'April Session',
65
+ practitionerName: 'Alice',
66
+ sessionDate: '2026-04-15',
67
+ sessionNotes: 'Bring notebooks',
68
+ }),
69
+ );
70
+ });
71
+ });
72
+
73
+ it('shows the group selection error when submitted without a chosen group', async () => {
74
+ const user = userEvent.setup();
75
+ const setSessionMeta = jest.fn();
76
+ renderSessionMetaWorkspace({
77
+ activeGroupUuid: null,
78
+ setSessionMeta,
79
+ });
80
+
81
+ await user.click(screen.getByRole('button', { name: 'Create New Session' }));
82
+
83
+ expect(await screen.findByText('Please choose a group.')).toBeInTheDocument();
84
+ expect(setSessionMeta).not.toHaveBeenCalled();
85
+ });
86
+
87
+ it('disables session creation until the participant list is populated', () => {
88
+ renderSessionMetaWorkspace({
89
+ patientUuids: [],
90
+ });
91
+
92
+ expect(screen.getByRole('button', { name: 'Create New Session' })).toBeDisabled();
93
+ });
94
+ });
@@ -0,0 +1,121 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { showSnackbar, useConfig, useSession } from '@openmrs/esm-framework';
5
+ import GroupFormWorkflowContext from '../../context/GroupFormWorkflowContext';
6
+ import GroupSearchHeader from './GroupSearchHeader';
7
+
8
+ let selectedGroup;
9
+
10
+ jest.mock('../group-search/CompactGroupSearch', () => ({
11
+ __esModule: true,
12
+ default: ({ selectGroupAction }) => (
13
+ <button data-testid="compact-group-search" onClick={() => selectGroupAction(selectedGroup)}>
14
+ Select group
15
+ </button>
16
+ ),
17
+ }));
18
+
19
+ jest.mock('../../add-group-modal/AddGroupModal', () => ({
20
+ __esModule: true,
21
+ default: ({ isOpen }) => (isOpen ? <div data-testid="add-group-modal" /> : null),
22
+ }));
23
+
24
+ const mockShowSnackbar = jest.mocked(showSnackbar);
25
+ const mockUseConfig = jest.mocked(useConfig);
26
+ const mockUseSession = jest.mocked(useSession);
27
+
28
+ const renderGroupSearchHeader = (contextOverrides = {}) =>
29
+ render(
30
+ <GroupFormWorkflowContext.Provider
31
+ value={
32
+ {
33
+ activeGroupUuid: null,
34
+ setGroup: jest.fn(),
35
+ destroySession: jest.fn(),
36
+ ...contextOverrides,
37
+ } as never
38
+ }
39
+ >
40
+ <GroupSearchHeader />
41
+ </GroupFormWorkflowContext.Provider>,
42
+ );
43
+
44
+ describe('GroupSearchHeader', () => {
45
+ beforeEach(() => {
46
+ mockUseSession.mockReturnValue({
47
+ sessionLocation: {
48
+ uuid: 'session-location',
49
+ display: 'General Hospital',
50
+ },
51
+ } as never);
52
+ mockUseConfig.mockReturnValue({
53
+ enforcePatientListLocationMatch: true,
54
+ } as never);
55
+ });
56
+
57
+ it('blocks group selection when location enforcement is enabled and locations mismatch', async () => {
58
+ const user = userEvent.setup();
59
+ const setGroup = jest.fn();
60
+ selectedGroup = {
61
+ uuid: 'group-1',
62
+ location: {
63
+ uuid: 'other-location',
64
+ display: 'Remote Clinic',
65
+ },
66
+ };
67
+
68
+ renderGroupSearchHeader({ setGroup });
69
+
70
+ await user.click(screen.getByTestId('compact-group-search'));
71
+
72
+ expect(mockShowSnackbar).toHaveBeenCalledWith(
73
+ expect.objectContaining({
74
+ kind: 'error',
75
+ title: 'Location Mismatch',
76
+ subtitle: 'Cannot select group from Remote Clinic for a session at General Hospital',
77
+ }),
78
+ );
79
+ expect(setGroup).not.toHaveBeenCalled();
80
+ });
81
+
82
+ it('sorts cohort members by display name before storing the selected group', async () => {
83
+ const user = userEvent.setup();
84
+ const setGroup = jest.fn();
85
+ selectedGroup = {
86
+ uuid: 'group-1',
87
+ location: {
88
+ uuid: 'session-location',
89
+ display: 'General Hospital',
90
+ },
91
+ cohortMembers: [
92
+ { patient: { person: { names: [{ display: 'zoe zebra' }] } } },
93
+ { patient: { person: { names: [{ display: 'Alice Able' }] } } },
94
+ { patient: { person: { names: [{ display: 'ben Brown' }] } } },
95
+ ],
96
+ };
97
+
98
+ renderGroupSearchHeader({ setGroup });
99
+
100
+ await user.click(screen.getByTestId('compact-group-search'));
101
+
102
+ expect(setGroup).toHaveBeenCalledTimes(1);
103
+ expect(setGroup.mock.calls[0][0].cohortMembers.map((member) => member.patient.person.names[0].display)).toEqual([
104
+ 'Alice Able',
105
+ 'ben Brown',
106
+ 'zoe zebra',
107
+ ]);
108
+ });
109
+
110
+ it('opens the add-group modal and lets the user cancel the session', async () => {
111
+ const user = userEvent.setup();
112
+ const destroySession = jest.fn();
113
+ renderGroupSearchHeader({ destroySession });
114
+
115
+ await user.click(screen.getByRole('button', { name: 'Create New Group' }));
116
+ expect(screen.getByTestId('add-group-modal')).toBeInTheDocument();
117
+
118
+ await user.click(screen.getByRole('button', { name: 'Cancel' }));
119
+ expect(destroySession).toHaveBeenCalledTimes(1);
120
+ });
121
+ });
@@ -0,0 +1,113 @@
1
+ import React from 'react';
2
+ import { act, render, screen } from '@testing-library/react';
3
+ import { fetchCurrentPatient } from '@openmrs/esm-framework';
4
+ import useGetPatient from './useGetPatient';
5
+
6
+ jest.mock('@openmrs/esm-framework', () => ({
7
+ fetchCurrentPatient: jest.fn(),
8
+ }));
9
+
10
+ type Deferred<T> = {
11
+ promise: Promise<T>;
12
+ resolve: (value: T) => void;
13
+ };
14
+
15
+ const createDeferred = <T,>(): Deferred<T> => {
16
+ let resolve: (value: T) => void;
17
+ const promise = new Promise<T>((res) => {
18
+ resolve = res;
19
+ });
20
+
21
+ return {
22
+ promise,
23
+ resolve,
24
+ };
25
+ };
26
+
27
+ const mockFetchCurrentPatient = fetchCurrentPatient as jest.MockedFunction<typeof fetchCurrentPatient>;
28
+
29
+ const TestHarness = ({ patientUuid }: { patientUuid?: string }) => {
30
+ const patient = useGetPatient(patientUuid);
31
+ const patientName = patient ? (patient as { name: string }).name : 'none';
32
+
33
+ return <div data-testid="patient-name">{patientName}</div>;
34
+ };
35
+
36
+ describe('useGetPatient', () => {
37
+ beforeEach(() => {
38
+ jest.clearAllMocks();
39
+ });
40
+
41
+ it('ignores stale responses after patientUuid changes', async () => {
42
+ const patientARequest = createDeferred<fhir.Patient>();
43
+ const patientBRequest = createDeferred<fhir.Patient>();
44
+
45
+ mockFetchCurrentPatient.mockImplementation((uuid) => {
46
+ if (uuid === 'patient-a') {
47
+ return patientARequest.promise;
48
+ }
49
+
50
+ if (uuid === 'patient-b') {
51
+ return patientBRequest.promise;
52
+ }
53
+
54
+ throw new Error(`Unexpected patient uuid: ${uuid}`);
55
+ });
56
+
57
+ const { rerender } = render(<TestHarness patientUuid="patient-a" />);
58
+
59
+ rerender(<TestHarness patientUuid="patient-b" />);
60
+
61
+ await act(async () => {
62
+ patientBRequest.resolve({ id: 'patient-b', name: 'Patient B' } as unknown as fhir.Patient);
63
+ await patientBRequest.promise;
64
+ });
65
+
66
+ expect(screen.getByTestId('patient-name')).toHaveTextContent('Patient B');
67
+
68
+ await act(async () => {
69
+ patientARequest.resolve({ id: 'patient-a', name: 'Patient A' } as unknown as fhir.Patient);
70
+ await patientARequest.promise;
71
+ });
72
+
73
+ expect(screen.getByTestId('patient-name')).toHaveTextContent('Patient B');
74
+ expect(mockFetchCurrentPatient).toHaveBeenNthCalledWith(1, 'patient-a');
75
+ expect(mockFetchCurrentPatient).toHaveBeenNthCalledWith(2, 'patient-b');
76
+ });
77
+
78
+ it('clears the previous patient while the next patient is loading', async () => {
79
+ const patientBRequest = createDeferred<fhir.Patient>();
80
+ mockFetchCurrentPatient.mockImplementation((uuid) => {
81
+ if (uuid === 'patient-a') {
82
+ return Promise.resolve({ id: 'patient-a', name: 'Patient A' } as unknown as fhir.Patient);
83
+ }
84
+
85
+ if (uuid === 'patient-b') {
86
+ return patientBRequest.promise;
87
+ }
88
+
89
+ throw new Error(`Unexpected patient uuid: ${uuid}`);
90
+ });
91
+
92
+ const { rerender } = render(<TestHarness patientUuid="patient-a" />);
93
+
94
+ expect(await screen.findByText('Patient A')).toBeInTheDocument();
95
+
96
+ rerender(<TestHarness patientUuid="patient-b" />);
97
+
98
+ expect(screen.getByTestId('patient-name')).toHaveTextContent('none');
99
+ });
100
+
101
+ it('clears the patient and skips fetching when patientUuid is removed', async () => {
102
+ mockFetchCurrentPatient.mockResolvedValue({ id: 'patient-a', name: 'Patient A' } as unknown as fhir.Patient);
103
+
104
+ const { rerender } = render(<TestHarness patientUuid="patient-a" />);
105
+
106
+ expect(await screen.findByText('Patient A')).toBeInTheDocument();
107
+
108
+ rerender(<TestHarness />);
109
+
110
+ expect(screen.getByTestId('patient-name')).toHaveTextContent('none');
111
+ expect(mockFetchCurrentPatient).toHaveBeenCalledTimes(1);
112
+ });
113
+ });
@@ -1,10 +0,0 @@
1
- import { test, expect } from '@playwright/test';
2
- import { FastDataEntryPage } from '../pages';
3
-
4
- // This test is a sample E2E test. You can delete it.
5
-
6
- test('sample-test', async ({ page }) => {
7
- const fastDataEntryPage = new FastDataEntryPage(page);
8
- await fastDataEntryPage.goto();
9
- await expect(page.getByText('Fast Data Entry')).toBeVisible();
10
- });