diff --git a/.github/workflows/docker/docker-compose.yml b/.github/workflows/docker/docker-compose.yml deleted file mode 100644 index 23c3b5a43..000000000 --- a/.github/workflows/docker/docker-compose.yml +++ /dev/null @@ -1,39 +0,0 @@ -# This docker compose file is used to create a backend environment for the e2e.yml workflow. -version: "3.7" - -services: - backend: - image: openmrs/openmrs-reference-application-3-backend:${TAG:-nightly} - depends_on: - - db - environment: - OMRS_CONFIG_MODULE_WEB_ADMIN: "true" - OMRS_CONFIG_AUTO_UPDATE_DATABASE: "true" - OMRS_CONFIG_CREATE_TABLES: "true" - OMRS_CONFIG_CONNECTION_SERVER: db - OMRS_CONFIG_CONNECTION_DATABASE: openmrs - OMRS_CONFIG_CONNECTION_USERNAME: ${OPENMRS_DB_USER:-openmrs} - OMRS_CONFIG_CONNECTION_PASSWORD: ${OPENMRS_DB_PASSWORD:-openmrs} - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8080/openmrs"] - timeout: 5s - volumes: - - openmrs-data:/openmrs/data - ports: - - 9000:8080 - - # MariaDB - db: - image: mariadb:10.8.2 - command: "mysqld --character-set-server=utf8 --collation-server=utf8_general_ci" - environment: - MYSQL_DATABASE: openmrs - MYSQL_USER: ${OPENMRS_DB_USER:-openmrs} - MYSQL_PASSWORD: ${OPENMRS_DB_PASSWORD:-openmrs} - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-openmrs} - volumes: - - db-data:/var/lib/mysql - -volumes: - openmrs-data: ~ - db-data: ~ diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 3a5ae0834..27a6f2f8d 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -9,8 +9,7 @@ on: - main jobs: - testOnPR: - if: ${{ github.event_name == 'pull_request' }} + main: runs-on: ubuntu-latest timeout-minutes: 60 steps: @@ -62,57 +61,3 @@ jobs: name: playwright-report path: playwright-report/ retention-days: 30 - - testOnPush: - if: ${{ github.event_name == 'push' }} - runs-on: ubuntu-latest - timeout-minutes: 60 - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - - name: Copy test environment variables - run: cp example.env .env - - - name: Setup node - uses: actions/setup-node@v3 - with: - node-version: "18.x" - - - name: Cache dependencies - id: cache - uses: actions/cache@v3 - with: - path: '**/node_modules' - key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - - - name: Install dependencies - if: steps.cache.outputs.cache-hit != 'true' - run: yarn install --immutable - - - name: Install Playwright Browsers - run: npx playwright install chromium --with-deps - - - name: Build apps - run: yarn turbo run build --color --concurrency=5 - - - name: Run dev server - run: bash e2e/support/github/run-e2e-docker-env.sh - - - name: Wait for OpenMRS instance to start - run: while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' http://localhost:8080/openmrs/login.htm)" != "200" ]]; do sleep 10; done - - - name: Run E2E tests - run: yarn playwright test - - - name: Stop dev server - if: "!cancelled()" - run: docker stop $(docker ps -a -q) - - - name: Upload report - uses: actions/upload-artifact@v3 - if: always() - with: - name: playwright-report - path: playwright-report/ - retention-days: 30 diff --git a/README.md b/README.md index 1c275725e..799e32212 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This repository contains frontend modules for the OpenMRS SPA. These modules rel - [Active visits app](packages/esm-active-visits-app/) - [Appointments app](packages/esm-appointments-app/) -- [Outpatient app](packages/esm-outpatient-app/README.md) +- [Service queues](packages/esm-service-queues-app/README.md) - [Patient search](packages/esm-patient-search-app) - [Patient registration](packages/esm-patient-registration-app) - [Patient list](packages/esm-patient-list-app) @@ -71,6 +71,50 @@ Please read our [contributing](http://o3-dev.docs.openmrs.org/#/getting_started/ ## Running tests +To run tests for all packages, run: + +```bash +yarn turbo test +``` + +To run tests in `watch` mode, run: + +```bash +yarn turbo test:watch +``` + +To run tests for a specific package, pass the package name to the `--filter` flag. For example, to run tests for `esm-patient-conditions-app`, run: + +```bash +yarn turbo test --filter="esm-patient-conditions-app" +``` + +To run a specific test file, run: + +```bash +yarn turbo test -- basic-search +``` + +The above command will only run tests in the file or files that match the provided string. + +You can also run the matching tests from above in watch mode by running: + +```bash +yarn turbo test:watch --basic-search +``` + +To generate a `coverage` report, run: + +```bash +yarn turbo coverage +``` + +By default, `turbo` will cache test runs. This means that re-running tests wihout changing any of the related files will return the cached logs from the last run. To bypass the cache, run tests with the `force` flag, as follows: + +```bash +yarn turbo test --force +``` + ### Unit tests To run unit tests, use: diff --git a/e2e/support/github/docker-compose.yml b/e2e/support/github/docker-compose.yml index 0c49fd169..d230464f4 100644 --- a/e2e/support/github/docker-compose.yml +++ b/e2e/support/github/docker-compose.yml @@ -1,4 +1,5 @@ -# This docker compose file is used to create a backend environment for the e2e.yml workflow. +# This docker compose file is used to create a backend environment for the e2e.yml workflow. +# The images are pre-filled with data so that the backend environment can be started within a short time. version: "3.7" services: @@ -15,35 +16,9 @@ services: API_URL: /openmrs backend: - image: openmrs/openmrs-reference-application-3-backend:${TAG:-nightly} + image: openmrs/openmrs-reference-application-3-backend:nightly-with-data depends_on: - db - environment: - OMRS_CONFIG_MODULE_WEB_ADMIN: "true" - OMRS_CONFIG_AUTO_UPDATE_DATABASE: "true" - OMRS_CONFIG_CREATE_TABLES: "true" - OMRS_CONFIG_CONNECTION_SERVER: db - OMRS_CONFIG_CONNECTION_DATABASE: openmrs - OMRS_CONFIG_CONNECTION_USERNAME: ${OPENMRS_DB_USER:-openmrs} - OMRS_CONFIG_CONNECTION_PASSWORD: ${OPENMRS_DB_PASSWORD:-openmrs} - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8080/openmrs"] - timeout: 5s - volumes: - - openmrs-data:/openmrs/data - # MariaDB db: - image: mariadb:10.8.2 - command: "mysqld --character-set-server=utf8 --collation-server=utf8_general_ci" - environment: - MYSQL_DATABASE: openmrs - MYSQL_USER: ${OPENMRS_DB_USER:-openmrs} - MYSQL_PASSWORD: ${OPENMRS_DB_PASSWORD:-openmrs} - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-openmrs} - volumes: - - db-data:/var/lib/mysql - -volumes: - openmrs-data: ~ - db-data: ~ + image: openmrs/openmrs-reference-application-3-db:nightly-with-data diff --git a/packages/esm-active-visits-app/package.json b/packages/esm-active-visits-app/package.json index f895249c8..709fd6ca2 100644 --- a/packages/esm-active-visits-app/package.json +++ b/packages/esm-active-visits-app/package.json @@ -14,8 +14,8 @@ "build": "webpack --mode production", "analyze": "webpack --mode=production --env.analyze=true", "lint": "cross-env TIMING=1 eslint src --ext ts,tsx", - "test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests", - "test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js", + "test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests --color", + "test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js --color", "coverage": "yarn test --coverage", "typescript": "tsc", "extract-translations": "i18next 'src/**/*.component.tsx'" diff --git a/packages/esm-active-visits-app/src/active-visits-widget/active-visits.component.tsx b/packages/esm-active-visits-app/src/active-visits-widget/active-visits.component.tsx index 6bf2042b3..2177e2285 100644 --- a/packages/esm-active-visits-app/src/active-visits-widget/active-visits.component.tsx +++ b/packages/esm-active-visits-app/src/active-visits-widget/active-visits.component.tsx @@ -247,15 +247,21 @@ const ActiveVisitsTable = () => { {rows.map((row, index) => { const visit = activeVisits.find((visit) => visit.id === row.id); + if (!visit) { + return null; + } + + const patientLink = `$\{openmrsSpaBase}/patient/${visit.patientUuid}/chart/Patient%20Summary`; + return ( - + {row.cells.map((cell) => ( - {cell.info.header === 'name' ? ( - + {cell.info.header === 'name' && visit.patientUuid ? ( + {cell.value} ) : ( diff --git a/packages/esm-active-visits-app/src/active-visits-widget/active-visits.scss b/packages/esm-active-visits-app/src/active-visits-widget/active-visits.scss index a00f60cd2..d69cdd819 100644 --- a/packages/esm-active-visits-app/src/active-visits-widget/active-visits.scss +++ b/packages/esm-active-visits-app/src/active-visits-widget/active-visits.scss @@ -89,7 +89,6 @@ .tabletHeading { text-align: left; text-transform: capitalize; - margin-bottom: spacing.$spacing-05; h4 { @include type.type-style('heading-compact-02'); diff --git a/packages/esm-active-visits-app/src/active-visits-widget/active-visits.test.tsx b/packages/esm-active-visits-app/src/active-visits-widget/active-visits.test.tsx new file mode 100644 index 000000000..51f20ed7c --- /dev/null +++ b/packages/esm-active-visits-app/src/active-visits-widget/active-visits.test.tsx @@ -0,0 +1,136 @@ +import React from 'react'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import ActiveVisitsTable from './active-visits.component'; +import { useConfig, usePagination } from '@openmrs/esm-framework'; +import { useActiveVisits } from './active-visits.resource'; + +const mockedUsePagination = usePagination as jest.Mock; +const mockActiveVisits = useActiveVisits as jest.Mock; + +const mockActiveVisitsData = [{ id: '1', name: 'John Doe', visitType: 'Checkup', patientUuid: 'uuid1' }]; +jest.mock('./active-visits.resource', () => ({ + ...jest.requireActual('./active-visits.resource'), + useActiveVisits: jest.fn(() => ({ + activeVisits: mockActiveVisitsData, + isLoading: false, + isValidating: false, + error: null, + })), +})); + +jest.mock('@openmrs/esm-framework', () => ({ + ...jest.requireActual('@openmrs/esm-framework'), + useConfig: jest.fn(() => ({ activeVisits: { pageSizes: [10, 20, 30, 40, 50], pageSize: 10 } })), + usePagination: jest.fn().mockImplementation((data) => ({ + currentPage: 1, + goTo: () => {}, + results: data, + paginated: false, + })), +})); + +describe('ActiveVisitsTable', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders data table with active visits', () => { + render(); + + expect(screen.getByText('Visit Time')).toBeInTheDocument(); + expect(screen.getByText('ID Number')).toBeInTheDocument(); + const expectedColumnHeaders = [/Visit Time/, /ID Number/, /Name/, /Gender/, /Age/, /Visit Type/]; + expectedColumnHeaders.forEach((header) => { + expect(screen.getByRole('columnheader', { name: new RegExp(header, 'i') })).toBeInTheDocument(); + }); + + const patientNameLink = screen.getByText('John Doe'); + expect(patientNameLink).toBeInTheDocument(); + expect(patientNameLink.tagName).toBe('A'); + }); + + it.skip('filters active visits based on search input', () => { + mockActiveVisits.mockImplementationOnce(() => ({ + activeVisits: [ + { id: '1', name: 'John Doe', visitType: 'Checkup', patientUuid: 'uuid1' }, + { id: '2', name: 'Some One', visitType: 'Checkup', patientUuid: 'uuid2' }, + ], + isLoading: false, + isValidating: false, + error: null, + })); + render(); + + const searchInput = screen.getByPlaceholderText('Filter table'); + fireEvent.change(searchInput, { target: { value: 'John' } }); + + expect(screen.getByText('John Doe')).toBeInTheDocument(); + expect(screen.queryByText('Some One')).toBeNull(); + }); + + it('displays empty state when there are no active visits', () => { + mockActiveVisits.mockImplementationOnce(() => ({ + activeVisits: [], + isLoading: false, + isValidating: false, + error: null, + })); + + render(); + + expect(screen.getByText('There are no active visits to display for this location.')).toBeInTheDocument(); + }); + + it('should not display the table when the data is loading', () => { + mockActiveVisits.mockImplementationOnce(() => ({ + activeVisits: undefined, + isLoading: true, + isValidating: false, + error: null, + })); + + render(); + + const expectedColumnHeaders = [/Visit Time/, /ID Number/, /Name/, /Gender/, /Age/, /Visit Type/]; + expectedColumnHeaders.forEach((header) => { + expect(screen.queryByRole('columnheader', { name: new RegExp(header, 'i') })).not.toBeInTheDocument(); + }); + }); + + it('should display the Error when there is error', () => { + mockActiveVisits.mockImplementationOnce(() => ({ + activeVisits: undefined, + isLoading: false, + isValidating: false, + error: 'Error in fetching data', + })); + + render(); + + const expectedColumnHeaders = [/Visit Time/, /ID Number/, /Name/, /Gender/, /Age/, /Visit Type/]; + expectedColumnHeaders.forEach((header) => { + expect(screen.queryByRole('columnheader', { name: new RegExp(header, 'i') })).toBeInTheDocument(); + }); + }); + + it('should display the pagination when pagination is true', () => { + mockActiveVisits.mockImplementationOnce(() => ({ + activeVisits: [ + { id: '1', name: 'John Doe', visitType: 'Checkup' }, + { id: '2', name: 'Some One', visitType: 'Checkup' }, + ], + isLoading: false, + isValidating: false, + error: null, + })); + mockedUsePagination.mockImplementationOnce((data) => ({ + currentPage: 1, + goTo: () => {}, + results: data, + paginated: true, + })); + render(); + + expect(screen.getByText(/Next Page/i)).toBeInTheDocument(); + }); +}); diff --git a/packages/esm-appointments-app/src/appointments-calendar/appointments-calendar-view.component.tsx b/packages/esm-appointments-app/src/appointments-calendar/appointments-calendar-view.component.tsx index 43f9bbf4a..cf6ebfbad 100644 --- a/packages/esm-appointments-app/src/appointments-calendar/appointments-calendar-view.component.tsx +++ b/packages/esm-appointments-app/src/appointments-calendar/appointments-calendar-view.component.tsx @@ -6,22 +6,23 @@ import type { CalendarType } from '../types'; import AppointmentsHeader from '../appointments-header/appointments-header.component'; import CalendarHeader from './header/calendar-header.component'; import CalendarView from './calendar-view.component'; +import { useAppointmentDate } from '../helpers'; const AppointmentsCalendarView: React.FC = () => { const { t } = useTranslation(); const [calendarView, setCalendarView] = useState('monthly'); - const [currentDate, setCurrentDate] = useState(dayjs()); - const { calendarEvents } = useAppointmentsCalendar(currentDate.toISOString(), calendarView); + const { currentAppointmentDate, setCurrentAppointmentDate } = useAppointmentDate(); + const { calendarEvents } = useAppointmentsCalendar(dayjs(currentAppointmentDate).toISOString(), calendarView); return ( -
- +
+
); diff --git a/packages/esm-appointments-app/src/appointments-calendar/daily/daily-view-workload.test.tsx b/packages/esm-appointments-app/src/appointments-calendar/daily/daily-view-workload.test.tsx new file mode 100644 index 000000000..62e753ffa --- /dev/null +++ b/packages/esm-appointments-app/src/appointments-calendar/daily/daily-view-workload.test.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import dayjs from 'dayjs'; +import { navigate } from '@openmrs/esm-framework'; +import DailyWorkloadView from './daily-view-workload.component'; +import { spaBasePath } from '../../constants'; +import { CalendarType } from '../../types'; + +jest.mock('@openmrs/esm-framework', () => ({ + navigate: jest.fn(), + useLayoutType: jest.fn(), +})); + +describe('DailyWorkloadView Component', () => { + const mockData = { + type: 'daily' as CalendarType, + dateTime: dayjs('2023-08-18'), + currentDate: dayjs('2023-08-18'), + events: [ + { + appointmentDate: '2023-08-18', + service: [ + { serviceName: 'HIV', count: 2 }, + { serviceName: 'Lab testing', count: 3 }, + ], + }, + ], + }; + + it('renders properly when type is "daily"', () => { + render(); + + expect(screen.getByText('All Day')).toBeInTheDocument(); + expect(screen.getByText('HIV')).toBeInTheDocument(); + expect(screen.getByText('Lab testing')).toBeInTheDocument(); + }); + + it('navigates when a service area is clicked', () => { + render(); + + fireEvent.click(screen.getByText('HIV')); + + expect(navigate).toHaveBeenCalledWith({ + to: `${spaBasePath}/appointments/list/Fri, 18 Aug 2023 00:00:00 GMT/HIV`, + }); + }); + + it('calculates and displays the total count correctly', () => { + render(); + + expect(screen.getByText('Total')).toBeInTheDocument(); + expect(screen.getByText('5')).toBeInTheDocument(); + }); +}); diff --git a/packages/esm-appointments-app/src/appointments-calendar/monthly/monthly-header.module.tsx b/packages/esm-appointments-app/src/appointments-calendar/monthly/monthly-header.module.tsx index 631332ffb..2d4c88519 100644 --- a/packages/esm-appointments-app/src/appointments-calendar/monthly/monthly-header.module.tsx +++ b/packages/esm-appointments-app/src/appointments-calendar/monthly/monthly-header.module.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Dayjs } from 'dayjs'; +import dayjs, { Dayjs } from 'dayjs'; import styles from './monthly-header.module.scss'; import { Button } from '@carbon/react'; import { useTranslation } from 'react-i18next'; @@ -24,6 +24,7 @@ function MonthlyHeader({ setCurrentDate: (date: Dayjs) => void; }) { const { t } = useTranslation(); + return ( <>
diff --git a/packages/esm-appointments-app/src/appointments-calendar/patient-list/calendar-patient-list.component.tsx b/packages/esm-appointments-app/src/appointments-calendar/patient-list/calendar-patient-list.component.tsx index 4d120f6d0..435ea7c34 100644 --- a/packages/esm-appointments-app/src/appointments-calendar/patient-list/calendar-patient-list.component.tsx +++ b/packages/esm-appointments-app/src/appointments-calendar/patient-list/calendar-patient-list.component.tsx @@ -63,7 +63,7 @@ const CalendarPatientList: React.FC = () => { if (isLoading) { return ( <> - + ); } diff --git a/packages/esm-appointments-app/src/appointments-calendar/weekly/weekly-view-workload.test.tsx b/packages/esm-appointments-app/src/appointments-calendar/weekly/weekly-view-workload.test.tsx new file mode 100644 index 000000000..44b17aafb --- /dev/null +++ b/packages/esm-appointments-app/src/appointments-calendar/weekly/weekly-view-workload.test.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import dayjs from 'dayjs'; +import WeeklyWorkloadView from './weekly-view-workload.component'; +import { CalendarType } from '../../types'; +import { spaBasePath } from '../../constants'; +import { navigate } from '@openmrs/esm-framework'; + +jest.mock('@openmrs/esm-framework', () => ({ + navigate: jest.fn(), + useLayoutType: jest.fn(), +})); + +describe('WeeklyWorkloadView Component', () => { + const mockData = { + type: 'weekly' as CalendarType, + dateTime: dayjs('2023-08-17'), + currentDate: dayjs('2023-08-17'), + events: [ + { + appointmentDate: '2023-08-17', + service: [ + { serviceName: 'HIV', count: 2 }, + { serviceName: 'Lab testing', count: 3 }, + ], + }, + ], + index: 1, + }; + + it('renders properly when type is "weekly"', () => { + render(); + + expect(screen.getByText('All Day')).toBeInTheDocument(); + expect(screen.getByText('HIV')).toBeInTheDocument(); + expect(screen.getByText('Lab testing')).toBeInTheDocument(); + }); + + it('navigates when a service area is clicked', () => { + render(); + + fireEvent.click(screen.getByText('HIV')); + + expect(navigate).toHaveBeenCalledWith({ + to: `${spaBasePath}/appointments/list/Thu, 17 Aug 2023 00:00:00 GMT/HIV`, + }); + }); + + it('calculates and displays the total count correctly', () => { + render(); + + expect(screen.getByText('Total')).toBeInTheDocument(); + expect(screen.getByText('5')).toBeInTheDocument(); + }); +}); diff --git a/packages/esm-appointments-app/src/appointments-header/appointments-header.component.tsx b/packages/esm-appointments-app/src/appointments-header/appointments-header.component.tsx index b57acdeb9..1198d23cc 100644 --- a/packages/esm-appointments-app/src/appointments-header/appointments-header.component.tsx +++ b/packages/esm-appointments-app/src/appointments-header/appointments-header.component.tsx @@ -6,7 +6,7 @@ import AppointmentsIllustration from './appointments-illustration.component'; import styles from './appointments-header.scss'; import { DatePicker, DatePickerInput, Dropdown, Layer } from '@carbon/react'; import dayjs from 'dayjs'; -import { changeStartDate } from '../helpers'; +import { changeStartDate, useAppointmentDate } from '../helpers'; import { useAppointmentServices } from '../hooks/useAppointmentService'; interface AppointmentHeaderProps { @@ -18,11 +18,12 @@ const AppointmentsHeader: React.FC = ({ title, onChange const { t } = useTranslation(); const session = useSession(); const datePickerRef = useRef(null); - const [appointmentDate, setDateTime] = useState(new Date()); + const { currentAppointmentDate } = useAppointmentDate(); const location = session?.sessionLocation?.display; const { serviceTypes } = useAppointmentServices(); + return ( -
+
@@ -46,7 +47,7 @@ const AppointmentsHeader: React.FC = ({ title, onChange placeholder="DD-MMM-YYYY" labelText="" type="text" - value={dayjs(appointmentDate).format('DD MMM YYYY')} + value={dayjs(currentAppointmentDate).format('DD MMM YYYY')} />
diff --git a/packages/esm-appointments-app/src/appointments-metrics/appointments-metrics.component.tsx b/packages/esm-appointments-app/src/appointments-metrics/appointments-metrics.component.tsx index ecb071ab7..aca0f909d 100644 --- a/packages/esm-appointments-app/src/appointments-metrics/appointments-metrics.component.tsx +++ b/packages/esm-appointments-app/src/appointments-metrics/appointments-metrics.component.tsx @@ -15,8 +15,8 @@ const AppointmentsMetrics: React.FC<{ serviceUuid: string }> = ({ serviceUuid }) const { totalProviders, isLoading: allAppointmentsLoading } = useAllAppointmentsByDate(); const { totalScheduledAppointments } = useScheduledAppointment(serviceUuid); - const startDate = useAppointmentDate(); - const formattedStartDate = formatDate(parseDate(startDate), { mode: 'standard', time: false }); + const { currentAppointmentDate } = useAppointmentDate(); + const formattedStartDate = formatDate(parseDate(currentAppointmentDate), { mode: 'standard', time: false }); const { appointmentList: arrivedAppointments } = useAppointmentList('Honoured'); const { appointmentList: pendingAppointments } = useAppointmentList('Pending'); @@ -37,13 +37,13 @@ const AppointmentsMetrics: React.FC<{ serviceUuid: string }> = ({ serviceUuid }) return ( <> -
+
{ const { t } = useTranslation(); const [appointmentServiceType, setAppointmentServiceType] = useState(''); - const pathname = window.location.pathname; - - if (pathname.includes('calendar')) { - return ; - } - - if (pathname.includes('list')) { - return ; - } return ( <> - + diff --git a/packages/esm-appointments-app/src/appointments.test.tsx b/packages/esm-appointments-app/src/appointments.test.tsx new file mode 100644 index 000000000..0a8bac437 --- /dev/null +++ b/packages/esm-appointments-app/src/appointments.test.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import ClinicalAppointments from './appointments.component'; + +describe('ClinicalAppointments Component', () => { + it('renders AppointmentsCalendarListView when pathname includes "calendar"', () => { + // Mock window.location.pathname + Object.defineProperty(window, 'location', { + value: { + pathname: '/some-path/calendar', + }, + writable: true, + }); + + render(); + + expect(screen.getByTestId('appointments-calendar')).toBeInTheDocument(); + }); + + it('renders CalendarPatientList when pathname includes "list"', () => { + Object.defineProperty(window, 'location', { + value: { + pathname: '/some-path/list', + }, + writable: true, + }); + + render(); + + expect(screen.getByTestId('calendar-patient-list')).toBeInTheDocument(); + }); + + it('renders other components when pathname does not include "calendar" or "list"', () => { + Object.defineProperty(window, 'location', { + value: { + pathname: '/some-path/some-other-path', + }, + writable: true, + }); + + render(); + + expect(screen.getByTestId('appointments-header')).toBeInTheDocument(); + expect(screen.getByTestId('clinic-metrics')).toBeInTheDocument(); + expect(screen.getByTestId('appointment-list')).toBeInTheDocument(); + }); +}); diff --git a/packages/esm-appointments-app/src/appointments/appointment-tabs.component.tsx b/packages/esm-appointments-app/src/appointments/appointment-tabs.component.tsx index 5bb935da1..939329f7b 100644 --- a/packages/esm-appointments-app/src/appointments/appointment-tabs.component.tsx +++ b/packages/esm-appointments-app/src/appointments/appointment-tabs.component.tsx @@ -22,7 +22,7 @@ const AppointmentTabs: React.FC = ({ appointmentServiceTyp }; return ( -
+
{t('scheduled', 'Scheduled')} diff --git a/packages/esm-appointments-app/src/appointments/appointments-table.resource.ts b/packages/esm-appointments-app/src/appointments/appointments-table.resource.ts index a3fb9c00e..cd940f582 100644 --- a/packages/esm-appointments-app/src/appointments/appointments-table.resource.ts +++ b/packages/esm-appointments-app/src/appointments/appointments-table.resource.ts @@ -6,8 +6,8 @@ import { getAppointment, useAppointmentDate } from '../helpers'; import isEmpty from 'lodash-es/isEmpty'; export function useAppointments(status?: string, forDate?: string) { - const appointmentDate = useAppointmentDate(); - const startDate = forDate ? forDate : appointmentDate; + const { currentAppointmentDate } = useAppointmentDate(); + const startDate = forDate ? forDate : currentAppointmentDate; const apiUrl = `/ws/rest/v1/appointment/appointmentStatus?forDate=${startDate}&status=${status}`; const allAppointmentsUrl = `/ws/rest/v1/appointment/all?forDate=${startDate}`; diff --git a/packages/esm-appointments-app/src/appointments/common-components/location-select-option.component.tsx b/packages/esm-appointments-app/src/appointments/common-components/location-select-option.component.tsx new file mode 100644 index 000000000..59b482b3c --- /dev/null +++ b/packages/esm-appointments-app/src/appointments/common-components/location-select-option.component.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { SelectItem } from '@carbon/react'; + +interface SelectLocationProps { + selectedLocation: string; + defaultFacility: { + uuid: string; + display: string; + }; + locations?: Array; +} + +interface LocationOptions { + uuid?: string; + display?: string; +} + +const LocationSelectOption: React.FC = ({ selectedLocation, defaultFacility, locations }) => { + const { t } = useTranslation(); + if (!selectedLocation) { + return ; + } + + if (defaultFacility && Object.keys(defaultFacility).length !== 0) { + return ( + + {defaultFacility?.display} + + ); + } + + if (locations && locations.length > 0) { + return ( + <> + {locations.map((location) => ( + + {location.display} + + ))} + + ); + } + + return null; +}; + +export default LocationSelectOption; diff --git a/packages/esm-appointments-app/src/appointments/forms/cancel-form/cancel-appointment.component.tsx b/packages/esm-appointments-app/src/appointments/forms/cancel-form/cancel-appointment.component.tsx index 049a796cf..843a424f5 100644 --- a/packages/esm-appointments-app/src/appointments/forms/cancel-form/cancel-appointment.component.tsx +++ b/packages/esm-appointments-app/src/appointments/forms/cancel-form/cancel-appointment.component.tsx @@ -19,7 +19,7 @@ const CancelAppointment: React.FC = ({ appointment }) => const session = useSession(); const [selectedLocation, setSelectedLocation] = useState(appointment.location); const [reason, setReason] = useState(''); - const startDate = useAppointmentDate(); + const { currentAppointmentDate } = useAppointmentDate(); const [isSubmitting, setIsSubmitting] = useState(false); useEffect(() => { @@ -38,8 +38,8 @@ const CancelAppointment: React.FC = ({ appointment }) => description: t('appointmentNowVisible', 'It has been cancelled successfully'), title: t('appointmentCancelled', 'Appointment cancelled'), }); - mutate(`/ws/rest/v1/appointment/appointmentStatus?forDate=${startDate}&status=Scheduled`); - mutate(`/ws/rest/v1/appointment/appointmentStatus?forDate=${startDate}&status=Cancelled`); + mutate(`/ws/rest/v1/appointment/appointmentStatus?forDate=${currentAppointmentDate}&status=Scheduled`); + mutate(`/ws/rest/v1/appointment/appointmentStatus?forDate=${currentAppointmentDate}&status=Cancelled`); closeOverlay(); } else { showNotification({ diff --git a/packages/esm-appointments-app/src/appointments/forms/create-edit-form/appointments-form.component.tsx b/packages/esm-appointments-app/src/appointments/forms/create-edit-form/appointments-form.component.tsx index e457d5c19..77df4feea 100644 --- a/packages/esm-appointments-app/src/appointments/forms/create-edit-form/appointments-form.component.tsx +++ b/packages/esm-appointments-app/src/appointments/forms/create-edit-form/appointments-form.component.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import dayjs from 'dayjs'; import { @@ -32,6 +32,7 @@ import { showNotification, showToast, ConfigObject, + useSession, } from '@openmrs/esm-framework'; import first from 'lodash-es/first'; @@ -50,6 +51,8 @@ import { import { useInitialAppointmentFormValue, PatientAppointment } from '../useInitialFormValues'; import { useCalendarDistribution } from '../workload-helper'; import WorkloadCard from '../workload.component'; +import { useDefaultLoginLocation } from '../../../hooks/useDefaultLocation'; +import LocationSelectOption from '../../common-components/location-select-option.component'; interface AppointmentFormProps { appointment?: MappedAppointment; @@ -63,6 +66,7 @@ const AppointmentForm: React.FC = ({ appointment, patientU const [patientAppointment, setPatientAppointment] = useState(initialAppointmentFormValues); const { patient, isLoading } = usePatient(patientUuid ?? patientAppointment.patientUuid); const locations = useLocations(); + const sessionUser = useSession(); const { providers } = useProviders(); const { services } = useServices(); const [isSubmitting, setIsSubmitting] = useState(false); @@ -71,11 +75,22 @@ const AppointmentForm: React.FC = ({ appointment, patientU const calendarWorkload = useCalendarDistribution( patientAppointment.serviceUuid, selectedTab === 0 ? 'week' : 'month', + patientAppointment.visitDate, ); - const appointmentStartDate = useAppointmentDate(); + const { currentAppointmentDate } = useAppointmentDate(); + const [selectedLocation, setSelectedLocation] = useState(''); + const { defaultFacility, isLoading: loadingDefaultFacility } = useDefaultLoginLocation(); const appointmentService = services?.find(({ uuid }) => uuid === patientAppointment.serviceUuid); + useEffect(() => { + if (locations?.length && sessionUser) { + setSelectedLocation(sessionUser?.sessionLocation?.uuid); + } else if (!loadingDefaultFacility && defaultFacility) { + setSelectedLocation(defaultFacility?.uuid); + } + }, [locations, sessionUser, loadingDefaultFacility]); + const handleSubmit = async () => { const [hours, minutes] = convertTime12to24(patientAppointment.startDateTime, patientAppointment.timeFormat); const startDatetime = toAppointmentDateTime(patientAppointment.visitDate, hours, minutes); @@ -107,11 +122,11 @@ const AppointmentForm: React.FC = ({ appointment, patientU title: t('appointmentScheduled', 'Appointment scheduled'), }); setIsSubmitting(false); - mutate(`/ws/rest/v1/appointment/appointmentStatus?forDate=${appointmentStartDate}&status=Scheduled`); - mutate(`/ws/rest/v1/appointment/appointmentStatus?forDate=${appointmentStartDate}&status=CheckedIn`); - mutate(`/ws/rest/v1/appointment/all?forDate=${appointmentStartDate}`); - mutate(`/ws/rest/v1/appointment/appointmentStatus?status=Scheduled&forDate=${appointmentStartDate}`); - mutate(`/ws/rest/v1/appointment/appointmentStatus?status=Pending&forDate=${appointmentStartDate}`); + mutate(`/ws/rest/v1/appointment/appointmentStatus?forDate=${currentAppointmentDate}&status=Scheduled`); + mutate(`/ws/rest/v1/appointment/appointmentStatus?forDate=${currentAppointmentDate}&status=CheckedIn`); + mutate(`/ws/rest/v1/appointment/all?forDate=${currentAppointmentDate}`); + mutate(`/ws/rest/v1/appointment/appointmentStatus?status=Scheduled&forDate=${currentAppointmentDate}`); + mutate(`/ws/rest/v1/appointment/appointmentStatus?status=Pending&forDate=${currentAppointmentDate}`); closeOverlay(); } }, @@ -159,15 +174,14 @@ const AppointmentForm: React.FC = ({ appointment, patientU labelText={t('selectLocation', 'Select a location')} id="location" invalidText="Required" - value={patientAppointment.locationUuid} - className={styles.inputContainer} - onChange={(event) => setPatientAppointment({ ...patientAppointment, locationUuid: event.target.value })}> - {locations?.length > 0 && - locations.map((location) => ( - - {location.display} - - ))} + value={selectedLocation} + defaultSelected={selectedLocation} + onChange={(event) => setSelectedLocation(event.target.value)}> + - - , - ); - return screen.getByLabelText('Number (optional)') as HTMLInputElement; - }; - - it('exists', async () => { - const input = await setupInput(); - expect(input.type).toEqual('number'); - }); - - it('can input data', async () => { - const user = userEvent.setup(); - - const input = await setupInput(); - const expected = 1; - - await user.type(input, expected.toString()); - await user.tab(); - - expect(input.valueAsNumber).toEqual(expected); - }); -}); - describe('text input', () => { const setupInput = async () => { render( @@ -100,108 +70,3 @@ describe('text input', () => { expect(screen.getByLabelText('Text (optional)')).toBeInTheDocument(); }); }); - -describe.skip('telephone number input', () => { - const setupInput = async () => { - render( - -
- -
-
, - ); - return screen.getByLabelText('telephoneNumber') as HTMLInputElement; - }; - - it('exists', async () => { - const input = await setupInput(); - expect(input.type).toEqual('tel'); - }); - - it('can input data', async () => { - const user = userEvent.setup(); - - const input = await setupInput(); - const expected = '0800001066'; - - await user.type(input, expected); - await user.tab(); - - expect(input.value).toEqual(expected); - }); -}); - -describe.skip('date input', () => { - const setupInput = async () => { - render( - -
- -
-
, - ); - return screen.getByLabelText('date') as HTMLInputElement; - }; - - it('exists', async () => { - const input = await setupInput(); - expect(input.type).toEqual('date'); - }); - - it('can input data', async () => { - const user = userEvent.setup(); - - const input = await setupInput(); - const expected = '1990-09-10'; - - await user.type(input, expected); - await user.tab(); - - expect(input.value).toEqual(expected); - }); -}); - -describe.skip('checkbox input', () => { - const setupInput = async () => { - render( - -
- -
-
, - ); - return screen.getByLabelText('checkbox') as HTMLInputElement; - }; - - it('exists', async () => { - const input = await setupInput(); - expect(input.type).toEqual('checkbox'); - }); - - it('can input data', async () => { - const user = userEvent.setup(); - const input = await setupInput(); - - const expected = true; - - await user.click(input); - await user.tab(); - - expect(input.checked).toEqual(expected); - }); - - it('can update data', async () => { - const user = userEvent.setup(); - const input = await setupInput(); - - const expected = false; - - await user.click(input); - await user.tab(); - - await user.click(input); - await user.tab(); - - expect(input.checked).toEqual(expected); - }); -}); diff --git a/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts b/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts index 90cd720f3..4ad89972d 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts +++ b/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts @@ -1,4 +1,11 @@ -import { FetchResponse, getSynchronizationItems, openmrsFetch, useConfig, usePatient } from '@openmrs/esm-framework'; +import { + FetchResponse, + OpenmrsResource, + getSynchronizationItems, + openmrsFetch, + useConfig, + usePatient, +} from '@openmrs/esm-framework'; import camelCase from 'lodash-es/camelCase'; import { Dispatch, useEffect, useMemo, useState } from 'react'; import useSWR from 'swr'; @@ -239,14 +246,16 @@ export function useInitialPatientIdentifiers(patientUuid: string): { function useInitialEncounters(patientUuid: string, patientToEdit: fhir.Patient) { const { registrationObs } = useConfig() as RegistrationConfig; const { data, error, isLoading } = useSWR }>>( - patientToEdit - ? `/ws/rest/v1/encounter?patient=${patientUuid}&v=full&encounterType=${registrationObs.encounterTypeUuid}` + patientToEdit && registrationObs.encounterTypeUuid + ? `/ws/rest/v1/encounter?patient=${patientUuid}&v=custom:(encounterDatetime,obs:(concept:ref,value:ref))&encounterType=${registrationObs.encounterTypeUuid}` : null, openmrsFetch, ); const obs = data?.data.results.sort(latestFirstEncounter)?.at(0)?.obs; const encounters = obs - ?.map(({ concept, value }) => ({ [concept['uuid']]: value['uuid'] })) + ?.map(({ concept, value }) => ({ + [(concept as OpenmrsResource).uuid]: typeof value === 'object' ? value?.uuid : value, + })) .reduce((accu, curr) => Object.assign(accu, curr), {}); return { data: encounters, isLoading, error }; diff --git a/packages/esm-patient-registration-app/src/patient-registration/patient-registration.test.tsx b/packages/esm-patient-registration-app/src/patient-registration/patient-registration.test.tsx index ded64f6bf..4c82b8e59 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/patient-registration.test.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/patient-registration.test.tsx @@ -1,16 +1,17 @@ import React from 'react'; -import { BrowserRouter as Router } from 'react-router-dom'; -import { render, screen, fireEvent, waitFor, within } from '@testing-library/react'; +import { BrowserRouter as Router, useParams } from 'react-router-dom'; +import { render, screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { showToast, useConfig, usePatient } from '@openmrs/esm-framework'; -import FormManager from './form-manager'; +import { FormManager } from './form-manager'; import { saveEncounter, savePatient } from './patient-registration.resource'; -import { Encounter } from './patient-registration-types'; +import type { Encounter } from './patient-registration.types'; import { Resources, ResourcesContext } from '../offline.resources'; import { PatientRegistration } from './patient-registration.component'; import { RegistrationConfig } from '../config-schema'; import { mockedAddressTemplate } from './field/address/tests/mocks'; import { mockPatient } from '../../../../tools/test-helpers'; +import { OpenmrsDatePicker } from '@openmrs/esm-styleguide/src/public'; const mockedUseConfig = useConfig as jest.Mock; const mockedUsePatient = usePatient as jest.Mock; @@ -18,6 +19,8 @@ const mockedSaveEncounter = saveEncounter as jest.Mock; const mockedSavePatient = savePatient as jest.Mock; const mockedShowToast = showToast as jest.Mock; +jest.setTimeout(10000); + jest.mock('@openmrs/esm-framework', () => { const originalModule = jest.requireActual('@openmrs/esm-framework'); @@ -36,6 +39,7 @@ jest.mock('react-router-dom', () => ({ pathname: 'openmrs/spa/patient-registration', }), useHistory: () => [], + useParams: jest.fn().mockReturnValue({ patientUuid: undefined }), })); jest.mock('./patient-registration.resource', () => { @@ -54,6 +58,18 @@ jest.mock('@openmrs/esm-framework', () => { return { ...originalModule, validator: jest.fn(), + getLocale: jest.fn().mockReturnValue('en'), + OpenmrsDatePicker: (datePickerProps) => ( + + ), }; }); @@ -167,264 +183,272 @@ const fillRequiredFields = async () => { await user.type(familyNameInput, 'Gaihre'); await user.clear(dateOfBirthInput); await user.type(dateOfBirthInput, '02/08/1993'); - fireEvent.blur(dateOfBirthInput); - fireEvent.click(genderInput); + user.click(genderInput); }; -describe('patient registration', () => { - beforeEach(() => { - mockedUseConfig.mockReturnValue(mockOpenmrsConfig); - mockedSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true }); - mockedSaveEncounter.mockClear(); - mockedShowToast.mockClear(); - }); +describe('patient registration component', () => { + describe('when registering a new patient', () => { + beforeEach(() => { + mockedUseConfig.mockReturnValue(mockOpenmrsConfig); + mockedSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true }); + mockedSaveEncounter.mockClear(); + mockedShowToast.mockClear(); + jest.clearAllMocks(); + }); - it.only('renders without crashing', () => { - render( - - - - - , - , - ); - }); + it('renders without crashing', () => { + render( + + + + + , + , + ); + }); - it('has the expected sections', async () => { - render( - - - - - , - ); - await waitFor(() => expect(screen.getByLabelText(/Demographics Section/)).not.toBeNull()); - expect(screen.getByLabelText(/Contact Info Section/)).not.toBeNull(); - }); + it('has the expected sections', async () => { + render( + + + + + , + ); + await waitFor(() => expect(screen.getByLabelText(/Demographics Section/)).not.toBeNull()); + expect(screen.getByLabelText(/Contact Info Section/)).not.toBeNull(); + }); - it('saves the patient without extra info', async () => { - const user = userEvent.setup(); - - render( - - - - - , - ); - - await fillRequiredFields(); - await user.click(await screen.findByText('Register Patient')); - await waitFor(() => { - expect(mockedSavePatient).toHaveBeenCalledWith( - expect.anything(), - expect.objectContaining({ - identifiers: [], //TODO when the identifer story is finished: { identifier: '', identifierType: '05a29f94-c0ed-11e2-94be-8c13b969e334', location: '' } - // identifiers: [{ identifier: '', identifierType: '05a29f94-c0ed-11e2-94be-8c13b969e334', location: '' }], - person: { - addresses: [{}], - attributes: [], - birthdate: '1993-8-2', - birthdateEstimated: false, - gender: 'M', - names: [{ givenName: 'Paul', middleName: '', familyName: 'Gaihre', preferred: true, uuid: undefined }], - dead: false, - uuid: expect.anything(), - }, - uuid: expect.anything(), - }), - undefined, + it('saves the patient without extra info', async () => { + const user = userEvent.setup(); + + render( + + + + + , ); + + await fillRequiredFields(); + await user.click(await screen.findByText('Register Patient')); + await waitFor(() => { + expect(mockedSavePatient).toHaveBeenCalledWith( + expect.objectContaining({ + identifiers: [], //TODO when the identifer story is finished: { identifier: '', identifierType: '05a29f94-c0ed-11e2-94be-8c13b969e334', location: '' }, + person: { + addresses: expect.arrayContaining([expect.any(Object)]), + attributes: [], + birthdate: '1993-8-2', + birthdateEstimated: false, + gender: 'M', + names: [{ givenName: 'Paul', middleName: '', familyName: 'Gaihre', preferred: true, uuid: undefined }], + dead: false, + uuid: expect.anything(), + }, + uuid: expect.anything(), + }), + undefined, + ); + }); }); - }); - it('should not save the patient if validation fails', async () => { - const user = userEvent.setup(); + it('should not save the patient if validation fails', async () => { + const user = userEvent.setup(); - const mockedSavePatientForm = jest.fn(); - render( - - - - - , - ); + const mockedSavePatientForm = jest.fn(); + render( + + + + + , + ); - const givenNameInput = (await screen.findByLabelText('First Name')) as HTMLInputElement; + const givenNameInput = (await screen.findByLabelText('First Name')) as HTMLInputElement; - await user.type(givenNameInput, ''); - await user.click(screen.getByText('Register Patient')); + await user.type(givenNameInput, '5'); + await user.click(screen.getByText('Register Patient')); - expect(mockedSavePatientForm).not.toHaveBeenCalled(); - }); + expect(mockedSavePatientForm).not.toHaveBeenCalled(); + }); - it.skip('edits patient demographics', async () => { - const user = userEvent.setup(); + it('renders and saves registration obs', async () => { + const user = userEvent.setup(); - mockedSavePatient.mockResolvedValue({}); + mockedSaveEncounter.mockResolvedValue({}); + mockedUseConfig.mockReturnValue(configWithObs); - mockedUsePatient.mockReturnValueOnce({ - isLoading: false, - patient: mockPatient, - patientUuid: mockPatient.id, - error: null, + render( + + + + + , + ); + + await fillRequiredFields(); + const customSection = screen.getByLabelText('Custom Section'); + const weight = within(customSection).getByLabelText('Weight (kg) (optional)'); + await user.type(weight, '50'); + const complaint = within(customSection).getByLabelText('Chief Complaint (optional)'); + await user.type(complaint, 'sad'); + const nationality = within(customSection).getByLabelText('Nationality'); + await user.selectOptions(nationality, 'USA'); + + await user.click(screen.getByText('Register Patient')); + + await waitFor(() => expect(mockedSavePatient).toHaveBeenCalled()); + await waitFor(() => + expect(mockedSaveEncounter).toHaveBeenCalledWith( + expect.objectContaining>({ + encounterType: 'reg-enc-uuid', + patient: 'new-pt-uuid', + obs: [ + { concept: 'weight-uuid', value: 50 }, + { concept: 'chief-complaint-uuid', value: 'sad' }, + { concept: 'nationality-uuid', value: 'usa' }, + ], + }), + ), + ); }); - render( - - - - - , - ); - - const givenNameInput = screen.getByLabelText(/First Name/) as HTMLInputElement; - const familyNameInput = screen.getByLabelText(/Family Name/) as HTMLInputElement; - const middleNameInput = screen.getByLabelText(/Middle Name/) as HTMLInputElement; - const dateOfBirthInput = screen.getByLabelText('Date of Birth') as HTMLInputElement; - const address1 = screen.getByLabelText('Location.address1') as HTMLInputElement; - - // assert initial values - expect(givenNameInput.value).toBe('John'); - expect(familyNameInput.value).toBe('Wilson'); - expect(middleNameInput.value).toBeFalsy(); - expect(dateOfBirthInput.value).toBe('04/04/1972'); - - // do some edits - await user.clear(givenNameInput); - await user.clear(middleNameInput); - await user.clear(familyNameInput); - await user.type(givenNameInput, 'Eric'); - await user.type(middleNameInput, 'Johnson'); - await user.type(familyNameInput, 'Smith'); - await user.type(address1, 'Bom Jesus Street'); - await user.click(screen.getByText('Update Patient')); - - expect(mockedSavePatient).toHaveBeenCalledWith( - expect.anything(), - { - uuid: '8673ee4f-e2ab-4077-ba55-4980f408773e', - identifiers: [ - { - uuid: '1f0ad7a1-430f-4397-b571-59ea654a52db', - identifier: '100GEJ', - identifierType: 'e5af9a9c-ff9d-486d-900c-5fbf66a5ba3c', - preferred: true, - }, - { - uuid: '1f0ad7a1-430f-4397-b571-59ea654a52db', - identifier: '100732HE', - identifierType: '3ff0063c-dd45-4d98-8af4-0c094f26166c', - preferred: false, - }, - ], - person: { - addresses: [ - { - address1: 'Bom Jesus Street', - address2: '', - cityVillage: 'City0351', - country: 'Country0351', - postalCode: '60351', - stateProvince: 'State0351tested', - }, - ], - attributes: [], - birthdate: new Date('1972-04-04'), - birthdateEstimated: false, - gender: 'M', - names: [ - { - uuid: 'efdb246f-4142-4c12-a27a-9be60b9592e9', - givenName: 'Eric', - middleName: 'Johnson', - familyName: 'Smith', - preferred: true, - }, - ], - dead: false, - uuid: '8673ee4f-e2ab-4077-ba55-4980f408773e', - }, - }, - '8673ee4f-e2ab-4077-ba55-4980f408773e', - ); - }); + it('retries saving registration obs after a failed attempt', async () => { + const user = userEvent.setup(); - it('renders and saves registration obs', async () => { - const user = userEvent.setup(); - - mockedSaveEncounter.mockResolvedValue({}); - mockedUseConfig.mockReturnValue(configWithObs); - - render( - - - - - , - ); - - await fillRequiredFields(); - const customSection = screen.getByLabelText('Custom Section'); - const weight = within(customSection).getByLabelText('Weight (kg)'); - await user.type(weight, '50'); - const complaint = within(customSection).getByLabelText('Chief Complaint'); - await user.type(complaint, 'sad'); - const nationality = within(customSection).getByLabelText('Nationality'); - await user.selectOptions(nationality, 'USA'); - - await user.click(screen.getByText('Register Patient')); - - await waitFor(() => expect(mockedSavePatient).toHaveBeenCalled()); - await waitFor(() => - expect(mockedSaveEncounter).toHaveBeenCalledWith( - expect.anything(), - expect.objectContaining>({ - encounterType: 'reg-enc-uuid', - patient: 'new-pt-uuid', - obs: [ - { concept: 'weight-uuid', value: 50 }, - { concept: 'chief-complaint-uuid', value: 'sad' }, - { concept: 'nationality-uuid', value: 'usa' }, - ], - }), - ), - ); - }); + mockedUseConfig.mockReturnValue(configWithObs); + + render( + + + + + , + ); + + await fillRequiredFields(); + const customSection = screen.getByLabelText('Custom Section'); + const weight = within(customSection).getByLabelText('Weight (kg) (optional)'); + await user.type(weight, '-999'); + + mockedSaveEncounter.mockRejectedValue({ status: 400, responseBody: { error: { message: 'an error message' } } }); + + await user.click(screen.getByText('Register Patient')); + + await waitFor(() => expect(mockedSavePatient).toHaveBeenCalledTimes(1)); + await waitFor(() => expect(mockedSaveEncounter).toHaveBeenCalledTimes(1)); + await waitFor(() => + expect(mockedShowToast).toHaveBeenCalledWith(expect.objectContaining({ description: 'an error message' })), + ); - it('retries saving registration obs after a failed attempt', async () => { - const user = userEvent.setup(); + mockedSaveEncounter.mockResolvedValue({}); - mockedUseConfig.mockReturnValue(configWithObs); + await user.click(screen.getByText('Register Patient')); + await waitFor(() => expect(mockedSavePatient).toHaveBeenCalledTimes(2)); + await waitFor(() => expect(mockedSaveEncounter).toHaveBeenCalledTimes(2)); + await waitFor(() => expect(mockedShowToast).toHaveBeenCalledWith(expect.objectContaining({ kind: 'success' }))); + }); + }); - render( - - - - - , - ); + describe('when updating an existing patient details', () => { + beforeEach(() => { + mockedUseConfig.mockReturnValue(mockOpenmrsConfig); + mockedSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true }); + mockedSaveEncounter.mockClear(); + mockedShowToast.mockClear(); + jest.clearAllMocks(); + }); - await fillRequiredFields(); - const customSection = screen.getByLabelText('Custom Section'); - const weight = within(customSection).getByLabelText('Weight (kg)'); - await user.type(weight, '-999'); + fit('edits patient demographics', async () => { + const user = userEvent.setup(); - mockedSaveEncounter.mockRejectedValue({ status: 400, responseBody: { error: { message: 'an error message' } } }); + mockedSavePatient.mockResolvedValue({}); - await user.click(screen.getByText('Register Patient')); + const mockedUseParams = useParams as jest.Mock; - await waitFor(() => expect(mockedSavePatient).toHaveBeenCalledTimes(1)); - await waitFor(() => expect(mockedSaveEncounter).toHaveBeenCalledTimes(1)); - await waitFor(() => - expect(mockedShowToast).toHaveBeenCalledWith(expect.objectContaining({ description: 'an error message' })), - ); + mockedUseParams.mockReturnValue({ patientUuid: mockPatient.id }); - mockedSaveEncounter.mockResolvedValue({}); + mockedUsePatient.mockReturnValue({ + isLoading: false, + patient: mockPatient, + patientUuid: mockPatient.id, + error: null, + }); - await user.click(screen.getByText('Register Patient')); - await waitFor(() => expect(mockedSavePatient).toHaveBeenCalledTimes(1)); - await waitFor(() => expect(mockedSaveEncounter).toHaveBeenCalledTimes(2)); - await waitFor(() => expect(mockedShowToast).toHaveBeenCalledWith(expect.objectContaining({ kind: 'success' }))); + render( + + + + + , + ); + + const givenNameInput: HTMLInputElement = screen.getByLabelText(/First Name/); + const familyNameInput: HTMLInputElement = screen.getByLabelText(/Family Name/); + const middleNameInput: HTMLInputElement = screen.getByLabelText(/Middle Name/); + const dateOfBirthInput: HTMLInputElement = screen.getByLabelText('Date of Birth'); + const genderInput: HTMLInputElement = screen.getByLabelText(/Male/); + + // assert initial values + expect(givenNameInput.value).toBe('John'); + expect(familyNameInput.value).toBe('Wilson'); + expect(middleNameInput.value).toBeFalsy(); + expect(dateOfBirthInput.value).toBe('04/04/1972'); + expect(genderInput.value).toBe('Male'); + + // do some edits + await user.clear(givenNameInput); + await user.clear(middleNameInput); + await user.clear(familyNameInput); + await user.type(givenNameInput, 'Eric'); + await user.type(middleNameInput, 'Johnson'); + await user.type(familyNameInput, 'Smith'); + await user.click(screen.getByText('Update Patient')); + + await waitFor(() => + expect(mockedSavePatient).toHaveBeenCalledWith( + false, + { + '0': { + oldIdentificationNumber: '100732HE', + }, + '1': { + openMrsId: '100GEJ', + }, + addNameInLocalLanguage: undefined, + additionalFamilyName: '', + additionalGivenName: '', + additionalMiddleName: '', + address: {}, + birthdate: new Date('1972-04-04T00:00:00.000Z'), + birthdateEstimated: false, + deathCause: '', + deathDate: '', + familyName: 'Smith', + gender: 'Male', + givenName: 'Eric', + identifiers: {}, + isDead: false, + middleName: 'Johnson', + monthsEstimated: 0, + patientUuid: '8673ee4f-e2ab-4077-ba55-4980f408773e', + relationships: [], + telephoneNumber: '', + unidentifiedPatient: undefined, + yearsEstimated: 0, + }, + expect.anything(), + expect.anything(), + null, + undefined, + expect.anything(), + expect.anything(), + expect.anything(), + { patientSaved: false }, + expect.anything(), + ), + ); + }); }); }); diff --git a/packages/esm-patient-registration-app/src/patient-registration/patient-registration.types.tsx b/packages/esm-patient-registration-app/src/patient-registration/patient-registration.types.tsx index 4028a7480..decbd752e 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/patient-registration.types.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/patient-registration.types.tsx @@ -1,4 +1,4 @@ -import { Session } from '@openmrs/esm-framework'; +import { OpenmrsResource, Session } from '@openmrs/esm-framework'; import { RegistrationConfig } from '../config-schema'; import { SavePatientTransactionManager } from './form-manager'; @@ -128,8 +128,8 @@ export interface Encounter { }>; form: string; obs: Array<{ - concept: string; - value: string | number; + concept: string | OpenmrsResource; + value: string | number | OpenmrsResource; }>; } diff --git a/packages/esm-patient-registration-app/src/patient-registration/section/demographics/demographics-section.test.tsx b/packages/esm-patient-registration-app/src/patient-registration/section/demographics/demographics-section.test.tsx index c292a6207..41da2af1f 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/section/demographics/demographics-section.test.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/section/demographics/demographics-section.test.tsx @@ -5,6 +5,7 @@ import { initialFormValues } from '../../patient-registration.component'; import { DemographicsSection } from './demographics-section.component'; import { PatientRegistrationContext } from '../../patient-registration-context'; import { FormValues } from '../../patient-registration-types'; +import { OpenmrsDatePicker } from '@openmrs/esm-styleguide/src/public'; jest.mock('@openmrs/esm-framework', () => { const originalModule = jest.requireActual('@openmrs/esm-framework'); @@ -15,6 +16,18 @@ jest.mock('@openmrs/esm-framework', () => { useConfig: jest.fn().mockImplementation(() => ({ fieldConfigurations: { dateOfBirth: { useEstimatedDateOfBirth: { enabled: true, dayOfMonth: 0, month: 0 } } }, })), + getLocale: jest.fn().mockReturnValue('en'), + OpenmrsDatePicker: (datePickerProps) => ( + + ), }; }); diff --git a/packages/esm-patient-registration-app/src/patient-registration/section/patient-relationships/relationships-section.component.tsx b/packages/esm-patient-registration-app/src/patient-registration/section/patient-relationships/relationships-section.component.tsx index f26559672..233907733 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/section/patient-relationships/relationships-section.component.tsx @@ -27,82 +27,6 @@ interface RelationshipType { direction: string; } -export const RelationshipsSection = () => { - const { relationshipTypes } = useContext(ResourcesContext); - const [displayRelationshipTypes, setDisplayRelationshipTypes] = useState([]); - const { t } = useTranslation(); - - useEffect(() => { - if (relationshipTypes) { - const tmp: RelationshipType[] = []; - relationshipTypes.results.forEach((type) => { - const aIsToB = { - display: type.displayAIsToB, - uuid: type.uuid, - direction: 'aIsToB', - }; - const bIsToA = { - display: type.displayBIsToA, - uuid: type.uuid, - direction: 'bIsToA', - }; - aIsToB.display === bIsToA.display ? tmp.push(aIsToB) : tmp.push(aIsToB, bIsToA); - }); - setDisplayRelationshipTypes(tmp); - } - }, [relationshipTypes]); - - if (!relationshipTypes) { - return ( -
- -
- ); - } - - return ( -
- - {({ - push, - remove, - form: { - values: { relationships }, - }, - }) => ( -
- {relationships && relationships.length > 0 - ? relationships.map((relationship: RelationshipValue, index) => ( -
- -
- )) - : null} -
- -
-
- )} -
-
- ); -}; - interface RelationshipViewProps { relationship: RelationshipValue; index: number; @@ -211,8 +135,12 @@ const RelationshipView: React.FC = ({ value="placeholder-item" text={t('relationshipToPatient', 'Relationship to patient')} /> - {displayRelationshipTypes.map((type) => ( - + {displayRelationshipTypes.map((relationshipType, index) => ( + ))} @@ -230,3 +158,79 @@ const RelationshipView: React.FC = ({ /> ); }; + +export const RelationshipsSection = () => { + const { relationshipTypes } = useContext(ResourcesContext); + const [displayRelationshipTypes, setDisplayRelationshipTypes] = useState([]); + const { t } = useTranslation(); + + useEffect(() => { + if (relationshipTypes) { + const tmp: RelationshipType[] = []; + relationshipTypes.results.forEach((type) => { + const aIsToB = { + display: type.aIsToB, + uuid: type.uuid, + direction: 'aIsToB', + }; + const bIsToA = { + display: type.bIsToA, + uuid: type.uuid, + direction: 'bIsToA', + }; + aIsToB.display === bIsToA.display ? tmp.push(aIsToB) : tmp.push(aIsToB, bIsToA); + }); + setDisplayRelationshipTypes(tmp); + } + }, [relationshipTypes]); + + if (!relationshipTypes) { + return ( +
+ +
+ ); + } + + return ( +
+ + {({ + push, + remove, + form: { + values: { relationships }, + }, + }) => ( +
+ {relationships && relationships.length > 0 + ? relationships.map((relationship: RelationshipValue, index) => ( +
+ +
+ )) + : null} +
+ +
+
+ )} +
+
+ ); +}; diff --git a/packages/esm-patient-registration-app/src/patient-registration/section/patient-relationships/relationships-section.test.tsx b/packages/esm-patient-registration-app/src/patient-registration/section/patient-relationships/relationships-section.test.tsx new file mode 100644 index 000000000..825f30de5 --- /dev/null +++ b/packages/esm-patient-registration-app/src/patient-registration/section/patient-relationships/relationships-section.test.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import { Form, Formik } from 'formik'; +import { render, screen } from '@testing-library/react'; +import { PatientRegistrationContext } from '../../patient-registration-context'; +import { Resources, ResourcesContext } from '../../../offline.resources'; +import { RelationshipsSection } from './relationships-section.component'; + +jest.mock('../../patient-registration.resource', () => ({ + fetchPerson: jest.fn().mockResolvedValue({ + data: { + results: [ + { uuid: '42ae5ce0-d64b-11ea-9064-5adc43bbdd24', display: 'Person 1' }, + { uuid: '691eed12-c0f1-11e2-94be-8c13b969e334', display: 'Person 2' }, + ], + }, + }), +})); + +let mockResourcesContextValue = { + addressTemplate: [], + currentSession: { + authenticated: true, + sessionId: 'JSESSION', + currentProvider: { uuid: '45ce6c2e-dd5a-11e6-9d9c-0242ac150002', identifier: 'PRO-123' }, + }, + identifierTypes: [], + relationshipTypes: null, +} as Resources; + +describe('RelationshipsSection', () => { + it('renders a loader when relationshipTypes are not available', () => { + render( + + +
+ + +
+
, + ); + + expect(screen.getByLabelText(/loading relationships section/i)).toBeInTheDocument(); + expect(screen.getByRole(/progressbar/i)).toBeInTheDocument(); + expect(screen.queryByText(/add relationship/i)).not.toBeInTheDocument(); + }); + + it('renders relationships when relationshipTypes are available', () => { + const relationshipTypes = { + results: [ + { aIsToB: 'Mother', bIsToA: 'Child', uuid: '42ae5ce0-d64b-11ea-9064-5adc43bbdd34' }, + { aIsToB: 'Father', bIsToA: 'Child', uuid: '52ae5ce0-d64b-11ea-9064-5adc43bbdd24' }, + ], + }; + mockResourcesContextValue = { + ...mockResourcesContextValue, + relationshipTypes: relationshipTypes, + }; + + render( + + +
+ + + +
+
+
, + ); + + expect(screen.getByLabelText(/relationships section/i)).toBeInTheDocument(); + expect(screen.getByRole('heading', { name: /relationship/i })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /add relationship/i })).toBeInTheDocument(); + expect(screen.getByRole('searchbox', { name: /full name/i })).toBeInTheDocument(); + expect(screen.getByRole('option', { name: /mother/i })).toBeInTheDocument(); + expect(screen.getByRole('option', { name: /father/i })).toBeInTheDocument(); + expect(screen.getAllByRole('option', { name: /child/i }).length).toEqual(2); + }); +}); diff --git a/packages/esm-patient-registration-app/src/root.component.tsx b/packages/esm-patient-registration-app/src/root.component.tsx index 4e6c620fc..d510e5bb3 100644 --- a/packages/esm-patient-registration-app/src/root.component.tsx +++ b/packages/esm-patient-registration-app/src/root.component.tsx @@ -43,7 +43,7 @@ export default function Root() { identifierTypes, currentSession, }}> - + ({ + usePatientPhoto: jest.fn().mockReturnValue({ data: { imageSrc: 'test-image-src' } }), +})); + +jest.mock('geopattern', () => ({ + generate: jest.fn().mockReturnValue({ + toDataUri: jest.fn().mockReturnValue('https://example.com'), + }), +})); + +const patientUuid = mockPatient.uuid; +const patientName = mockPatient.name; + +describe('DisplayPatientPhoto Component', () => { + it('should render the component with the patient photo and size should not be small', () => { + render(); + + const avatarImage = screen.getByTitle(`${patientName}`); + + expect(avatarImage).toBeInTheDocument(); + expect(avatarImage).toHaveAttribute('style', expect.stringContaining('width: 80px; height: 80px')); + }); + + it('should render the component with the patient photo and size should be small i.e. 48px', () => { + render(); + + const avatarImage = screen.getByTitle(`${patientName}`); + + expect(avatarImage).toBeInTheDocument(); + expect(avatarImage).toHaveAttribute('style', expect.stringContaining('width: 48px; height: 48px')); + }); +}); diff --git a/packages/esm-patient-registration-app/src/widgets/edit-patient-details-button.test.tsx b/packages/esm-patient-registration-app/src/widgets/edit-patient-details-button.test.tsx new file mode 100644 index 000000000..ee2496b9c --- /dev/null +++ b/packages/esm-patient-registration-app/src/widgets/edit-patient-details-button.test.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import EditPatientDetailsButton from './edit-patient-details-button.component'; +import { navigate } from '@openmrs/esm-framework'; +import { mockPatient } from '../../../../__mocks__/appointments.mock'; + +describe('EditPatientDetailsButton', () => { + const patientUuid = mockPatient.uuid; + it('should navigate to the edit page when clicked', () => { + const mockNavigate = navigate as jest.Mock; + + jest.mock('@openmrs/esm-framework', () => { + const originalModule = jest.requireActual('@openmrs/esm-framework'); + return { + ...originalModule, + }; + }); + + render(); + + const button = screen.getByRole('menuitem'); + fireEvent.click(button); + + expect(mockNavigate).toHaveBeenCalledWith({ to: expect.stringContaining(`/patient/${patientUuid}/edit`) }); + }); + + it('should call the onTransition function when provided', () => { + const onTransitionMock = jest.fn(); + render(); + + const button = screen.getByRole('menuitem'); + fireEvent.click(button); + + expect(onTransitionMock).toHaveBeenCalled(); + }); +}); diff --git a/packages/esm-patient-search-app/__mocks__/address.mock.ts b/packages/esm-patient-search-app/__mocks__/address.mock.ts new file mode 100644 index 000000000..3787aa428 --- /dev/null +++ b/packages/esm-patient-search-app/__mocks__/address.mock.ts @@ -0,0 +1,8 @@ +export const mockAddress = { + postalCode: '12345', + address1: '123 Main St', + cityVillage: 'City', + stateProvince: 'State', + country: 'Country', + preferred: true, +}; diff --git a/packages/esm-patient-search-app/package.json b/packages/esm-patient-search-app/package.json index 20c72205c..98dcb956f 100644 --- a/packages/esm-patient-search-app/package.json +++ b/packages/esm-patient-search-app/package.json @@ -14,8 +14,8 @@ "build": "webpack --mode production", "analyze": "webpack --mode=production --env.analyze=true", "lint": "cross-env TIMING=1 eslint src --ext ts,tsx", - "test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests", - "test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js", + "test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests --color", + "test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js --color", "coverage": "yarn test --coverage", "typescript": "tsc", "extract-translations": "i18next 'src/**/*.component.tsx'" diff --git a/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-banner.component.tsx b/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-banner.component.tsx index 96cda1a8e..5e2cc7313 100644 --- a/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-banner.component.tsx +++ b/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-banner.component.tsx @@ -101,11 +101,11 @@ const PatientSearchResults = React.forwardRef{`${patient.name?.[0]?.given?.join(' ')} ${ patient.name?.[0]?.family }`} - + /> */}

{getGender(patient.gender)} · {age(patient.birthDate)} @@ -135,7 +135,7 @@ const PatientSearchResults = React.forwardRef { return ( -

+
({ + ...jest.requireActual('@openmrs/esm-framework'), +})); + +describe('Compact Patient Search Results', () => { + beforeEach(() => { + mockedUseConfig.mockReturnValue({ + defaultIdentifierTypes: ['identifier-type-1', 'identifier-type-2'], + search: { + patientResultUrl: '/patient/{{patientUuid}}/chart', + }, + }); + }); + const patients = [ + { + uuid: 'patient-1', + person: { + personName: { + givenName: 'John', + middleName: 'Doe', + familyName: 'Smith', + }, + gender: 'M', + birthdate: '1990-01-01', + }, + identifiers: [{ identifier: '123', identifierType: { uuid: 'identifier-type-1' } }], + }, + ]; + + it('should render patient search results', () => { + render(); + + expect(screen.getByText('John Doe Smith')).toBeInTheDocument(); + + expect(screen.getByRole('img')).toBeInTheDocument(); + }); + + // Fix this test later + // it('should call selectPatientAction when a patient is clicked', () => { + // const selectPatientActionMock = jest.fn(); + // render(); + + // fireEvent.click(screen.getByText('John Doe Smith')); + // expect(selectPatientActionMock).toBeCalledWith(patients[0]); + // }); +}); diff --git a/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-search.test.tsx b/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-search.test.tsx new file mode 100644 index 000000000..fe9afc564 --- /dev/null +++ b/packages/esm-patient-search-app/src/compact-patient-search/compact-patient-search.test.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import { render, fireEvent, waitFor, screen } from '@testing-library/react'; +import CompactPatientSearchComponent from './compact-patient-search.component'; + +jest.mock('@openmrs/esm-framework', () => ({ + ...jest.requireActual('@openmrs/esm-framework'), + useSession: jest.fn(() => ({ + ...jest.requireActual('@openmrs/esm-framework').useSession(), + sessionLocation: { + uuid: 'location-uuid', + }, + })), + useConfig: jest.fn(() => ({ + ...jest.requireActual('@openmrs/esm-framework').useConfig(), + search: { + patientResultUrl: '/patient/{{patientUuid}}/chart', + }, + })), +})); + +describe('CompactPatientSearchComponent', () => { + it('renders without crashing', () => { + render(); + expect(screen.getByRole('searchbox')).toBeInTheDocument(); + }); + + it('updates search term on input change', async () => { + render(); + const searchInput: HTMLInputElement = screen.getByRole('searchbox'); + + fireEvent.change(searchInput, { target: { value: 'John' } }); + + await waitFor(() => { + expect(searchInput.value).toBe('John'); + }); + }); + + it('clears search term on clear button click', async () => { + render(); + const searchInput: HTMLInputElement = screen.getByRole('searchbox'); + const clearButton = screen.getByRole('button', { name: 'Clear' }); + + fireEvent.change(searchInput, { target: { value: 'John' } }); + fireEvent.click(clearButton); + + await waitFor(() => { + expect(searchInput.value).toBe(''); + }); + }); + + it('renders search results when search term is not empty', async () => { + render(); + const searchInput = screen.getByRole('searchbox'); + + fireEvent.change(searchInput, { target: { value: 'John' } }); + + await waitFor(() => { + const searchResultsContainer = screen.getByTestId('floatingSearchResultsContainer'); + expect(searchResultsContainer).toBeInTheDocument(); + }); + }); + + it('renders recent patient search when search term is empty', async () => { + render(); + const searchInput = screen.getByRole('searchbox'); + + fireEvent.change(searchInput, { target: { value: '' } }); + + await waitFor(() => { + const searchResultsContainer = screen.getByTestId('floatingSearchResultsContainer'); + expect(searchResultsContainer).toBeInTheDocument(); + }); + }); +}); diff --git a/packages/esm-patient-search-app/src/compact-patient-search/patient-search.test.tsx b/packages/esm-patient-search-app/src/compact-patient-search/patient-search.test.tsx new file mode 100644 index 000000000..ea0c77f3b --- /dev/null +++ b/packages/esm-patient-search-app/src/compact-patient-search/patient-search.test.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import PatientSearch from './patient-search.component'; +import { useConfig } from '@openmrs/esm-framework'; + +const mockedUseConfig = useConfig as jest.Mock; + +jest.mock('@openmrs/esm-framework', () => ({ + ...jest.requireActual('@openmrs/esm-framework'), +})); + +describe('PatientSearch', () => { + const mockProps = { + query: 'John', + isLoading: false, + data: [ + { + uuid: '6baa7963-68ea-497e-b258-6fb82382bd07', + person: { + personName: { + givenName: 'John', + middleName: 'Doe', + familyName: 'Smith', + }, + gender: 'M', + birthdate: '1990-01-01', + }, + identifiers: [ + { + identifierType: { + uuid: '05a29f94-c0ed-11e2-94be-8c13b969e334', + display: 'National ID', + }, + identifier: '123456', + }, + ], + }, + ], + fetchError: false, + loadingNewData: false, + setPage: jest.fn(), + hasMore: false, + totalResults: 1, + }; + + beforeEach(() => { + mockedUseConfig.mockReturnValue({ + defaultIdentifierTypes: ['identifier-type-1', 'identifier-type-2'], + search: { + patientResultUrl: '/patient/{{patientUuid}}/chart', + }, + }); + }); + + it('should render search results correctly', () => { + render(); + + const resultsTextElement = screen.getByText('John Doe Smith'); + expect(resultsTextElement).toBeInTheDocument(); + }); + + it('should render loading state correctly', () => { + render(); + + const skeletonAvatar = screen.getAllByTestId('search-skeleton'); + expect(skeletonAvatar.length).toBeGreaterThan(0); + }); + + it('should render error state correctly', () => { + const error = new Error('Error'); + render(); + + const errorMessageElement = screen.getByText('Error'); + expect(errorMessageElement).toBeInTheDocument(); + }); +}); diff --git a/packages/esm-patient-search-app/src/compact-patient-search/recent-patient-search.test.tsx b/packages/esm-patient-search-app/src/compact-patient-search/recent-patient-search.test.tsx new file mode 100644 index 000000000..990ebf27a --- /dev/null +++ b/packages/esm-patient-search-app/src/compact-patient-search/recent-patient-search.test.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import RecentPatientSearch from './recent-patient-search.component'; +import { useConfig } from '@openmrs/esm-framework'; + +const mockedUseConfig = useConfig as jest.Mock; + +jest.mock('@openmrs/esm-framework', () => ({ + ...jest.requireActual('@openmrs/esm-framework'), +})); + +describe('RecentPatientSearch', () => { + beforeEach(() => { + mockedUseConfig.mockReturnValue({ + defaultIdentifierTypes: ['identifier-type-1', 'identifier-type-2'], + search: { + patientResultUrl: '/patient/{{patientUuid}}/chart', + }, + }); + }); + + it('should render loading skeleton when isLoading is true', () => { + render(); + + const resultText = screen.queryByText(/recent search result/i); + expect(resultText).not.toBeInTheDocument(); + }); + + it('should render error message when fetchError is true', () => { + const error = new Error('Error'); + render(); + const errorMessage = screen.getByText('Error'); + + expect(errorMessage).toBeInTheDocument(); + }); + + it('should render search results when searchResults contain data', () => { + const mockSearchResults = [ + { + uuid: 'patient-1', + identifiers: [{ identifier: '123', identifierType: { uuid: 'identifier-type-1' } }], + person: { + personName: { + givenName: 'John', + middleName: 'Doe', + }, + }, + }, + ]; + + render(); + const resultText = screen.getByText(/recent search result/i); + + expect(resultText).toBeInTheDocument(); + }); + + it('should render empty result message when searchResults is empty', () => { + render(); + const emptyResultText = screen.getByText(/no patient charts were found/i); + + expect(emptyResultText).toBeInTheDocument(); + }); +}); diff --git a/packages/esm-patient-search-app/src/patient-search-bar/patient-search-bar.test.tsx b/packages/esm-patient-search-app/src/patient-search-bar/patient-search-bar.test.tsx new file mode 100644 index 000000000..dcb8e22d5 --- /dev/null +++ b/packages/esm-patient-search-app/src/patient-search-bar/patient-search-bar.test.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import PatientSearchBar from './patient-search-bar.component'; + +describe('PatientSearchBar', () => { + it('should render', () => { + render(); + + const searchInput = screen.getByPlaceholderText('Search for a patient by name or identifier number'); + + expect(searchInput).toBeInTheDocument(); + }); + + it('displays initial search term', () => { + const initialSearchTerm = 'John Doe'; + render(); + + const searchInput: HTMLInputElement = screen.getByPlaceholderText( + 'Search for a patient by name or identifier number', + ); + + expect(searchInput.value).toBe(initialSearchTerm); + }); + + it('calls onChange callback on input change', () => { + const onChangeMock = jest.fn(); + render(); + + const searchInput = screen.getByPlaceholderText('Search for a patient by name or identifier number'); + + fireEvent.change(searchInput, { target: { value: 'New Value' } }); + + expect(onChangeMock).toHaveBeenCalledWith('New Value'); + }); + + it('calls onClear callback on clear button click', () => { + const onClearMock = jest.fn(); + render(); + + const clearButton = screen.getByRole('button', { name: 'Clear' }); + + fireEvent.click(clearButton); + + expect(onClearMock).toHaveBeenCalled(); + }); + + it('calls onSubmit callback on form submission', () => { + const onSubmitMock = jest.fn(); + render(); + + const searchInput = screen.getByPlaceholderText('Search for a patient by name or identifier number'); + const searchButton = screen.getByRole('button', { name: 'Search' }); + + fireEvent.change(searchInput, { target: { value: 'Search Term' } }); + fireEvent.click(searchButton); + + expect(onSubmitMock).toHaveBeenCalledWith('Search Term'); + }); +}); diff --git a/packages/esm-patient-search-app/src/patient-search-icon/index.tsx b/packages/esm-patient-search-app/src/patient-search-icon/index.tsx index 5794af617..70f9e4813 100644 --- a/packages/esm-patient-search-app/src/patient-search-icon/index.tsx +++ b/packages/esm-patient-search-app/src/patient-search-icon/index.tsx @@ -4,7 +4,7 @@ import PatientSearchLaunch from './patient-search-icon.component'; const PatientSearchIconWrapper = () => { return ( - + } /> diff --git a/packages/esm-patient-search-app/src/patient-search-icon/patient-search-icon.test.tsx b/packages/esm-patient-search-app/src/patient-search-icon/patient-search-icon.test.tsx new file mode 100644 index 000000000..0cba40cf4 --- /dev/null +++ b/packages/esm-patient-search-app/src/patient-search-icon/patient-search-icon.test.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import PatientSearchLaunch from './patient-search-icon.component'; +import { isDesktop } from '@openmrs/esm-framework'; + +const isDesktopMock = isDesktop as jest.Mock; + +jest.mock('@openmrs/esm-framework', () => ({ + ...jest.requireActual('@openmrs/esm-framework'), + isDesktop: jest.fn(), + useOnClickOutside: jest.fn(), +})); + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useSearchParams: jest.fn(() => [ + { + get: jest.fn(() => 'John'), + }, + ]), +})); + +describe('PatientSearchLaunch', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders without errors', () => { + render(); + expect(screen.getByRole('button', { name: 'Search Patient' })).toBeInTheDocument(); + }); + + it('toggles search input when search button is clicked', () => { + render(); + const searchButton = screen.getByTestId('searchPatientIcon'); + + fireEvent.click(searchButton); + const searchInput = screen.getByText('Search results'); + expect(searchInput).toBeInTheDocument(); + + fireEvent.click(searchButton); + expect(searchInput).not.toBeInTheDocument(); + }); + + it('displays search input in overlay on mobile', () => { + isDesktopMock.mockReturnValue(false); + render(); + const searchButton = screen.getByTestId('searchPatientIcon'); + + fireEvent.click(searchButton); + const overlay = screen.getByText('Search results'); + expect(overlay).toBeInTheDocument(); + }); +}); diff --git a/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/patient-banner.component.tsx b/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/patient-banner.component.tsx index 96b488f0d..b9eb75f9b 100644 --- a/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/patient-banner.component.tsx +++ b/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/patient-banner.component.tsx @@ -131,7 +131,7 @@ const PatientBanner: React.FC = ({ } - dropDownMenu={showDropdown}> + dropdownMenu={showDropdown}> { + beforeEach(() => { + mockUsePatientContactAttributes.mockReturnValue({ + isLoading: false, + contactAttributes: mockContactAttributes, + }); + + mockUseRelationships.mockReturnValue({ isLoading: false, data: mockRelationships }); + }); + it('renders address', () => { + render(); + + expect(screen.getByText('Address')).toBeInTheDocument(); + expect(screen.getByText('12345')).toBeInTheDocument(); + expect(screen.getByText('123 Main St')).toBeInTheDocument(); + expect(screen.getByText('City')).toBeInTheDocument(); + expect(screen.getByText('State')).toBeInTheDocument(); + expect(screen.getByText('Country')).toBeInTheDocument(); + }); + + it('renders contact attributes', async () => { + render(); + + expect(screen.getByText('Contact Details')).toBeInTheDocument(); + expect(screen.getByText('Email : test@example.com')); + }); + + it('renders relationships', async () => { + render(); + + expect(screen.getByText('Relationships')).toBeInTheDocument(); + expect(screen.getByText('John Doe')).toBeInTheDocument(); + }); + + it('renders skeleton when loading', async () => { + mockUsePatientContactAttributes.mockReturnValue({ + isLoading: true, + contactAttributes: [], + }); + + mockUseRelationships.mockReturnValue({ isLoading: true, data: [] }); + + render(); + + expect(screen.getByText('Contact Details')).toBeInTheDocument(); + expect(screen.queryByText('John Doe')).not.toBeInTheDocument(); + expect(screen.queryByText('Email : test@example.com')).not.toBeInTheDocument(); + }); +}); diff --git a/packages/esm-patient-search-app/src/patient-search-page/patient-banner/ui-components/overflow-menu.component.tsx b/packages/esm-patient-search-app/src/patient-search-page/patient-banner/ui-components/overflow-menu.component.tsx index 638806660..656fd0dff 100644 --- a/packages/esm-patient-search-app/src/patient-search-page/patient-banner/ui-components/overflow-menu.component.tsx +++ b/packages/esm-patient-search-app/src/patient-search-page/patient-banner/ui-components/overflow-menu.component.tsx @@ -1,16 +1,16 @@ -import { Button } from '@carbon/react'; import React, { useState, useCallback, useEffect, useRef } from 'react'; +import { Button } from '@carbon/react'; import styles from './overflow-menu.scss'; interface CustomOverflowMenuComponentProps { menuTitle: React.ReactNode; - dropDownMenu: boolean; + dropdownMenu: boolean; children?: React.ReactNode; isDeceased?: boolean; } const CustomOverflowMenuComponent: React.FC = ({ - dropDownMenu, + dropdownMenu, menuTitle, children, isDeceased, @@ -19,12 +19,12 @@ const CustomOverflowMenuComponent: React.FC = const wrapperRef = useRef(null); useEffect(() => { - if (!dropDownMenu) { + if (!dropdownMenu) { setShowMenu((state) => state); } setShowMenu(() => false); - }, [dropDownMenu]); + }, [dropdownMenu]); const toggleShowMenu = useCallback(() => setShowMenu((state) => !state), []); useEffect(() => { @@ -52,7 +52,7 @@ const CustomOverflowMenuComponent: React.FC = className={`cds--overflow-menu__trigger ${showMenu && 'cds--overflow-menu--open'} ${ isDeceased ? styles.deceased : '' } ${styles.overflowMenuButton}`} - aria-haspopup="true" + aria-haspopup aria-expanded={showMenu} id="custom-actions-overflow-menu-trigger" aria-controls="custom-actions-overflow-menu" diff --git a/packages/esm-patient-search-app/src/patient-search-page/patient-banner/ui-components/overflow-menu.test.tsx b/packages/esm-patient-search-app/src/patient-search-page/patient-banner/ui-components/overflow-menu.test.tsx new file mode 100644 index 000000000..6e1ed3c01 --- /dev/null +++ b/packages/esm-patient-search-app/src/patient-search-page/patient-banner/ui-components/overflow-menu.test.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import CustomOverflowMenuComponent from './overflow-menu.component'; + +describe('CustomOverflowMenuComponent', () => { + it('should render', () => { + render(Option 1} />); + expect(screen.getByRole('button', { name: 'Test Menu' })).toBeInTheDocument(); + }); + + it('should toggle menu on trigger button click', () => { + render( + +
  • Option 1
  • +
  • Option 2
  • +
    , + ); + + const triggerButton = screen.getByRole('button', { name: /menu/i }); + + fireEvent.click(triggerButton); + expect(triggerButton.getAttribute('aria-expanded')).toBe('true'); + + fireEvent.click(triggerButton); + expect(triggerButton.getAttribute('aria-expanded')).toBe('false'); + }); +}); diff --git a/packages/esm-patient-search-app/src/patient-search-page/patient-search-page.test.tsx b/packages/esm-patient-search-app/src/patient-search-page/patient-search-page.test.tsx new file mode 100644 index 000000000..a359ac205 --- /dev/null +++ b/packages/esm-patient-search-app/src/patient-search-page/patient-search-page.test.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { isDesktop } from '@openmrs/esm-framework'; +import PatientSearchPageComponent from './patient-search-page.component'; + +const mockIsDesktop = isDesktop as jest.Mock; + +jest.mock('@openmrs/esm-framework', () => ({ + ...jest.requireActual('@openmrs/esm-framework'), + isDesktop: jest.fn(), +})); + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: jest.fn(() => ({ + page: 1, + })), + useLocation: jest.fn(), + useSearchParams: jest.fn(() => [ + { + get: jest.fn(() => 'John'), + }, + ]), +})); + +describe('PatientSearchPageComponent', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should render advanced search component on desktop layout', () => { + mockIsDesktop.mockReturnValue(true); + render(); + + const applyBtn = screen.getByRole('button', { name: 'Apply' }); + const resetBtn = screen.getByRole('button', { name: /Reset/i }); + + expect(applyBtn).toBeInTheDocument(); + expect(resetBtn).toBeInTheDocument(); + }); + + it('should render patient search overlay on tablet layout', () => { + mockIsDesktop.mockReturnValue(false); + + render(); + + const searchBtn = screen.getByRole('button', { name: 'Search' }); + expect(searchBtn).toBeInTheDocument(); + }); +}); diff --git a/packages/esm-patient-search-app/src/root.component.tsx b/packages/esm-patient-search-app/src/root.component.tsx index 43e4c31ab..a1e75056e 100644 --- a/packages/esm-patient-search-app/src/root.component.tsx +++ b/packages/esm-patient-search-app/src/root.component.tsx @@ -4,9 +4,9 @@ import PatientSearchPageComponent from './patient-search-page/patient-search-pag const PatientSearchRootComponent: React.FC = () => { return ( - + - } /> + } /> ); diff --git a/packages/esm-patient-search-app/src/routes.json b/packages/esm-patient-search-app/src/routes.json index f79651220..851460d2e 100644 --- a/packages/esm-patient-search-app/src/routes.json +++ b/packages/esm-patient-search-app/src/routes.json @@ -3,24 +3,30 @@ "backendDependencies": { "webservices.rest": "^2.2.0" }, - "pages": [{ - "component": "root", - "route": "search" - }], - "extensions": [{ - "name": "patient-search-icon", - "component": "patientSearchIcon", - "slot": "top-nav-actions-slot", - "order": 0 - }, { - "name": "patient-search-button", - "component": "patientSearchButton", - "slot": "patient-search-button-slot", - "offline": true - }, { - "name": "patient-search-bar", - "component": "patientSearchBar", - "slot": "patient-search-bar-slot", - "offline": true - }] -} \ No newline at end of file + "pages": [ + { + "component": "root", + "route": "search" + } + ], + "extensions": [ + { + "name": "patient-search-icon", + "component": "patientSearchIcon", + "slot": "top-nav-actions-slot", + "order": 0 + }, + { + "name": "patient-search-button", + "component": "patientSearchButton", + "slot": "patient-search-button-slot", + "offline": true + }, + { + "name": "patient-search-bar", + "component": "patientSearchBar", + "slot": "patient-search-bar-slot", + "offline": true + } + ] +} diff --git a/packages/esm-patient-search-app/src/ui-components/pagination/pagination.test.tsx b/packages/esm-patient-search-app/src/ui-components/pagination/pagination.test.tsx new file mode 100644 index 000000000..bcb0ec5ac --- /dev/null +++ b/packages/esm-patient-search-app/src/ui-components/pagination/pagination.test.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; +import Pagination from './pagination.component'; + +describe('Pagination', () => { + it('should render correctly with page numbers', () => { + render( {}} hasMore={true} />); + + for (let i = 1; i <= 5; i++) { + const pageButtons = screen.getByRole('button', { name: `${i}` }); + expect(pageButtons).toBeInTheDocument(); + } + }); + + it('should disable previous button on first page', () => { + render( {}} hasMore={true} />); + const previousButton = screen.getByLabelText(/previous page/i); + expect(previousButton).toBeDisabled(); + }); + + it('should disable next button on last page when hasMore is false', () => { + render( {}} hasMore={false} />); + const nextButton = screen.getByLabelText(/next page/i); + expect(nextButton).toBeDisabled(); + }); + + it('should increment the page when next button is clicked', () => { + const setCurrentPageMock = jest.fn(); + render(); + + const nextButton = screen.getByLabelText(/next page/i); + fireEvent.click(nextButton); + expect(setCurrentPageMock).toHaveBeenCalledWith(2); + }); + + it('should decrement the page when previous button is clicked', () => { + const setCurrentPageMock = jest.fn(); + render(); + + const previousButton = screen.getByLabelText(/previous page/i); + fireEvent.click(previousButton); + expect(setCurrentPageMock).toHaveBeenCalledWith(2); + }); + + it('should call setCurrentPage when page button is clicked', () => { + const setCurrentPageMock = jest.fn(); + render(); + + const pageButton = screen.getByRole('button', { name: '4' }); + fireEvent.click(pageButton); + expect(setCurrentPageMock).toHaveBeenCalledWith(4); + }); + + it('should render empty component when totalPages is 1', () => { + render( {}} hasMore={true} />); + const pagination = screen.queryByRole('button', { name: 'next page' }); + expect(pagination).not.toBeInTheDocument(); + }); +}); diff --git a/packages/esm-outpatient-app/README.md b/packages/esm-service-queues-app/README.md similarity index 96% rename from packages/esm-outpatient-app/README.md rename to packages/esm-service-queues-app/README.md index c9426366a..f4053bb63 100644 --- a/packages/esm-outpatient-app/README.md +++ b/packages/esm-service-queues-app/README.md @@ -9,7 +9,7 @@ The `Service Queues` app is a frontend module that enables users to track a pati The key component of the service queue app is the `Active Visits` table. It displays a tabular overview of the active visits ongoing in a facility and the wait time of patients. Users can add patients to the service queue by starting visits for them. They can also view information from the current active visits as well as the previous visit on each queue entry by clicking the table extension slot. Users can also change the priority and status of an entry in the queue from the UI, effectively moving a patient from one point in the queue to another. In order to indicate that a patient is currently attending service, click on the bell icon. In order to edit an entry, click the pencil icon. Amend the following concepts in the configuration schema to get started using the module: -- `priorityConceptSetUuid` - concept UUID for `prioritity`. +- `priorityConceptSetUuid` - concept UUID for `priority`. - `defaultPriorityConceptUuid` - concept UUID for `not urgent`. - `serviceConceptSetUuid` - concept UUID for `service`. - `statusConceptSetUuid` - concept UUID for `status`. diff --git a/packages/esm-outpatient-app/__mocks__/active-visits.mock.ts b/packages/esm-service-queues-app/__mocks__/active-visits.mock.ts similarity index 100% rename from packages/esm-outpatient-app/__mocks__/active-visits.mock.ts rename to packages/esm-service-queues-app/__mocks__/active-visits.mock.ts diff --git a/packages/esm-outpatient-app/__mocks__/appointments-data.mock.ts b/packages/esm-service-queues-app/__mocks__/appointments-data.mock.ts similarity index 100% rename from packages/esm-outpatient-app/__mocks__/appointments-data.mock.ts rename to packages/esm-service-queues-app/__mocks__/appointments-data.mock.ts diff --git a/packages/esm-outpatient-app/__mocks__/search.mock.ts b/packages/esm-service-queues-app/__mocks__/search.mock.ts similarity index 100% rename from packages/esm-outpatient-app/__mocks__/search.mock.ts rename to packages/esm-service-queues-app/__mocks__/search.mock.ts diff --git a/packages/esm-outpatient-app/__mocks__/visits.mock.ts b/packages/esm-service-queues-app/__mocks__/visits.mock.ts similarity index 100% rename from packages/esm-outpatient-app/__mocks__/visits.mock.ts rename to packages/esm-service-queues-app/__mocks__/visits.mock.ts diff --git a/packages/esm-outpatient-app/jest.config.js b/packages/esm-service-queues-app/jest.config.js similarity index 100% rename from packages/esm-outpatient-app/jest.config.js rename to packages/esm-service-queues-app/jest.config.js diff --git a/packages/esm-outpatient-app/package.json b/packages/esm-service-queues-app/package.json similarity index 90% rename from packages/esm-outpatient-app/package.json rename to packages/esm-service-queues-app/package.json index d2b742a6f..411ba31a0 100644 --- a/packages/esm-outpatient-app/package.json +++ b/packages/esm-service-queues-app/package.json @@ -1,8 +1,8 @@ { - "name": "@openmrs/esm-outpatient-app", + "name": "@openmrs/esm-service-queues-app", "version": "5.0.0", "description": "Outpatient front-end module for the OpenMRS SPA", - "browser": "dist/openmrs-esm-outpatient-app.js", + "browser": "dist/openmrs-esm-service-queues-app.js", "main": "src/index.ts", "source": true, "license": "MPL-2.0", @@ -14,8 +14,8 @@ "build": "webpack --mode production", "analyze": "webpack --mode=production --env.analyze=true", "lint": "cross-env TIMING=1 eslint src --ext ts,tsx", - "test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests", - "test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js", + "test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests --color", + "test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js --color", "coverage": "yarn test --coverage", "typescript": "tsc", "extract-translations": "i18next 'src/**/*.component.tsx'" diff --git a/packages/esm-outpatient-app/src/active-visits/active-visits-tab.component.tsx b/packages/esm-service-queues-app/src/active-visits/active-visits-tab.component.tsx similarity index 97% rename from packages/esm-outpatient-app/src/active-visits/active-visits-tab.component.tsx rename to packages/esm-service-queues-app/src/active-visits/active-visits-tab.component.tsx index 4970d39f0..16acfa678 100644 --- a/packages/esm-outpatient-app/src/active-visits/active-visits-tab.component.tsx +++ b/packages/esm-service-queues-app/src/active-visits/active-visits-tab.component.tsx @@ -18,7 +18,7 @@ function ActiveVisitsTabs() { const [overlayHeader, setOverlayTitle] = useState(''); return ( -
    +
    ({ + ...jest.requireActual('@openmrs/esm-framework'), + useConfig: jest.fn(() => ({ + concepts: { + visitQueueNumberAttributeUuid: 'c61ce16f-272a-41e7-9924-4c555d0932c5', + }, + })), +})); + +describe('ActiveVisitsTabs', () => { + it('renders the component', () => { + const { container } = render(); + expect(container).toBeInTheDocument(); + }); +}); diff --git a/packages/esm-outpatient-app/src/active-visits/active-visits-table.component.tsx b/packages/esm-service-queues-app/src/active-visits/active-visits-table.component.tsx similarity index 93% rename from packages/esm-outpatient-app/src/active-visits/active-visits-table.component.tsx rename to packages/esm-service-queues-app/src/active-visits/active-visits-table.component.tsx index 06b7673c6..6f91d846f 100644 --- a/packages/esm-outpatient-app/src/active-visits/active-visits-table.component.tsx +++ b/packages/esm-service-queues-app/src/active-visits/active-visits-table.component.tsx @@ -161,11 +161,16 @@ function ActiveVisitsTable() { }, { id: 3, + header: t('queueComingFrom', 'Coming from'), + key: 'queueComingFrom', + }, + { + id: 4, header: t('status', 'Status'), key: 'status', }, { - id: 4, + id: 5, header: t('waitTime', 'Wait time'), key: 'waitTime', }, @@ -213,6 +218,9 @@ function ActiveVisitsTable() { ), }, + queueComingFrom: { + content: {entry?.queueComingFrom}, + }, status: { content: ( @@ -324,9 +332,10 @@ function ActiveVisitsTable() { rows={tableRows} size="xs" useZebraStyles> - {({ rows, headers, getHeaderProps, getTableProps, getRowProps, onInputChange }) => ( + {({ rows, headers, getHeaderProps, getTableProps, getRowProps, getToolbarProps, onInputChange }) => (
    @@ -498,25 +507,27 @@ function ActiveVisitsTable() { ) : null}
    -

    {t('noPatientsToDisplay', 'No patients to display')}

    - , - size: 'sm', - }, - selectPatientAction: (selectedPatientUuid) => { - setShowOverlay(true); - setView(SearchTypes.SCHEDULED_VISITS); - setViewState({ selectedPatientUuid }); - setOverlayTitle(t('addPatientWithAppointmentToQueue', 'Add patient with appointment to queue')); - }, - }} - /> +
    +

    {t('noPatientsToDisplay', 'No patients to display')}

    + , + size: 'sm', + }, + selectPatientAction: (selectedPatientUuid) => { + setShowOverlay(true); + setView(SearchTypes.SCHEDULED_VISITS); + setViewState({ selectedPatientUuid }); + setOverlayTitle(t('addPatientWithAppointmentToQueue', 'Add patient with appointment to queue')); + }, + }} + /> +
    {showOverlay && ( diff --git a/packages/esm-outpatient-app/src/active-visits/active-visits-table.resource.ts b/packages/esm-service-queues-app/src/active-visits/active-visits-table.resource.ts similarity index 98% rename from packages/esm-outpatient-app/src/active-visits/active-visits-table.resource.ts rename to packages/esm-service-queues-app/src/active-visits/active-visits-table.resource.ts index 3829b9a14..22e6e6138 100644 --- a/packages/esm-outpatient-app/src/active-visits/active-visits-table.resource.ts +++ b/packages/esm-service-queues-app/src/active-visits/active-visits-table.resource.ts @@ -71,6 +71,9 @@ export interface VisitQueueEntry { uuid: string; visit: Visit; sortWeight: number; + queueComingFrom: { + name: string; + }; } export interface MappedVisitQueueEntry { @@ -99,6 +102,7 @@ export interface MappedVisitQueueEntry { sortWeight: number; visitQueueNumber: string; identifiers: Array; + queueComingFrom: string; } interface UseVisitQueueEntries { @@ -244,16 +248,17 @@ export function useVisitQueueEntries(currServiceName: string, locationUuid: stri (e) => e.attributeType.uuid === visitQueueNumberAttributeUuid, )?.value, identifiers: visitQueueEntry.queueEntry.patient?.identifiers, + queueComingFrom: visitQueueEntry.queueEntry?.queueComingFrom?.name, }); let mappedVisitQueueEntries; if (!currServiceName || currServiceName == t('all', 'All')) { + mappedVisitQueueEntries = data?.data?.results?.map(mapVisitQueueEntryProperties); + } else { mappedVisitQueueEntries = data?.data?.results ?.map(mapVisitQueueEntryProperties) - .filter((data) => dayjs(data.visitStartDateTime).isToday()); - } else { - mappedVisitQueueEntries = data?.data?.results?.map(mapVisitQueueEntryProperties); + .filter((data) => data.service == currServiceName); } return { @@ -310,6 +315,7 @@ export async function updateQueueEntry( }, startedAt: toDateObjectStrict(toOmrsIsoString(new Date())), sortWeight: sortWeight, + queueComingFrom: previousQueueUuid, }, }, }); diff --git a/packages/esm-outpatient-app/src/active-visits/active-visits-table.scss b/packages/esm-service-queues-app/src/active-visits/active-visits-table.scss similarity index 98% rename from packages/esm-outpatient-app/src/active-visits/active-visits-table.scss rename to packages/esm-service-queues-app/src/active-visits/active-visits-table.scss index ef03b8da7..e902d5565 100644 --- a/packages/esm-outpatient-app/src/active-visits/active-visits-table.scss +++ b/packages/esm-service-queues-app/src/active-visits/active-visits-table.scss @@ -130,6 +130,7 @@ @include type.type-style('heading-compact-02'); color: $text-02; margin-bottom: 0.5rem; + align-self: center; } .helper { @@ -181,6 +182,7 @@ .tileContent { display: flex; flex-direction: column; + justify-content: center; align-items: center; } diff --git a/packages/esm-outpatient-app/src/active-visits/active-visits-table.test.tsx b/packages/esm-service-queues-app/src/active-visits/active-visits-table.test.tsx similarity index 100% rename from packages/esm-outpatient-app/src/active-visits/active-visits-table.test.tsx rename to packages/esm-service-queues-app/src/active-visits/active-visits-table.test.tsx diff --git a/packages/esm-outpatient-app/src/active-visits/change-status-dialog.component.tsx b/packages/esm-service-queues-app/src/active-visits/change-status-dialog.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/active-visits/change-status-dialog.component.tsx rename to packages/esm-service-queues-app/src/active-visits/change-status-dialog.component.tsx diff --git a/packages/esm-outpatient-app/src/active-visits/change-status-dialog.scss b/packages/esm-service-queues-app/src/active-visits/change-status-dialog.scss similarity index 100% rename from packages/esm-outpatient-app/src/active-visits/change-status-dialog.scss rename to packages/esm-service-queues-app/src/active-visits/change-status-dialog.scss diff --git a/packages/esm-outpatient-app/src/active-visits/change-status-dialog.test.tsx b/packages/esm-service-queues-app/src/active-visits/change-status-dialog.test.tsx similarity index 100% rename from packages/esm-outpatient-app/src/active-visits/change-status-dialog.test.tsx rename to packages/esm-service-queues-app/src/active-visits/change-status-dialog.test.tsx diff --git a/packages/esm-outpatient-app/src/add-patient-toqueue/add-patient-toqueue-dialog.component.tsx b/packages/esm-service-queues-app/src/add-patient-toqueue/add-patient-toqueue-dialog.component.tsx similarity index 97% rename from packages/esm-outpatient-app/src/add-patient-toqueue/add-patient-toqueue-dialog.component.tsx rename to packages/esm-service-queues-app/src/add-patient-toqueue/add-patient-toqueue-dialog.component.tsx index 03b32f1ce..d7bd40b5c 100644 --- a/packages/esm-outpatient-app/src/add-patient-toqueue/add-patient-toqueue-dialog.component.tsx +++ b/packages/esm-service-queues-app/src/add-patient-toqueue/add-patient-toqueue-dialog.component.tsx @@ -21,7 +21,7 @@ import { useVisitQueueEntries, } from '../active-visits/active-visits-table.resource'; import styles from './add-patient-toqueue-dialog.scss'; -import { ActiveVisit } from '../visits-missing-inqueue/visits-missing-inqueue.resource'; +import { ActiveVisit, useMissingQueueEntries } from '../visits-missing-inqueue/visits-missing-inqueue.resource'; import { useQueueLocations } from '../patient-search/hooks/useQueueLocations'; interface AddVisitToQueueDialogProps { @@ -48,6 +48,7 @@ const AddVisitToQueue: React.FC = ({ visitDetails, c const config = useConfig() as ConfigObject; const { mutate } = useVisitQueueEntries('', selectedQueueLocation); const [priority, setPriority] = useState(config.concepts.defaultPriorityConceptUuid); + const { mutateQueueEntries } = useMissingQueueEntries(); const addVisitToQueue = useCallback(() => { if (!queueUuid) { @@ -86,6 +87,7 @@ const AddVisitToQueue: React.FC = ({ visitDetails, c }); closeModal(); mutate(); + mutateQueueEntries(); } }, (error) => { diff --git a/packages/esm-outpatient-app/src/add-patient-toqueue/add-patient-toqueue-dialog.scss b/packages/esm-service-queues-app/src/add-patient-toqueue/add-patient-toqueue-dialog.scss similarity index 100% rename from packages/esm-outpatient-app/src/add-patient-toqueue/add-patient-toqueue-dialog.scss rename to packages/esm-service-queues-app/src/add-patient-toqueue/add-patient-toqueue-dialog.scss diff --git a/packages/esm-outpatient-app/src/add-provider-queue-room/add-provider-queue-room.component.tsx b/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/add-provider-queue-room/add-provider-queue-room.component.tsx rename to packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.component.tsx diff --git a/packages/esm-outpatient-app/src/add-provider-queue-room/add-provider-queue-room.resource.tsx b/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.resource.tsx similarity index 100% rename from packages/esm-outpatient-app/src/add-provider-queue-room/add-provider-queue-room.resource.tsx rename to packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.resource.tsx diff --git a/packages/esm-outpatient-app/src/add-provider-queue-room/add-provider-queue-room.scss b/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.scss similarity index 100% rename from packages/esm-outpatient-app/src/add-provider-queue-room/add-provider-queue-room.scss rename to packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.scss diff --git a/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.test.tsx b/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.test.tsx new file mode 100644 index 000000000..38856df9d --- /dev/null +++ b/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.test.tsx @@ -0,0 +1,98 @@ +import React from 'react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import AddProviderQueueRoom from './add-provider-queue-room.component'; +import { showToast } from '@openmrs/esm-framework'; + +jest.mock('@openmrs/esm-framework', () => ({ + ...jest.requireActual('@openmrs/esm-framework'), + useCurrentProvider: jest.fn(() => ({ + currentProvider: { uuid: 'provider-uuid-1' }, + })), + showToast: jest.fn(), + showNotification: jest.fn(), +})); +jest.mock('./add-provider-queue-room.resource', () => ({ + useProvidersQueueRoom: jest.fn(() => ({ + providerRoom: [ + { + queueRoom: { uuid: 'c187d78b-5c54-49bf-a0f8-b7fb6034d36d' }, + uuid: '6b3e233d-2b44-40ca-b0c8-c5a57a8c51b6', + }, + ], + isLoading: false, + mutate: jest.fn(), + })), + updateProviderToQueueRoom: jest.fn().mockResolvedValue({ status: 200 }), + addProviderToQueueRoom: jest.fn(), + useQueueRooms: jest.fn(() => ({ + rooms: [ + { uuid: '6b3e233d-2b44-40ca-b0c8-c5a57a8c51b6', display: 'Room 1' }, + { uuid: 'e7786ac0-ab62-11ec-b909-0242ac120002', display: 'Room 2' }, + ], + })), +})); + +jest.mock('../active-visits/active-visits-table.resource', () => ({ + useServices: jest.fn(() => ({ + services: [ + { uuid: 'e7786ac0-ab62-11ec-b909-0242ac120002', display: 'Service 1' }, + { uuid: 'e7786ac0-ab62-11ec-b909-0242ac120032', display: 'Service 2' }, + ], + })), +})); + +jest.mock('../patient-search/hooks/useQueueLocations', () => ({ + useQueueLocations: jest.fn(() => ({ + queueLocations: [ + { id: '1GHI12', name: 'Location 1' }, + { id: '1GHI45', name: 'Location 2' }, + ], + })), +})); + +const providerUuid = 'cc75ad73-c24b-499c-8db9-a7ef4fc0b36d'; + +describe('AddProviderQueueRoom', () => { + it('renders the form fields', () => { + render(); + + expect(screen.getByText('Select a room')).toBeInTheDocument(); + expect(screen.getByLabelText('Retain location')).toBeInTheDocument(); + expect(screen.getByText('Cancel')).toBeInTheDocument(); + expect(screen.getByText('Save')).toBeInTheDocument(); + }); + + it('updates queue room selection', () => { + render(); + + const selectQueueRoom: HTMLInputElement = screen.getByRole('combobox'); + fireEvent.change(selectQueueRoom, { target: { value: 'room-uuid-1' } }); + + expect(selectQueueRoom.value).toBe('6b3e233d-2b44-40ca-b0c8-c5a57a8c51b6'); + }); + + it('should update the retain location checkbox', () => { + render(); + + const retainLocationCheckbox: HTMLInputElement = screen.getByRole('checkbox'); + fireEvent.click(retainLocationCheckbox); + + expect(retainLocationCheckbox.checked).toBe(true); + }); + + it('should submit the form and add provider to queue room when all fields are filled', async () => { + const mockCloseModal = jest.fn(); + render(); + + const queueRoomSelect = screen.getByRole('combobox'); + const submitButton = screen.getByText('Save'); + + fireEvent.change(queueRoomSelect, { target: { value: '6b3e233d-2b44-40ca-b0c8-c5a57a8c51b6' } }); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(mockCloseModal).toHaveBeenCalled(); + expect(showToast).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/esm-outpatient-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.component.tsx b/packages/esm-service-queues-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.component.tsx rename to packages/esm-service-queues-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.component.tsx diff --git a/packages/esm-outpatient-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.resource.tsx b/packages/esm-service-queues-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.resource.tsx similarity index 100% rename from packages/esm-outpatient-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.resource.tsx rename to packages/esm-service-queues-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.resource.tsx diff --git a/packages/esm-outpatient-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.scss b/packages/esm-service-queues-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.scss similarity index 100% rename from packages/esm-outpatient-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.scss rename to packages/esm-service-queues-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.scss diff --git a/packages/esm-service-queues-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.test.tsx b/packages/esm-service-queues-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.test.tsx new file mode 100644 index 000000000..0282c5512 --- /dev/null +++ b/packages/esm-service-queues-app/src/clear-queue-entries-dialog/clear-queue-entries-dialog.test.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import ClearQueueEntriesDialog from './clear-queue-entries-dialog.component'; +import { batchClearQueueEntries } from './clear-queue-entries-dialog.resource'; + +const mockBatchClearQueueEntries = batchClearQueueEntries as jest.Mock; + +jest.mock('@openmrs/esm-framework', () => ({ + ...jest.requireActual('@openmrs/esm-framework'), + showToast: jest.fn(), +})); + +jest.mock('./clear-queue-entries-dialog.resource'); + +jest.mock('../active-visits/active-visits-table.resource', () => ({ + useVisitQueueEntries: () => ({ + mutate: jest.fn(), + }), +})); + +describe('ClearQueueEntriesDialog Component', () => { + const visitQueueEntriesMock = []; + + it('renders the component with warning message', () => { + render( {}} />); + + expect(screen.getByRole('heading', { name: 'Service queue' })).toBeInTheDocument(); + expect(screen.getByText('Clear all queue entries?')).toBeInTheDocument(); + expect(screen.getByText('Cancel')).toBeInTheDocument(); + expect(screen.getByText('Clear queue')).toBeInTheDocument(); + }); + + it('should close modal when clicked on cancel', async () => { + const closeModalMock = jest.fn(); + mockBatchClearQueueEntries.mockImplementationOnce(() => Promise.resolve()); + render(); + + fireEvent.click(screen.getByText('Cancel')); + + expect(closeModalMock).toHaveBeenCalled(); + }); +}); diff --git a/packages/esm-outpatient-app/src/clear-queue-entries-dialog/clear-queue-entries.component.tsx b/packages/esm-service-queues-app/src/clear-queue-entries-dialog/clear-queue-entries.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/clear-queue-entries-dialog/clear-queue-entries.component.tsx rename to packages/esm-service-queues-app/src/clear-queue-entries-dialog/clear-queue-entries.component.tsx diff --git a/packages/esm-outpatient-app/src/config-schema.ts b/packages/esm-service-queues-app/src/config-schema.ts similarity index 92% rename from packages/esm-outpatient-app/src/config-schema.ts rename to packages/esm-service-queues-app/src/config-schema.ts index e2f489541..282003b54 100644 --- a/packages/esm-outpatient-app/src/config-schema.ts +++ b/packages/esm-service-queues-app/src/config-schema.ts @@ -11,12 +11,12 @@ export const configSchema = { defaultPriorityConceptUuid: { _type: Type.ConceptUuid, _description: 'The UUID of the default priority for the queues eg Not urgent.', - _default: '9e123c90-76ac-4eaa-8d40-35577781eb46', + _default: 'f4620bfa-3625-4883-bd3f-84c2cce14470', }, emergencyPriorityConceptUuid: { _type: Type.ConceptUuid, _description: 'The UUID of the priority with the highest sort weight for the queues eg Emergency.', - _default: '5c2d5f8c-5efb-46d0-8e28-9e707ab7523c', + _default: '04f6f7e0-e3cb-4e13-a133-4479f759574e', }, serviceConceptSetUuid: { _type: Type.ConceptUuid, @@ -29,17 +29,12 @@ export const configSchema = { defaultStatusConceptUuid: { _type: Type.ConceptUuid, _description: 'The UUID of the default status for the queues eg Waiting.', - _default: '136203AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + _default: '51ae5e4d-b72b-4912-bf31-a17efb690aeb', }, defaultTransitionStatus: { _type: Type.ConceptUuid, _description: 'The UUID of the default status for attending a service in the queues eg In Service.', - _default: '167408AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - }, - visitQueueNumberAttributeUuid: { - _type: Type.ConceptUuid, - _description: 'The UUID of the visit attribute that contains the visit queue number.', - _default: 'c61ce16f-272a-41e7-9924-4c555d0932c5', + _default: 'ca7494ae-437f-4fd0-8aae-b88b9a2ba47d', }, systolicBloodPressureUuid: { _type: Type.ConceptUuid, @@ -93,6 +88,11 @@ export const configSchema = { 'The Uuids of person attribute-type that captures contact information `e.g Next of kin contact details`', _default: [], }, + visitQueueNumberAttributeUuid: { + _type: Type.UUID, + _description: 'The UUID of the visit attribute that contains the visit queue number.', + _default: 'c61ce16f-272a-41e7-9924-4c555d0932c5', + }, vitals: vitalsConfigSchema, biometrics: biometricsConfigSchema, showQueueTableTab: { diff --git a/packages/esm-outpatient-app/src/constants.ts b/packages/esm-service-queues-app/src/constants.ts similarity index 100% rename from packages/esm-outpatient-app/src/constants.ts rename to packages/esm-service-queues-app/src/constants.ts diff --git a/packages/esm-outpatient-app/src/createDashboardLink.tsx b/packages/esm-service-queues-app/src/createDashboardLink.tsx similarity index 100% rename from packages/esm-outpatient-app/src/createDashboardLink.tsx rename to packages/esm-service-queues-app/src/createDashboardLink.tsx diff --git a/packages/esm-outpatient-app/src/current-visit/current-visit-summary.component.tsx b/packages/esm-service-queues-app/src/current-visit/current-visit-summary.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/current-visit/current-visit-summary.component.tsx rename to packages/esm-service-queues-app/src/current-visit/current-visit-summary.component.tsx diff --git a/packages/esm-service-queues-app/src/current-visit/current-visit-summary.test.tsx b/packages/esm-service-queues-app/src/current-visit/current-visit-summary.test.tsx new file mode 100644 index 000000000..2d5725998 --- /dev/null +++ b/packages/esm-service-queues-app/src/current-visit/current-visit-summary.test.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { render, screen, waitFor } from '@testing-library/react'; +import CurrentVisit from './current-visit-summary.component'; +import { useVisit } from './current-visit.resource'; +import { mockPastVisit } from '../../__mocks__/visits.mock'; + +const useVisitMock = useVisit as jest.Mock; + +jest.mock('./current-visit.resource', () => ({ + useVisit: jest.fn(() => ({ + visit: { + visitType: { display: 'Visit Type' }, + encounters: [], + }, + isError: false, + isLoading: false, + })), +})); + +const patientUuid = mockPastVisit.data.results[0].patient.uuid; +const visitUuid = mockPastVisit.data.results[0].uuid; + +describe('CurrentVisit', () => { + it('renders visit details correctly', async () => { + render(); + + await waitFor(() => { + expect(screen.queryByRole('progressbar')).toBeNull(); + expect(screen.getByText('Visit Type')).toBeInTheDocument(); + expect(screen.getByText('Scheduled for today')).toBeInTheDocument(); + expect(screen.getByText('On time')).toBeInTheDocument(); + }); + }); + it('should render skeleton when loading', async () => { + useVisitMock.mockImplementationOnce(() => ({ + visit: undefined, + isError: false, + isLoading: true, + })); + + render(); + + await waitFor(() => { + expect(screen.getByRole('progressbar')).toBeInTheDocument(); + }); + }); +}); diff --git a/packages/esm-outpatient-app/src/current-visit/current-visit.resource.ts b/packages/esm-service-queues-app/src/current-visit/current-visit.resource.ts similarity index 100% rename from packages/esm-outpatient-app/src/current-visit/current-visit.resource.ts rename to packages/esm-service-queues-app/src/current-visit/current-visit.resource.ts diff --git a/packages/esm-outpatient-app/src/current-visit/current-visit.scss b/packages/esm-service-queues-app/src/current-visit/current-visit.scss similarity index 100% rename from packages/esm-outpatient-app/src/current-visit/current-visit.scss rename to packages/esm-service-queues-app/src/current-visit/current-visit.scss diff --git a/packages/esm-outpatient-app/src/current-visit/hooks/useVitalsConceptMetadata.tsx b/packages/esm-service-queues-app/src/current-visit/hooks/useVitalsConceptMetadata.tsx similarity index 100% rename from packages/esm-outpatient-app/src/current-visit/hooks/useVitalsConceptMetadata.tsx rename to packages/esm-service-queues-app/src/current-visit/hooks/useVitalsConceptMetadata.tsx diff --git a/packages/esm-outpatient-app/src/current-visit/visit-details/biometrics-config-schema.ts b/packages/esm-service-queues-app/src/current-visit/visit-details/biometrics-config-schema.ts similarity index 100% rename from packages/esm-outpatient-app/src/current-visit/visit-details/biometrics-config-schema.ts rename to packages/esm-service-queues-app/src/current-visit/visit-details/biometrics-config-schema.ts diff --git a/packages/esm-outpatient-app/src/current-visit/visit-details/current-visit-details.component.tsx b/packages/esm-service-queues-app/src/current-visit/visit-details/current-visit-details.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/current-visit/visit-details/current-visit-details.component.tsx rename to packages/esm-service-queues-app/src/current-visit/visit-details/current-visit-details.component.tsx diff --git a/packages/esm-outpatient-app/src/current-visit/visit-details/triage-note.component.tsx b/packages/esm-service-queues-app/src/current-visit/visit-details/triage-note.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/current-visit/visit-details/triage-note.component.tsx rename to packages/esm-service-queues-app/src/current-visit/visit-details/triage-note.component.tsx diff --git a/packages/esm-outpatient-app/src/current-visit/visit-details/triage-note.scss b/packages/esm-service-queues-app/src/current-visit/visit-details/triage-note.scss similarity index 100% rename from packages/esm-outpatient-app/src/current-visit/visit-details/triage-note.scss rename to packages/esm-service-queues-app/src/current-visit/visit-details/triage-note.scss diff --git a/packages/esm-outpatient-app/src/current-visit/visit-details/vitals-config-schema.ts b/packages/esm-service-queues-app/src/current-visit/visit-details/vitals-config-schema.ts similarity index 100% rename from packages/esm-outpatient-app/src/current-visit/visit-details/vitals-config-schema.ts rename to packages/esm-service-queues-app/src/current-visit/visit-details/vitals-config-schema.ts diff --git a/packages/esm-outpatient-app/src/current-visit/visit-details/vitals.component.tsx b/packages/esm-service-queues-app/src/current-visit/visit-details/vitals.component.tsx similarity index 99% rename from packages/esm-outpatient-app/src/current-visit/visit-details/vitals.component.tsx rename to packages/esm-service-queues-app/src/current-visit/visit-details/vitals.component.tsx index 2d2944061..1b6b610ed 100644 --- a/packages/esm-outpatient-app/src/current-visit/visit-details/vitals.component.tsx +++ b/packages/esm-service-queues-app/src/current-visit/visit-details/vitals.component.tsx @@ -86,8 +86,6 @@ const Vitals: React.FC = ({ vitals, patientUuid, visitType

    -
    -

    {t('sp02', 'Sp02')}

    @@ -97,6 +95,8 @@ const Vitals: React.FC = ({ vitals, patientUuid, visitType

    {conceptUnits.get(config.concepts.oxygenSaturationUuid) ?? ''}

    +
    +

    {t('rRate', 'R. Rate')}

    @@ -106,8 +106,6 @@ const Vitals: React.FC = ({ vitals, patientUuid, visitType

    {conceptUnits.get(config.concepts.respiratoryRateUuid) ?? ''}

    -
    -

    {t('height', 'Height')}

    @@ -115,6 +113,13 @@ const Vitals: React.FC = ({ vitals, patientUuid, visitType

    {conceptUnits.get(config.concepts.heightUuid) ?? ''}

    + +

    {t('weight', 'Weight')}

    +
    +

    {vitalsToDisplay.weight ? vitalsToDisplay.weight : '--'}

    +

    {conceptUnits.get(config.concepts.weightUuid) ?? ''}

    +
    +

    {t('bmi', 'Bmi')}

    @@ -125,13 +130,6 @@ const Vitals: React.FC = ({ vitals, patientUuid, visitType

    {config.biometrics['bmiUnit']}

    - -

    {t('weight', 'Weight')}

    -
    -

    {vitalsToDisplay.weight ? vitalsToDisplay.weight : '--'}

    -

    {conceptUnits.get(config.concepts.weightUuid) ?? ''}

    -
    -

    {vitalsToDisplay.provider?.name ? {vitalsToDisplay.provider.name} : null} ·{' '} diff --git a/packages/esm-outpatient-app/src/dashboard.meta.ts b/packages/esm-service-queues-app/src/dashboard.meta.ts similarity index 100% rename from packages/esm-outpatient-app/src/dashboard.meta.ts rename to packages/esm-service-queues-app/src/dashboard.meta.ts diff --git a/packages/esm-outpatient-app/src/declarations.d.ts b/packages/esm-service-queues-app/src/declarations.d.ts similarity index 100% rename from packages/esm-outpatient-app/src/declarations.d.ts rename to packages/esm-service-queues-app/src/declarations.d.ts diff --git a/packages/esm-outpatient-app/src/helpers/functions.ts b/packages/esm-service-queues-app/src/helpers/functions.ts similarity index 100% rename from packages/esm-outpatient-app/src/helpers/functions.ts rename to packages/esm-service-queues-app/src/helpers/functions.ts diff --git a/packages/esm-outpatient-app/src/helpers/helpers.ts b/packages/esm-service-queues-app/src/helpers/helpers.ts similarity index 100% rename from packages/esm-outpatient-app/src/helpers/helpers.ts rename to packages/esm-service-queues-app/src/helpers/helpers.ts diff --git a/packages/esm-outpatient-app/src/helpers/time-helpers.ts b/packages/esm-service-queues-app/src/helpers/time-helpers.ts similarity index 100% rename from packages/esm-outpatient-app/src/helpers/time-helpers.ts rename to packages/esm-service-queues-app/src/helpers/time-helpers.ts diff --git a/packages/esm-service-queues-app/src/home.component.tsx b/packages/esm-service-queues-app/src/home.component.tsx new file mode 100644 index 000000000..0be18f6d5 --- /dev/null +++ b/packages/esm-service-queues-app/src/home.component.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { ConfigObject, useConfig } from '@openmrs/esm-framework'; +import ActiveVisitsTable from './active-visits/active-visits-table.component'; +import ActiveVisitsTabs from './active-visits/active-visits-tab.component'; +import ClinicMetrics from './patient-queue-metrics/clinic-metrics.component'; +import PatientQueueHeader from './patient-queue-header/patient-queue-header.component'; + +const Home: React.FC = () => { + const config = useConfig(); + const useQueueTableTabs: boolean = config.showQueueTableTab; + + return ( + <> + + + {useQueueTableTabs ? : } + + ); +}; + +export default Home; diff --git a/packages/esm-service-queues-app/src/home.test.tsx b/packages/esm-service-queues-app/src/home.test.tsx new file mode 100644 index 000000000..d6c676b63 --- /dev/null +++ b/packages/esm-service-queues-app/src/home.test.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import Home from './home.component'; +import { useConfig } from '@openmrs/esm-framework'; + +const mockedUseConfig = useConfig as jest.Mock; + +jest.mock('@openmrs/esm-framework', () => ({ + ...jest.requireActual('@openmrs/esm-framework'), + useConfig: jest.fn(() => ({ + showQueueTableTab: true, + concepts: { + visitQueueNumberAttributeUuid: 'c61ce16f-272a-41e7-9924-4c555d0932c5', + }, + })), +})); + +describe('Home Component', () => { + it('renders PatientQueueHeader, ClinicMetrics, and ActiveVisitsTabs when activeTicketScreen is not "screen"', () => { + // Mock window.location.pathname + const originalLocation = window.location; + delete window.location; + window.location = { pathname: '/some-path' }; + + render(); + + // Assert that the expected components are rendered + expect(screen.getByTestId('patient-queue-header')).toBeInTheDocument(); + expect(screen.getByTestId('clinic-metrics')).toBeInTheDocument(); + expect(screen.getByTestId('active-visits-tabs')).toBeInTheDocument(); + + // Clean up the mock + window.location = originalLocation; + }); + + it('renders QueueScreen when activeTicketScreen is "screen"', () => { + const originalLocation = window.location; + delete window.location; + window.location = { pathname: '/some-path/screen' }; + + render(); + expect(screen.getByRole('progressbar')).toBeInTheDocument(); + + window.location = originalLocation; + }); + + it('renders ActiveVisitsTable when showQueueTableTab is false', () => { + mockedUseConfig.mockImplementationOnce(() => ({ + showQueueTableTab: false, + concepts: { + visitQueueNumberAttributeUuid: 'c61ce16f-272a-41e7-9924-4c555d0932c5', + }, + })); + render(); + + expect(screen.getByRole('progressbar')).toBeInTheDocument(); + }); +}); diff --git a/packages/esm-outpatient-app/src/index.ts b/packages/esm-service-queues-app/src/index.ts similarity index 95% rename from packages/esm-outpatient-app/src/index.ts rename to packages/esm-service-queues-app/src/index.ts index ef367cd4c..1aab10649 100644 --- a/packages/esm-outpatient-app/src/index.ts +++ b/packages/esm-service-queues-app/src/index.ts @@ -5,13 +5,15 @@ import { dashboardMeta } from './dashboard.meta'; export const importTranslation = require.context('../translations', false, /.json$/, 'lazy'); -const moduleName = '@openmrs/esm-outpatient-app'; +const moduleName = '@openmrs/esm-service-queues-app'; const options = { featureName: 'outpatient', moduleName, }; +export const root = getAsyncLifecycle(() => import('./root.component'), options); + export const appointmentsList = getAsyncLifecycle( () => import('./queue-patient-linelists/scheduled-appointments-table.component'), options, diff --git a/packages/esm-outpatient-app/src/overlay.component.tsx b/packages/esm-service-queues-app/src/overlay.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/overlay.component.tsx rename to packages/esm-service-queues-app/src/overlay.component.tsx diff --git a/packages/esm-outpatient-app/src/overlay.scss b/packages/esm-service-queues-app/src/overlay.scss similarity index 100% rename from packages/esm-outpatient-app/src/overlay.scss rename to packages/esm-service-queues-app/src/overlay.scss diff --git a/packages/esm-service-queues-app/src/overlay.test.tsx b/packages/esm-service-queues-app/src/overlay.test.tsx new file mode 100644 index 000000000..ea35b6440 --- /dev/null +++ b/packages/esm-service-queues-app/src/overlay.test.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import Overlay from './overlay.component'; +import { useLayoutType, isDesktop } from '@openmrs/esm-framework'; + +const mockUseLayoutType = useLayoutType as jest.Mock; +const mockIsDesktop = isDesktop as jest.Mock; + +jest.mock('@openmrs/esm-framework'); + +const headerText = 'Test Header'; + +describe('Overlay Component', () => { + it('renders desktop layout with close button', () => { + const closePanelMock = jest.fn(); + + mockIsDesktop.mockImplementation(() => true); + + render(); + + const headerContent = screen.getByText(headerText); + const closeButton = screen.getByLabelText('Close overlay'); + + expect(headerContent).toBeInTheDocument(); + expect(closeButton).toBeInTheDocument(); + + fireEvent.click(closeButton); + expect(closePanelMock).toHaveBeenCalled(); + }); + + it('renders tablet layout with back arrow button and the children', () => { + const closePanelMock = jest.fn(); + mockIsDesktop.mockImplementation(() => false); + mockUseLayoutType.mockImplementation(() => 'tablet'); + + render( + +

    Something
    + , + ); + + const headerContent = screen.getByText(headerText); + const backButton = screen.getByText('Close overlay'); + + expect(headerContent).toBeInTheDocument(); + expect(backButton).toBeInTheDocument(); + expect(screen.getByText('Something')).toBeInTheDocument(); + }); +}); diff --git a/packages/esm-outpatient-app/src/past-visit/past-visit-details/encounter-list.component.tsx b/packages/esm-service-queues-app/src/past-visit/past-visit-details/encounter-list.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/past-visit/past-visit-details/encounter-list.component.tsx rename to packages/esm-service-queues-app/src/past-visit/past-visit-details/encounter-list.component.tsx diff --git a/packages/esm-outpatient-app/src/past-visit/past-visit-details/medications-list.component.tsx b/packages/esm-service-queues-app/src/past-visit/past-visit-details/medications-list.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/past-visit/past-visit-details/medications-list.component.tsx rename to packages/esm-service-queues-app/src/past-visit/past-visit-details/medications-list.component.tsx diff --git a/packages/esm-outpatient-app/src/past-visit/past-visit-details/notes-list.component.tsx b/packages/esm-service-queues-app/src/past-visit/past-visit-details/notes-list.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/past-visit/past-visit-details/notes-list.component.tsx rename to packages/esm-service-queues-app/src/past-visit/past-visit-details/notes-list.component.tsx diff --git a/packages/esm-outpatient-app/src/past-visit/past-visit-details/past-visit-summary.component.tsx b/packages/esm-service-queues-app/src/past-visit/past-visit-details/past-visit-summary.component.tsx similarity index 96% rename from packages/esm-outpatient-app/src/past-visit/past-visit-details/past-visit-summary.component.tsx rename to packages/esm-service-queues-app/src/past-visit/past-visit-details/past-visit-summary.component.tsx index ca7453855..4e84b9449 100644 --- a/packages/esm-outpatient-app/src/past-visit/past-visit-details/past-visit-summary.component.tsx +++ b/packages/esm-service-queues-app/src/past-visit/past-visit-details/past-visit-summary.component.tsx @@ -144,12 +144,6 @@ const PastVisitSummary: React.FC = ({ encounters, patient onClick={() => setSelectedTabIndex(2)}> {t('medications', 'Medications')} - setSelectedTabIndex(3)}> - {t('encounters', 'Encounters')} - diff --git a/packages/esm-outpatient-app/src/past-visit/past-visit-details/past-visit-summary.scss b/packages/esm-service-queues-app/src/past-visit/past-visit-details/past-visit-summary.scss similarity index 100% rename from packages/esm-outpatient-app/src/past-visit/past-visit-details/past-visit-summary.scss rename to packages/esm-service-queues-app/src/past-visit/past-visit-details/past-visit-summary.scss diff --git a/packages/esm-outpatient-app/src/past-visit/past-visit.component.tsx b/packages/esm-service-queues-app/src/past-visit/past-visit.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/past-visit/past-visit.component.tsx rename to packages/esm-service-queues-app/src/past-visit/past-visit.component.tsx diff --git a/packages/esm-outpatient-app/src/past-visit/past-visit.resource.ts b/packages/esm-service-queues-app/src/past-visit/past-visit.resource.ts similarity index 100% rename from packages/esm-outpatient-app/src/past-visit/past-visit.resource.ts rename to packages/esm-service-queues-app/src/past-visit/past-visit.resource.ts diff --git a/packages/esm-outpatient-app/src/past-visit/past-visit.scss b/packages/esm-service-queues-app/src/past-visit/past-visit.scss similarity index 96% rename from packages/esm-outpatient-app/src/past-visit/past-visit.scss rename to packages/esm-service-queues-app/src/past-visit/past-visit.scss index dc479c62d..6c12590c5 100644 --- a/packages/esm-outpatient-app/src/past-visit/past-visit.scss +++ b/packages/esm-service-queues-app/src/past-visit/past-visit.scss @@ -26,8 +26,8 @@ .container { background-color: $ui-background; - padding: spacing.$spacing-05; - margin: spacing.$spacing-05 0rem spacing.$spacing-05; + padding: spacing.$spacing-03; + margin: spacing.$spacing-03 0rem spacing.$spacing-03; width: 100%; } diff --git a/packages/esm-outpatient-app/src/past-visit/past-visit.test.tsx b/packages/esm-service-queues-app/src/past-visit/past-visit.test.tsx similarity index 85% rename from packages/esm-outpatient-app/src/past-visit/past-visit.test.tsx rename to packages/esm-service-queues-app/src/past-visit/past-visit.test.tsx index be5d00f19..b2540dfc7 100644 --- a/packages/esm-outpatient-app/src/past-visit/past-visit.test.tsx +++ b/packages/esm-service-queues-app/src/past-visit/past-visit.test.tsx @@ -24,18 +24,15 @@ describe('PastVisit: ', () => { expect(screen.queryAllByText(/vitals/i)); const vitalsTab = screen.getByRole('tab', { name: /vitals/i }); - const encountersTab = screen.getByRole('tab', { name: /encounters/i }); expect(vitalsTab).toBeInTheDocument(); expect(screen.getByRole('tab', { name: /notes/i })).toBeInTheDocument(); expect(screen.getByRole('tab', { name: /medications/i })).toBeInTheDocument(); - expect(screen.getByRole('tab', { name: /^encounters$/i })).toBeInTheDocument(); await user.click(vitalsTab); expect(vitalsTab).toHaveAttribute('aria-selected', 'true'); - expect(encountersTab).toHaveAttribute('aria-selected', 'false'); }); }); diff --git a/packages/esm-outpatient-app/src/patient-info/appointment-details.component.tsx b/packages/esm-service-queues-app/src/patient-info/appointment-details.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/patient-info/appointment-details.component.tsx rename to packages/esm-service-queues-app/src/patient-info/appointment-details.component.tsx diff --git a/packages/esm-outpatient-app/src/patient-info/appointment-details.scss b/packages/esm-service-queues-app/src/patient-info/appointment-details.scss similarity index 100% rename from packages/esm-outpatient-app/src/patient-info/appointment-details.scss rename to packages/esm-service-queues-app/src/patient-info/appointment-details.scss diff --git a/packages/esm-outpatient-app/src/patient-info/appointment-details.test.tsx b/packages/esm-service-queues-app/src/patient-info/appointment-details.test.tsx similarity index 100% rename from packages/esm-outpatient-app/src/patient-info/appointment-details.test.tsx rename to packages/esm-service-queues-app/src/patient-info/appointment-details.test.tsx diff --git a/packages/esm-outpatient-app/src/patient-info/appointments.resource.tsx b/packages/esm-service-queues-app/src/patient-info/appointments.resource.tsx similarity index 100% rename from packages/esm-outpatient-app/src/patient-info/appointments.resource.tsx rename to packages/esm-service-queues-app/src/patient-info/appointments.resource.tsx diff --git a/packages/esm-outpatient-app/src/patient-info/contact-details.component.tsx b/packages/esm-service-queues-app/src/patient-info/contact-details.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/patient-info/contact-details.component.tsx rename to packages/esm-service-queues-app/src/patient-info/contact-details.component.tsx diff --git a/packages/esm-outpatient-app/src/patient-info/contact-details.scss b/packages/esm-service-queues-app/src/patient-info/contact-details.scss similarity index 100% rename from packages/esm-outpatient-app/src/patient-info/contact-details.scss rename to packages/esm-service-queues-app/src/patient-info/contact-details.scss diff --git a/packages/esm-outpatient-app/src/patient-info/contact-details.test.tsx b/packages/esm-service-queues-app/src/patient-info/contact-details.test.tsx similarity index 100% rename from packages/esm-outpatient-app/src/patient-info/contact-details.test.tsx rename to packages/esm-service-queues-app/src/patient-info/contact-details.test.tsx diff --git a/packages/esm-outpatient-app/src/patient-info/hooks/usePatientAttributes.tsx b/packages/esm-service-queues-app/src/patient-info/hooks/usePatientAttributes.tsx similarity index 100% rename from packages/esm-outpatient-app/src/patient-info/hooks/usePatientAttributes.tsx rename to packages/esm-service-queues-app/src/patient-info/hooks/usePatientAttributes.tsx diff --git a/packages/esm-outpatient-app/src/patient-info/patient-info.component.tsx b/packages/esm-service-queues-app/src/patient-info/patient-info.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/patient-info/patient-info.component.tsx rename to packages/esm-service-queues-app/src/patient-info/patient-info.component.tsx diff --git a/packages/esm-outpatient-app/src/patient-info/patient-info.scss b/packages/esm-service-queues-app/src/patient-info/patient-info.scss similarity index 100% rename from packages/esm-outpatient-app/src/patient-info/patient-info.scss rename to packages/esm-service-queues-app/src/patient-info/patient-info.scss diff --git a/packages/esm-outpatient-app/src/patient-info/patient-info.test.tsx b/packages/esm-service-queues-app/src/patient-info/patient-info.test.tsx similarity index 100% rename from packages/esm-outpatient-app/src/patient-info/patient-info.test.tsx rename to packages/esm-service-queues-app/src/patient-info/patient-info.test.tsx diff --git a/packages/esm-outpatient-app/src/patient-queue-header/patient-queue-header.component.tsx b/packages/esm-service-queues-app/src/patient-queue-header/patient-queue-header.component.tsx similarity index 94% rename from packages/esm-outpatient-app/src/patient-queue-header/patient-queue-header.component.tsx rename to packages/esm-service-queues-app/src/patient-queue-header/patient-queue-header.component.tsx index aac0e27f9..fd2024efb 100644 --- a/packages/esm-outpatient-app/src/patient-queue-header/patient-queue-header.component.tsx +++ b/packages/esm-service-queues-app/src/patient-queue-header/patient-queue-header.component.tsx @@ -15,9 +15,9 @@ import styles from './patient-queue-header.scss'; const PatientQueueHeader: React.FC<{ title?: string }> = ({ title }) => { const { t } = useTranslation(); + const { queueLocations } = useQueueLocations(); const userSession = useSession(); const userLocation = userSession?.sessionLocation?.display; - const { queueLocations } = useQueueLocations(); const currentQueueLocationName = useSelectedQueueLocationName(); const handleQueueLocationChange = ({ selectedItem }) => { @@ -28,11 +28,11 @@ const PatientQueueHeader: React.FC<{ title?: string }> = ({ title }) => { return ( <> -
    +
    -

    {t('serviceQueue', 'Service queue')}

    +

    {t('serviceQueues', 'Service queues')}

    {title ?? t('home', 'Home')}

    diff --git a/packages/esm-outpatient-app/src/patient-queue-header/patient-queue-header.scss b/packages/esm-service-queues-app/src/patient-queue-header/patient-queue-header.scss similarity index 100% rename from packages/esm-outpatient-app/src/patient-queue-header/patient-queue-header.scss rename to packages/esm-service-queues-app/src/patient-queue-header/patient-queue-header.scss diff --git a/packages/esm-outpatient-app/src/patient-queue-header/patient-queue-illustration.component.tsx b/packages/esm-service-queues-app/src/patient-queue-header/patient-queue-illustration.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/patient-queue-header/patient-queue-illustration.component.tsx rename to packages/esm-service-queues-app/src/patient-queue-header/patient-queue-illustration.component.tsx diff --git a/packages/esm-outpatient-app/src/patient-queue-metrics/clinic-metrics.component.tsx b/packages/esm-service-queues-app/src/patient-queue-metrics/clinic-metrics.component.tsx similarity index 96% rename from packages/esm-outpatient-app/src/patient-queue-metrics/clinic-metrics.component.tsx rename to packages/esm-service-queues-app/src/patient-queue-metrics/clinic-metrics.component.tsx index e67b2c17e..5e3aa6bb3 100644 --- a/packages/esm-outpatient-app/src/patient-queue-metrics/clinic-metrics.component.tsx +++ b/packages/esm-service-queues-app/src/patient-queue-metrics/clinic-metrics.component.tsx @@ -29,7 +29,7 @@ function ClinicMetrics() { const { allServices } = useServices(currentQueueLocation ?? queueLocations?.[0]?.id); const currentServiceUuid = useSelectedServiceUuid(); const currentServiceName = useSelectedServiceName(); - const { serviceCount } = useServiceMetricsCount(currentServiceName, currentQueueLocation); + const { serviceCount } = useServiceMetricsCount(currentServiceName, currentQueueLocation ?? queueLocations?.[0]?.id); const [initialSelectedItem, setInitialSelectItem] = useState(() => { if (currentServiceName && currentServiceUuid) { return false; @@ -56,7 +56,7 @@ function ClinicMetrics() { return ( <> -
    +
    ({ + ...jest.requireActual('@openmrs/esm-framework'), + useLayoutType: () => 'desktop', + showNotification: jest.fn(), + showToast: jest.fn(), + useConfig: jest.fn(() => ({ + concepts: { + visitQueueNumberAttributeUuid: 'c61ce16f-272a-41e7-9924-4c555d0932c5', + defaultStatusConceptUuid: 'c61ce16f-272a-41e7-9924-4c555d0932c5', + }, + })), +})); +jest.mock('../hooks/useQueueLocations', () => ({ + useQueueLocations: jest.fn(() => ({ + queueLocations: [{ id: '1', name: 'Location 1' }], + })), +})); + +jest.mock('../../active-visits/active-visits-table.resource', () => ({ + usePriority: jest.fn(() => ({ + priorities: [{ uuid: '197852c7-5fd4-4b33-89cc-7bae6848c65a', display: 'High' }], + })), + useStatus: jest.fn(() => ({ + statuses: [{ uuid: '176052c7-5fd4-4b33-89cc-7bae6848c65a', display: 'In Progress' }], + })), +})); + +jest.mock('../../patient-queue-metrics/queue-metrics.resource', () => ({ + useServices: jest.fn((selectedQueueLocation) => ({ + allServices: [{ uuid: 'e2ec9cf0-ec38-4d2b-af6c-59c82fa30b90', name: 'Service 1' }], + isLoading: false, + })), +})); + +describe('StartVisitQueueFields', () => { + it('renders the form fields', () => { + const { getByLabelText, getByText } = render(); + + expect(getByLabelText('Select a queue location')).toBeInTheDocument(); + expect(getByLabelText('Select a service')).toBeInTheDocument(); + expect(getByLabelText('Select a status')).toBeInTheDocument(); + expect(getByText('High')).toBeInTheDocument(); + expect(getByLabelText('Sort weight')).toBeInTheDocument(); + }); + + it('updates the selected queue location', () => { + const { getByLabelText } = render(); + + const selectQueueLocation = getByLabelText('Select a queue location') as HTMLInputElement; + fireEvent.change(selectQueueLocation, { target: { value: '1' } }); + + expect(selectQueueLocation.value).toBe('1'); + }); + + it('updates the selected service', () => { + const { getByLabelText } = render(); + + const selectService = getByLabelText('Select a service') as HTMLInputElement; + fireEvent.change(selectService, { target: { value: 'service-1' } }); + + expect(selectService.value).toBe('e2ec9cf0-ec38-4d2b-af6c-59c82fa30b90'); + }); +}); diff --git a/packages/esm-outpatient-app/src/patient-search/visit-form/base-visit-type.component.tsx b/packages/esm-service-queues-app/src/patient-search/visit-form/base-visit-type.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/patient-search/visit-form/base-visit-type.component.tsx rename to packages/esm-service-queues-app/src/patient-search/visit-form/base-visit-type.component.tsx diff --git a/packages/esm-outpatient-app/src/patient-search/visit-form/base-visit-type.scss b/packages/esm-service-queues-app/src/patient-search/visit-form/base-visit-type.scss similarity index 100% rename from packages/esm-outpatient-app/src/patient-search/visit-form/base-visit-type.scss rename to packages/esm-service-queues-app/src/patient-search/visit-form/base-visit-type.scss diff --git a/packages/esm-service-queues-app/src/patient-search/visit-form/base-visit-type.test.tsx b/packages/esm-service-queues-app/src/patient-search/visit-form/base-visit-type.test.tsx new file mode 100644 index 000000000..895e46d6f --- /dev/null +++ b/packages/esm-service-queues-app/src/patient-search/visit-form/base-visit-type.test.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import BaseVisitType from './base-visit-type.component'; +import { mockVisitTypes } from '../../../__mocks__/visits.mock'; + +jest.mock('@openmrs/esm-framework', () => ({ + useLayoutType: () => 'desktop', + usePagination: jest.fn(() => ({ results: mockVisitTypes, currentPage: 1, goTo: jest.fn() })), +})); + +describe('BaseVisitType', () => { + it('renders visit types correctly', () => { + render( {}} visitTypes={mockVisitTypes} />); + + const searchInput = screen.getByRole('searchbox'); + expect(searchInput).toBeInTheDocument(); + + mockVisitTypes.forEach((visitType) => { + const radioButton = screen.getByLabelText(visitType.display); + expect(radioButton).toBeInTheDocument(); + }); + }); + + it('handles search input correctly', () => { + render( {}} visitTypes={mockVisitTypes} />); + + const searchInput: HTMLInputElement = screen.getByRole('searchbox'); + fireEvent.change(searchInput, { target: { value: 'Visit Type 1' } }); + + expect(searchInput.value).toBe('Visit Type 1'); + }); + + it('calls onChange when a visit type is selected', () => { + const mockOnChange = jest.fn(); + render(); + + const radioButton: HTMLInputElement = screen.getByLabelText(mockVisitTypes[0].display); + fireEvent.click(radioButton); + expect(radioButton.checked).toBe(true); + }); +}); diff --git a/packages/esm-outpatient-app/src/patient-search/visit-form/queue.resource.ts b/packages/esm-service-queues-app/src/patient-search/visit-form/queue.resource.ts similarity index 100% rename from packages/esm-outpatient-app/src/patient-search/visit-form/queue.resource.ts rename to packages/esm-service-queues-app/src/patient-search/visit-form/queue.resource.ts diff --git a/packages/esm-outpatient-app/src/patient-search/visit-form/recommended-visit-type.component.tsx b/packages/esm-service-queues-app/src/patient-search/visit-form/recommended-visit-type.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/patient-search/visit-form/recommended-visit-type.component.tsx rename to packages/esm-service-queues-app/src/patient-search/visit-form/recommended-visit-type.component.tsx diff --git a/packages/esm-outpatient-app/src/patient-search/visit-form/visit-form.component.tsx b/packages/esm-service-queues-app/src/patient-search/visit-form/visit-form.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/patient-search/visit-form/visit-form.component.tsx rename to packages/esm-service-queues-app/src/patient-search/visit-form/visit-form.component.tsx diff --git a/packages/esm-outpatient-app/src/patient-search/visit-form/visit-form.scss b/packages/esm-service-queues-app/src/patient-search/visit-form/visit-form.scss similarity index 100% rename from packages/esm-outpatient-app/src/patient-search/visit-form/visit-form.scss rename to packages/esm-service-queues-app/src/patient-search/visit-form/visit-form.scss diff --git a/packages/esm-outpatient-app/src/queue-entry-table-components/actions-menu.component.tsx b/packages/esm-service-queues-app/src/queue-entry-table-components/actions-menu.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-entry-table-components/actions-menu.component.tsx rename to packages/esm-service-queues-app/src/queue-entry-table-components/actions-menu.component.tsx diff --git a/packages/esm-outpatient-app/src/queue-entry-table-components/actions-menu.scss b/packages/esm-service-queues-app/src/queue-entry-table-components/actions-menu.scss similarity index 100% rename from packages/esm-outpatient-app/src/queue-entry-table-components/actions-menu.scss rename to packages/esm-service-queues-app/src/queue-entry-table-components/actions-menu.scss diff --git a/packages/esm-outpatient-app/src/queue-entry-table-components/edit-entry.component.tsx b/packages/esm-service-queues-app/src/queue-entry-table-components/edit-entry.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-entry-table-components/edit-entry.component.tsx rename to packages/esm-service-queues-app/src/queue-entry-table-components/edit-entry.component.tsx diff --git a/packages/esm-outpatient-app/src/queue-entry-table-components/edit-entry.scss b/packages/esm-service-queues-app/src/queue-entry-table-components/edit-entry.scss similarity index 100% rename from packages/esm-outpatient-app/src/queue-entry-table-components/edit-entry.scss rename to packages/esm-service-queues-app/src/queue-entry-table-components/edit-entry.scss diff --git a/packages/esm-outpatient-app/src/queue-entry-table-components/open-chart.component.tsx b/packages/esm-service-queues-app/src/queue-entry-table-components/open-chart.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-entry-table-components/open-chart.component.tsx rename to packages/esm-service-queues-app/src/queue-entry-table-components/open-chart.component.tsx diff --git a/packages/esm-outpatient-app/src/queue-entry-table-components/open-chart.resource.tsx b/packages/esm-service-queues-app/src/queue-entry-table-components/open-chart.resource.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-entry-table-components/open-chart.resource.tsx rename to packages/esm-service-queues-app/src/queue-entry-table-components/open-chart.resource.tsx diff --git a/packages/esm-outpatient-app/src/queue-entry-table-components/open-chart.scss b/packages/esm-service-queues-app/src/queue-entry-table-components/open-chart.scss similarity index 100% rename from packages/esm-outpatient-app/src/queue-entry-table-components/open-chart.scss rename to packages/esm-service-queues-app/src/queue-entry-table-components/open-chart.scss diff --git a/packages/esm-outpatient-app/src/queue-entry-table-components/status-icon.component.tsx b/packages/esm-service-queues-app/src/queue-entry-table-components/status-icon.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-entry-table-components/status-icon.component.tsx rename to packages/esm-service-queues-app/src/queue-entry-table-components/status-icon.component.tsx diff --git a/packages/esm-outpatient-app/src/queue-entry-table-components/transition-entry.component.tsx b/packages/esm-service-queues-app/src/queue-entry-table-components/transition-entry.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-entry-table-components/transition-entry.component.tsx rename to packages/esm-service-queues-app/src/queue-entry-table-components/transition-entry.component.tsx diff --git a/packages/esm-outpatient-app/src/queue-entry-table-components/transition-entry.scss b/packages/esm-service-queues-app/src/queue-entry-table-components/transition-entry.scss similarity index 100% rename from packages/esm-outpatient-app/src/queue-entry-table-components/transition-entry.scss rename to packages/esm-service-queues-app/src/queue-entry-table-components/transition-entry.scss diff --git a/packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist-base-table.component.tsx b/packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist-base-table.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist-base-table.component.tsx rename to packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist-base-table.component.tsx diff --git a/packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist-base-table.scss b/packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist-base-table.scss similarity index 100% rename from packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist-base-table.scss rename to packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist-base-table.scss diff --git a/packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist-base-table.test.tsx b/packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist-base-table.test.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist-base-table.test.tsx rename to packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist-base-table.test.tsx diff --git a/packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist-filter.component.tsx b/packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist-filter.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist-filter.component.tsx rename to packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist-filter.component.tsx diff --git a/packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist-filter.scss b/packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist-filter.scss similarity index 100% rename from packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist-filter.scss rename to packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist-filter.scss diff --git a/packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist-filter.test.tsx b/packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist-filter.test.tsx new file mode 100644 index 000000000..2dacd0fdc --- /dev/null +++ b/packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist-filter.test.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import QueueLinelistFilter from './queue-linelist-filter.component'; +import { mockVisitTypes } from '../../__mocks__/visits.mock'; + +jest.mock('@openmrs/esm-framework', () => ({ + ...jest.requireActual('@openmrs/esm-framework'), + useLayoutType: jest.fn(() => 'tablet'), + useVisitTypes: jest.fn(() => mockVisitTypes), + toOpemrsIsoString: jest.fn(), +})); +// Additional mock functions if needed + +describe('QueueLinelistFilter', () => { + it('renders the form with filter elements', () => { + render(); + + expect(screen.getByText('Gender')).toBeInTheDocument(); + expect(screen.getByLabelText('Age')).toBeInTheDocument(); + expect(screen.getByLabelText('Between')).toBeInTheDocument(); + expect(screen.getByLabelText('And')).toBeInTheDocument(); + expect(screen.getByLabelText('Date')).toBeInTheDocument(); + expect(screen.getByText("Use today's date")).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /Select visit type/i })).toBeInTheDocument(); + expect(screen.getByText('Cancel')).toBeInTheDocument(); + expect(screen.getByText('Apply filters')).toBeInTheDocument(); + }); + + it('calls closePanel function when cancel button is clicked', () => { + const closePanelMock = jest.fn(); + render(); + + const cancelButton = screen.getByText('Cancel'); + fireEvent.click(cancelButton); + + expect(closePanelMock).toHaveBeenCalledTimes(1); + }); + + it('updates gender state when a radio button is selected', () => { + render(); + + const maleRadioButton = screen.getByLabelText('Male'); + fireEvent.click(maleRadioButton); + + expect(maleRadioButton).toBeChecked(); + }); + + it('updates startAge state when a number is entered', () => { + render(); + + const startAgeInput = screen.getByLabelText('Between'); + fireEvent.change(startAgeInput, { target: { value: '10' } }); + + expect(startAgeInput).toHaveValue(10); + }); + + it('updates returnDate state when date input changes', () => { + render(); + + const returnDateInput = screen.getByLabelText('Date'); + fireEvent.change(returnDateInput, { target: { value: '2023-08-20' } }); + + expect(returnDateInput).toHaveValue('2023-08-20'); + }); + + it('should open the visit type dropdown and close after selection', () => { + render(); + + const visitTypeDropdown = screen.getByRole('button', { name: /Select visit type/i }); + fireEvent.click(visitTypeDropdown); + const type1Option = screen.getByText('Outpatient Visit'); + fireEvent.click(type1Option); + + expect(visitTypeDropdown).toHaveTextContent('Open menu'); + }); +}); diff --git a/packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist.component.tsx b/packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist.component.tsx rename to packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist.component.tsx diff --git a/packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist.resource.tsx b/packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist.resource.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-patient-linelists/queue-linelist.resource.tsx rename to packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist.resource.tsx diff --git a/packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist.test.tsx b/packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist.test.tsx new file mode 100644 index 000000000..6476bb8b0 --- /dev/null +++ b/packages/esm-service-queues-app/src/queue-patient-linelists/queue-linelist.test.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import QueueLinelist from './queue-linelist.component'; + +describe('QueueLinelist', () => { + it('renders with filter content initially', () => { + render(); + + const filterContent = screen.getByText('Filters'); + expect(filterContent).toBeInTheDocument(); + }); + + it('toggles between filter content and null', () => { + render(); + + const closeButton = screen.getByText('Close overlay'); + fireEvent.click(closeButton); + + const filterContent = screen.queryByTestId('filter-content'); + expect(filterContent).not.toBeInTheDocument(); + }); +}); diff --git a/packages/esm-outpatient-app/src/queue-patient-linelists/queue-services-table.component.tsx b/packages/esm-service-queues-app/src/queue-patient-linelists/queue-services-table.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-patient-linelists/queue-services-table.component.tsx rename to packages/esm-service-queues-app/src/queue-patient-linelists/queue-services-table.component.tsx diff --git a/packages/esm-outpatient-app/src/queue-patient-linelists/scheduled-appointments-table.component.tsx b/packages/esm-service-queues-app/src/queue-patient-linelists/scheduled-appointments-table.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-patient-linelists/scheduled-appointments-table.component.tsx rename to packages/esm-service-queues-app/src/queue-patient-linelists/scheduled-appointments-table.component.tsx diff --git a/packages/esm-service-queues-app/src/queue-patient-linelists/scheduled-appointments-table.test.tsx b/packages/esm-service-queues-app/src/queue-patient-linelists/scheduled-appointments-table.test.tsx new file mode 100644 index 000000000..e179d6262 --- /dev/null +++ b/packages/esm-service-queues-app/src/queue-patient-linelists/scheduled-appointments-table.test.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import AppointmentsTable from './scheduled-appointments-table.component'; +import { mockAppointmentsData } from '../../__mocks__/appointments-data.mock'; + +jest.mock('@openmrs/esm-framework', () => ({ + ...jest.requireActual('@openmrs/esm-framework'), + useConfig: () => ({ + appointmentStatuses: ['All', 'Scheduled', 'Completed'], + }), + usePagination: () => ({ + goTo: jest.fn(), + results: mockAppointmentsData.data, + currentPage: 1, + }), +})); + +jest.mock('./queue-linelist.resource', () => ({ + useAppointments: () => ({ + appointmentQueueEntries: mockAppointmentsData.data, + isLoading: false, + }), +})); + +describe('AppointmentsTable', () => { + it('renders appointments when loading is complete', () => { + render(); + + const appointmentName = screen.getByText('Hungai Kevin'); + expect(appointmentName).toBeInTheDocument(); + }); + + it('filters appointments based on status selection', () => { + render(); + + const statusDropdown = screen.getAllByLabelText('Status:'); + fireEvent.change(statusDropdown[0], { target: { value: 'Completed' } }); + + const filteredAppointmentName = screen.getByText('Hungai Kevin'); + expect(filteredAppointmentName).toBeInTheDocument(); + }); +}); diff --git a/packages/esm-outpatient-app/src/queue-rooms/queue-room-form.component.tsx b/packages/esm-service-queues-app/src/queue-rooms/queue-room-form.component.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-rooms/queue-room-form.component.tsx rename to packages/esm-service-queues-app/src/queue-rooms/queue-room-form.component.tsx diff --git a/packages/esm-service-queues-app/src/queue-rooms/queue-room-form.test.tsx b/packages/esm-service-queues-app/src/queue-rooms/queue-room-form.test.tsx new file mode 100644 index 000000000..937d9ea5f --- /dev/null +++ b/packages/esm-service-queues-app/src/queue-rooms/queue-room-form.test.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import QueueRoomForm from './queue-room-form.component'; +import { mockServices } from '../../__mocks__/active-visits.mock'; + +jest.mock('@openmrs/esm-framework', () => ({ + ...jest.requireActual('@openmrs/esm-framework'), + useLayoutType: jest.fn(() => 'tablet'), + useServices: jest.fn(() => ({ services: mockServices })), + useQueueLocations: jest.fn(() => ({ + queueLocations: { uuid: 'e7786d9a-ab62-11ec-b909-0242ac120002', display: 'Location Test' }, + })), + showToast: jest.fn(), +})); + +jest.mock('./queue-room.resource', () => ({ + saveQueueRoom: jest.fn(() => Promise.resolve({ status: 201 })), +})); + +describe('QueueRoomForm', () => { + it('renders the form with queue room elements', () => { + render(); + + expect(screen.getByLabelText('Queue room name')).toBeInTheDocument(); + expect(screen.getByLabelText('Select a service')).toBeInTheDocument(); + expect(screen.getByLabelText('Select a queue location')).toBeInTheDocument(); + expect(screen.getByText('Cancel')).toBeInTheDocument(); + expect(screen.getByText('Save')).toBeInTheDocument(); + }); + + it('displays error notification if queue room name is missing on submission', () => { + render(); + + fireEvent.click(screen.getByText('Save')); + + expect(screen.getByText('Missing queue room name')).toBeInTheDocument(); + }); + + it('displays error notification if queue room service is missing on submission', () => { + render(); + + fireEvent.change(screen.getByLabelText('Queue room name'), { target: { value: 'Room 123' } }); + fireEvent.click(screen.getByText('Save')); + + expect(screen.getByText('Missing queue room service')).toBeInTheDocument(); + }); + + it('calls closePanel when Cancel button is clicked', () => { + const closePanelMock = jest.fn(); + render(); + + fireEvent.click(screen.getByText('Cancel')); + + expect(closePanelMock).toHaveBeenCalledTimes(1); + }); + + it('updates queue room name state when a value is entered', () => { + render(); + + const queueRoomNameInput = screen.getByLabelText('Queue room name'); + fireEvent.change(queueRoomNameInput, { target: { value: 'Room 123' } }); + + expect(queueRoomNameInput).toHaveValue('Room 123'); + }); +}); diff --git a/packages/esm-outpatient-app/src/queue-rooms/queue-room.resource.tsx b/packages/esm-service-queues-app/src/queue-rooms/queue-room.resource.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-rooms/queue-room.resource.tsx rename to packages/esm-service-queues-app/src/queue-rooms/queue-room.resource.tsx diff --git a/packages/esm-outpatient-app/src/queue-rooms/queue-room.scss b/packages/esm-service-queues-app/src/queue-rooms/queue-room.scss similarity index 100% rename from packages/esm-outpatient-app/src/queue-rooms/queue-room.scss rename to packages/esm-service-queues-app/src/queue-rooms/queue-room.scss diff --git a/packages/esm-outpatient-app/src/queue-screen/queue-screen.component.tsx b/packages/esm-service-queues-app/src/queue-screen/queue-screen.component.tsx similarity index 95% rename from packages/esm-outpatient-app/src/queue-screen/queue-screen.component.tsx rename to packages/esm-service-queues-app/src/queue-screen/queue-screen.component.tsx index d0e9321d4..5b68b255d 100644 --- a/packages/esm-outpatient-app/src/queue-screen/queue-screen.component.tsx +++ b/packages/esm-service-queues-app/src/queue-screen/queue-screen.component.tsx @@ -28,7 +28,7 @@ const QueueScreen: React.FC = () => { return (
    - +
    {rowData.map((row) => (
    diff --git a/packages/esm-outpatient-app/src/queue-screen/queue-screen.scss b/packages/esm-service-queues-app/src/queue-screen/queue-screen.scss similarity index 100% rename from packages/esm-outpatient-app/src/queue-screen/queue-screen.scss rename to packages/esm-service-queues-app/src/queue-screen/queue-screen.scss diff --git a/packages/esm-outpatient-app/src/queue-screen/queue-screen.test.tsx b/packages/esm-service-queues-app/src/queue-screen/queue-screen.test.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-screen/queue-screen.test.tsx rename to packages/esm-service-queues-app/src/queue-screen/queue-screen.test.tsx diff --git a/packages/esm-outpatient-app/src/queue-screen/useActiveTickets.tsx b/packages/esm-service-queues-app/src/queue-screen/useActiveTickets.tsx similarity index 100% rename from packages/esm-outpatient-app/src/queue-screen/useActiveTickets.tsx rename to packages/esm-service-queues-app/src/queue-screen/useActiveTickets.tsx diff --git a/packages/esm-outpatient-app/src/queue-services/queue-service-form.component.tsx b/packages/esm-service-queues-app/src/queue-services/queue-service-form.component.tsx similarity index 99% rename from packages/esm-outpatient-app/src/queue-services/queue-service-form.component.tsx rename to packages/esm-service-queues-app/src/queue-services/queue-service-form.component.tsx index 625824a72..30b7cf9c6 100644 --- a/packages/esm-outpatient-app/src/queue-services/queue-service-form.component.tsx +++ b/packages/esm-service-queues-app/src/queue-services/queue-service-form.component.tsx @@ -111,7 +111,7 @@ const QueueServiceForm: React.FC = ({ toggleSearchType, c