diff --git a/CHANGELOG.md b/CHANGELOG.md index 1974d77c695..966117c1586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ ## 2.0.0 Release Candidate +### Breaking changes + +- **Removed following endpoints from gateway:** + | Path | Method | + |--------------------|--------| + | `/location` | `*` | + | `/location/{id}` | `*` | + | `/locations` | `GET` | + | `/locations` | `POST` | + | `/locations/{id}` | `*` | + ### New features #### HTTP Input diff --git a/packages/client/.storybook/decorators.tsx b/packages/client/.storybook/decorators.tsx index 6f230036706..0251428b4cf 100644 --- a/packages/client/.storybook/decorators.tsx +++ b/packages/client/.storybook/decorators.tsx @@ -19,7 +19,7 @@ import { } from '@opencrvs/commons/client' import { testDataGenerator } from '@client/tests/test-data-generators' import { getLeafLocationIds } from '@client/v2-events/hooks/useLocations' -import { V2_DEFAULT_MOCK_LOCATIONS_MAP } from '../.storybook/default-request-handlers' +import { V2_DEFAULT_MOCK_LOCATIONS_MAP } from '@client/tests/v2-events/locations-mock' const generator = testDataGenerator() diff --git a/packages/client/.storybook/default-request-handlers.ts b/packages/client/.storybook/default-request-handlers.ts index 979b934f005..e9b58bf6ea7 100644 --- a/packages/client/.storybook/default-request-handlers.ts +++ b/packages/client/.storybook/default-request-handlers.ts @@ -28,9 +28,11 @@ import { footballClubMembershipEvent, libraryMembershipEvent, LocationType, - TestUserRole + TestUserRole, + Location } from '@opencrvs/commons/client' import { testDataGenerator } from '@client/tests/test-data-generators' +import { V2_DEFAULT_MOCK_LOCATIONS } from '@client/tests/v2-events/locations-mock' async function ensureCacheExists(cacheName: string) { const cacheNames = await caches.keys() @@ -55,169 +57,6 @@ const tRPCMsw = createTRPCMsw({ transformer: { input: superjson, output: superjson } }) -export const V2_DEFAULT_MOCK_LOCATIONS = [ - { - id: 'a45b982a-5c7b-4bd9-8fd8-a42d0994054c' as UUID, - name: 'Central', - locationType: LocationType.enum.ADMIN_STRUCTURE, - parentId: null, - validUntil: null - }, - { - id: 'c599b691-fd2d-45e1-abf4-d185de727fb5' as UUID, - name: 'Sulaka', - locationType: LocationType.enum.ADMIN_STRUCTURE, - parentId: null, - validUntil: null - }, - { - id: '7ef2b9c7-5e6d-49f6-ae05-656207d0fc64' as UUID, - name: 'Pualula', - locationType: LocationType.enum.ADMIN_STRUCTURE, - parentId: null, - validUntil: null - }, - { - id: '6d1a59df-988c-4021-a846-ccbc021931a7' as UUID, - name: 'Chuminga', - locationType: LocationType.enum.ADMIN_STRUCTURE, - parentId: null, - validUntil: null - }, - { - id: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, - name: 'Ibombo', - locationType: LocationType.enum.ADMIN_STRUCTURE, - parentId: 'a45b982a-5c7b-4bd9-8fd8-a42d0994054c' as UUID, - validUntil: null - }, - { - // @NOTE: This happens to map to a valid location in events test environment. Updating it will break tests. - // @TODO: Find a way to give out context aware mock values in the future. - id: '27160bbd-32d1-4625-812f-860226bfb92a' as UUID, - name: 'Isango', - locationType: LocationType.enum.ADMIN_STRUCTURE, - parentId: 'a45b982a-5c7b-4bd9-8fd8-a42d0994054c' as UUID, - validUntil: null - }, - { - id: '967032fd-3f81-478a-826c-30cb8fe121bd' as UUID, - name: 'Isamba', - locationType: LocationType.enum.ADMIN_STRUCTURE, - parentId: 'a45b982a-5c7b-4bd9-8fd8-a42d0994054c' as UUID, - validUntil: null - }, - { - id: '89a33893-b17d-481d-a26d-6461e7ac1651' as UUID, - name: 'Itambo', - locationType: LocationType.enum.ADMIN_STRUCTURE, - parentId: 'a45b982a-5c7b-4bd9-8fd8-a42d0994054c' as UUID, - validUntil: null - }, - { - id: 'd42ab2fe-e7ed-470e-8b31-4fb27f9b8250' as UUID, - name: 'Ezhi', - locationType: LocationType.enum.ADMIN_STRUCTURE, - parentId: 'a45b982a-5c7b-4bd9-8fd8-a42d0994054c' as UUID, - validUntil: null - }, - { - id: '423d000f-101b-47c0-8b86-21a908067cee' as UUID, - name: 'Chamakubi Health Post', - locationType: LocationType.enum.HEALTH_FACILITY, - parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, - validUntil: null - }, - { - id: '4d3279be-d026-420c-88f7-f0a4ae986973' as UUID, - name: 'Ibombo Rural Health Centre', - locationType: LocationType.enum.HEALTH_FACILITY, - parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, - validUntil: null - }, - { - id: '190902f4-1d77-476a-8947-41145af1db7d' as UUID, - name: 'Chikobo Rural Health Centre', - locationType: LocationType.enum.HEALTH_FACILITY, - parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, - validUntil: null - }, - { - id: 'f5ecbd9b-a01e-4a65-910e-70e86ab41b71' as UUID, - name: 'Chilochabalenje Health Post', - locationType: LocationType.enum.HEALTH_FACILITY, - parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, - validUntil: null - }, - { - id: 'dbfc178f-7295-4b90-b28d-111c95b03127' as UUID, - name: 'Chipeso Rural Health Centre', - locationType: LocationType.enum.HEALTH_FACILITY, - parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, - validUntil: null - }, - { - id: '09862bfe-c7ac-46cd-987b-668681533c80' as UUID, - name: 'Chisamba Rural Health Centre', - locationType: LocationType.enum.HEALTH_FACILITY, - parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, - validUntil: null - }, - { - id: '834ce389-e95b-4fb0-96a0-33e9ab323059' as UUID, - name: 'Chitanda Rural Health Centre', - locationType: LocationType.enum.HEALTH_FACILITY, - parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, - validUntil: null - }, - { - id: '0431c433-6062-4a4c-aee9-25271aec61ee' as UUID, - name: 'Golden Valley Rural Health Centre', - locationType: LocationType.enum.HEALTH_FACILITY, - parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, - validUntil: null - }, - { - id: 'bc84d0b6-7ba7-480d-a339-5d9920d90eb2' as UUID, - name: 'Ipongo Rural Health Centre', - locationType: LocationType.enum.HEALTH_FACILITY, - parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, - validUntil: null - }, - { - id: '4cf1f53b-b730-41d2-8649-dff7eeed970d' as UUID, - name: 'Itumbwe Health Post', - locationType: LocationType.enum.HEALTH_FACILITY, - parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, - validUntil: null - }, - { - id: '4b3676cb-9355-4942-9eb9-2ce46acaf0e0' as UUID, - name: 'Kabangalala Rural Health Centre', - locationType: LocationType.enum.HEALTH_FACILITY, - parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, - validUntil: null - }, - { - id: '028d2c85-ca31-426d-b5d1-2cef545a4902' as UUID, - name: 'Ibombo District Office', - locationType: LocationType.enum.CRVS_OFFICE, - parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, - validUntil: null - }, - { - id: '62a0ccb4-4f30-4f30-8882-f256007dff9f' as UUID, - name: 'Isamba District Office', - locationType: LocationType.enum.CRVS_OFFICE, - parentId: '967032fd-3f81-478a-826c-30cb8fe121bd' as UUID, - validUntil: null - } -] - -export const V2_DEFAULT_MOCK_LOCATIONS_MAP = new Map( - V2_DEFAULT_MOCK_LOCATIONS.map((l) => [l.id, l]) -) - export const handlers = { drafts: [ tRPCMsw.event.draft.list.query(() => { diff --git a/packages/client/src/App.test.tsx b/packages/client/src/App.test.tsx index 879de1cdcb5..8f58d37ab25 100644 --- a/packages/client/src/App.test.tsx +++ b/packages/client/src/App.test.tsx @@ -13,7 +13,6 @@ import { createTestApp, flushPromises, getItem } from '@client/tests/util' import * as actions from '@client/notification/actions' import { AppStore } from '@client/store' import { createClient } from '@client/utils/apolloClient' -import { referenceApi } from '@client/utils/referenceApi' import { ReactWrapper } from 'enzyme' import { vi } from 'vitest' import { waitFor } from './tests/wait-for-element' @@ -110,16 +109,4 @@ describe('when user has a valid token in local storage', () => { await flushPromises() expect(assign.mock.calls).toHaveLength(0) }) - - it('loads languages, facilities and locations on startup', async () => { - const loadFacilities = vi.spyOn(referenceApi, 'loadFacilities') - const loadContent = vi.spyOn(referenceApi, 'loadContent') - const loadLocations = vi.spyOn(referenceApi, 'loadLocations') - - createTestApp() - await flushPromises() - expect(loadFacilities).toHaveBeenCalled() - expect(loadContent).toHaveBeenCalled() - expect(loadLocations).toHaveBeenCalled() - }) }) diff --git a/packages/client/src/App.tsx b/packages/client/src/App.tsx index 44c61a353ee..d373cfc31e7 100644 --- a/packages/client/src/App.tsx +++ b/packages/client/src/App.tsx @@ -74,6 +74,7 @@ import { UserList } from './views/SysAdmin/Team/user/UserList' import VSExport from './views/SysAdmin/Vsexports/VSExport' import { UserAudit } from './views/UserAudit/UserAudit' import { config } from './config' +import { TRPCProvider } from './v2-events/trpc' // Injecting global styles for the body tag - used only once const GlobalStyle = createGlobalStyle` @@ -299,14 +300,6 @@ export const routesConfig = turnOffV2Events path: routes.PERFORMANCE_FIELD_AGENT_LIST, element: }, - { - path: routes.PERFORMANCE_HOME, - element: ( - - - - ) - }, { path: routes.EVENT_COMPLETENESS_RATES, element: @@ -331,19 +324,35 @@ export const routesConfig = turnOffV2Events }, { path: routes.CREATE_USER_ON_LOCATION, - element: + element: ( + + + + ) }, { path: routes.CREATE_USER_SECTION, - element: + element: ( + + + + ) }, { path: routes.REVIEW_USER_FORM, - element: + element: ( + + + + ) }, { path: routes.REVIEW_USER_DETAILS, - element: + element: ( + + + + ) } ] } @@ -399,14 +408,6 @@ export const routesConfig = turnOffV2Events path: routes.PERFORMANCE_FIELD_AGENT_LIST, element: }, - { - path: routes.PERFORMANCE_HOME, - element: ( - - - - ) - }, { path: routes.EVENT_COMPLETENESS_RATES, element: @@ -431,19 +432,35 @@ export const routesConfig = turnOffV2Events }, { path: routes.CREATE_USER_ON_LOCATION, - element: + element: ( + + + + ) }, { path: routes.CREATE_USER_SECTION, - element: + element: ( + + + + ) }, { path: routes.REVIEW_USER_FORM, - element: + element: ( + + + + ) }, { path: routes.REVIEW_USER_DETAILS, - element: + element: ( + + + + ) } ] } diff --git a/packages/client/src/components/LocationPicker.test.tsx b/packages/client/src/components/LocationPicker.test.tsx index 802a75682bd..05830d5b3de 100644 --- a/packages/client/src/components/LocationPicker.test.tsx +++ b/packages/client/src/components/LocationPicker.test.tsx @@ -8,12 +8,13 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ + +import { vi, beforeEach, afterEach, describe, it, expect } from 'vitest' import { AppStore, createStore } from '@client/store' import { createTestComponent, flushPromises } from '@client/tests/util' import { waitForElement } from '@client/tests/wait-for-element' import { ReactWrapper } from 'enzyme' import React from 'react' -import { vi } from 'vitest' import { LocationPicker } from './LocationPicker' describe('location picker tests', () => { @@ -29,7 +30,7 @@ describe('location picker tests', () => { beforeEach(async () => { const { component: testComponent } = await createTestComponent( , { store }, @@ -48,7 +49,7 @@ describe('location picker tests', () => { '#location-range-picker-action' ) - expect(actionElement.hostNodes().text()).toBe('Baniajan Union') + expect(actionElement.hostNodes().text()).toBe('Ibombo District Office') }) it('focuses input on click action', async () => { diff --git a/packages/client/src/components/LocationPicker.tsx b/packages/client/src/components/LocationPicker.tsx index d04a0cd9472..4490eb41748 100644 --- a/packages/client/src/components/LocationPicker.tsx +++ b/packages/client/src/components/LocationPicker.tsx @@ -9,10 +9,14 @@ * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ import * as React from 'react' -import { MapPin, Location, Cross } from '@opencrvs/components/lib/icons' +import { + MapPin, + Location as LocationIcon, + Cross +} from '@opencrvs/components/lib/icons' import { IStoreState } from '@client/store' import { getOfflineData } from '@client/offline/selectors' -import { generateLocations } from '@client/utils/locationUtils' +import { generateLocationsV2 } from '@client/utils/locationUtils' import { ISearchLocation, LocationSearch @@ -32,6 +36,8 @@ import { } from '@client/components/DateRangePicker' import styled from 'styled-components' import { ILocation } from '@client/offline/reducer' +import { useLocations } from '@client/v2-events/hooks/useLocations' +import { Location } from '@opencrvs/commons/client' const { useState, useEffect } = React @@ -45,7 +51,7 @@ interface IBaseProps { selectedLocationId?: string disabled?: boolean onChangeLocation: (locationId: string) => void - locationFilter?: (location: ILocation) => boolean + locationFilter?: (location: Location) => boolean } type LocationPickerProps = IBaseProps & IConnectProps & WrappedComponentProps @@ -110,8 +116,11 @@ function LocationPickerComponent(props: LocationPickerProps) { } = props const [modalVisible, setModalVisible] = useState(false) - const offlineSearchableLocations = generateLocations( - { ...offlineLocations, ...offlineOffices }, + const { getLocations } = useLocations() + const locations = getLocations.useSuspenseQuery() + + const offlineSearchableLocations = generateLocationsV2( + locations, intl, locationFilter ) @@ -161,7 +170,7 @@ function LocationPickerComponent(props: LocationPickerProps) { - + {intl.formatMessage(constantsMessages.location)} { children } = this.props - if (offlineDataLoaded && initialDeclarationsLoaded && registerFormLoaded) { + if (offlineDataLoaded && initialDeclarationsLoaded) { return (
{children} diff --git a/packages/client/src/components/interface/DownloadButton.test.tsx b/packages/client/src/components/interface/DownloadButton.test.tsx deleted file mode 100644 index ade1739254f..00000000000 --- a/packages/client/src/components/interface/DownloadButton.test.tsx +++ /dev/null @@ -1,114 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { - createTestComponent, - createTestStore, - setScopes -} from '@client/tests/util' -import { DownloadButton } from './DownloadButton' -import { AppStore } from '@client/store' -import * as React from 'react' -import { DownloadAction } from '@client/forms' -import * as declarationReducer from '@client/declarations' -import { ApolloClient } from '@apollo/client' -import { createClient } from '@client/utils/apolloClient' -import { SCOPES } from '@opencrvs/commons/client' - -const { DOWNLOAD_STATUS } = declarationReducer - -describe('download button', () => { - let store: AppStore - let client: ApolloClient<{}> - - describe('when there is no assignment', () => { - beforeEach(async () => { - const testStore = await createTestStore() - store = testStore.store - - client = createClient(store) - }) - - it('if the record is actionable, download button should not be disabled', async () => { - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - , - { store, apolloClient: client } - ) - - expect( - component.find('#download-icon').hostNodes().prop('disabled') - ).toBeFalsy() - }) - - it('if the record is not actionable, download button should be disabled', async () => { - setScopes([SCOPES.RECORD_SUBMIT_FOR_REVIEW], store) - const { component } = await createTestComponent( - , - { store, apolloClient: client } - ) - expect( - component.find('#download-icon').hostNodes().prop('disabled') - ).toBeTruthy() - }) - }) - - describe('when there is assignment', () => { - beforeEach(async () => { - const testStore = await createTestStore() - store = testStore.store - client = createClient(store) - }) - - it('if assigned to current user & not downloaded then should not show avatar', async () => { - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - , - { store, apolloClient: client } - ) - expect(component.find('img').length).toBe(0) - }) - }) -}) diff --git a/packages/client/src/components/interface/Navigation.test.tsx b/packages/client/src/components/interface/Navigation.test.tsx deleted file mode 100644 index ac7af4f2715..00000000000 --- a/packages/client/src/components/interface/Navigation.test.tsx +++ /dev/null @@ -1,645 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ - -import { Navigation } from '@client/components/interface/Navigation' -import { WORKQUEUE_TABS } from '@client/components/interface/WorkQueueTabs' -import { queries } from '@client/profile/queries' -import { storage } from '@client/storage' -import { createStore } from '@client/store' -import { - createTestComponent, - flushPromises, - mockUserResponse, - REGISTRATION_AGENT_DEFAULT_SCOPES, - setScopes, - SYSTEM_ADMIN_DEFAULT_SCOPES -} from '@client/tests/util' -import { createClient } from '@client/utils/apolloClient' -import { OfficeHome } from '@client/views/OfficeHome/OfficeHome' -import { ReactWrapper } from 'enzyme' -import { merge } from 'lodash' -import * as React from 'react' -import { scopes as allScopes, Scope, SCOPES } from '@opencrvs/commons/client' -import { vi } from 'vitest' -import { createMemoryRouter } from 'react-router-dom' -import { formatUrl } from '@client/navigation' -import { REGISTRAR_HOME_TAB } from '@client/navigation/routes' - -const mockFetchUserDetails = vi.fn() - -const nameObj = { - data: { - getUser: { - name: [ - { - use: 'en', - firstNames: 'Mohammad', - familyName: 'Ashraful', - __typename: 'HumanName' - }, - { use: 'bn', firstNames: '', familyName: '', __typename: 'HumanName' } - ] - } - } -} - -const nameObjNatlSysAdmin = { - data: { - getUser: { - name: [ - { - use: 'en', - firstNames: 'Mohammad', - familyName: 'Ashraful', - __typename: 'HumanName' - }, - { use: 'bn', firstNames: '', familyName: '', __typename: 'HumanName' } - ] - } - } -} - -storage.getItem = vi.fn() -storage.setItem = vi.fn() - -let { store } = createStore() -let client = createClient(store) - -describe('Navigation for national system admin related tests', () => { - let testComponent: ReactWrapper<{}, {}> - - beforeEach(async () => { - merge(mockUserResponse, nameObjNatlSysAdmin) - mockFetchUserDetails.mockReturnValue(mockUserResponse) - queries.fetchUserDetails = mockFetchUserDetails - ;({ store } = createStore()) - client = createClient(store) - - setScopes(SYSTEM_ADMIN_DEFAULT_SCOPES, store) - await flushPromises() - - const { component } = await createTestComponent(, { store }) - - testComponent = component - }) - - it('Tabs loaded successfully including config tab', async () => { - expect(testComponent.exists('#navigation_team')).toBeTruthy() - expect(testComponent.exists('#navigation_config_main')).toBeTruthy() - testComponent.find('#navigation_config_main').hostNodes().simulate('click') - testComponent.update() - }) - - it('No application related tabs', async () => { - expect(testComponent.exists('#navigation_progress')).toBeFalsy() - expect(testComponent.exists('#navigation_sentForReview')).toBeFalsy() - expect(testComponent.exists('#navigation_readyForReview')).toBeFalsy() - expect(testComponent.exists('#navigation_requiresUpdate')).toBeFalsy() - expect(testComponent.exists('#navigation_print')).toBeFalsy() - expect(testComponent.exists('#navigation_waitingValidation')).toBeFalsy() - }) -}) - -describe('Navigation for Registration agent related tests', () => { - let testComponent: ReactWrapper<{}, {}> - let router: ReturnType - beforeEach(async () => { - merge(mockUserResponse, nameObj) - mockFetchUserDetails.mockReturnValue(mockUserResponse) - queries.fetchUserDetails = mockFetchUserDetails - ;({ store } = createStore()) - client = createClient(store) - - setScopes(REGISTRATION_AGENT_DEFAULT_SCOPES, store) - - await flushPromises() - - const { component, router: testRouter } = await createTestComponent( - , - { store } - ) - router = testRouter - testComponent = component - }) - it('renders page with team and performance tab for registration agent', async () => { - const { component } = await createTestComponent(, { - store, - apolloClient: client - }) - expect(component.exists('#navigation_team')).toBeTruthy() - expect(component.exists('#navigation_config_main')).toBeFalsy() - }) - - it('5 application tabs exists for registration agent', async () => { - expect(testComponent.exists('#navigation_progress')).toBeTruthy() - expect(testComponent.exists('#navigation_sentForReview')).toBeFalsy() - expect(testComponent.exists('#navigation_readyForReview')).toBeTruthy() - expect(testComponent.exists('#navigation_requiresUpdate')).toBeTruthy() - expect(testComponent.exists('#navigation_print')).toBeTruthy() - expect(testComponent.exists('#navigation_waitingValidation')).toBeFalsy() - expect(testComponent.exists('#navigation_approvals')).toBeTruthy() - }) - - it('redirects when tabs are clicked', async () => { - testComponent - .find('#navigation_readyForReview') - .hostNodes() - .simulate('click') - await flushPromises() - - expect(router.state.location.pathname).toContain('readyForReview') - - testComponent - .find('#navigation_requiresUpdate') - .hostNodes() - .simulate('click') - await flushPromises() - expect(router.state.location.pathname).toContain('requiresUpdate') - - testComponent.find('#navigation_approvals').hostNodes().simulate('click') - await flushPromises() - expect(router.state.location.pathname).toContain('approvals') - }) -}) - -describe('Navigation for District Registrar related tests', () => { - let testComponent: ReactWrapper<{}, {}> - - beforeEach(async () => { - merge(mockUserResponse, nameObj) - mockFetchUserDetails.mockReturnValue(mockUserResponse) - queries.fetchUserDetails = mockFetchUserDetails - ;({ store } = createStore()) - client = createClient(store) - - const { component } = await createTestComponent( - {}} />, - { store } - ) - - testComponent = component - }) - it('settings and logout exists on navigation mobile view', async () => { - expect(testComponent.exists('#navigation_settings')).toBeTruthy() - expect(testComponent.exists('#navigation_logout')).toBeTruthy() - }) -}) - -describe('Given a user with scopes views Navigation', () => { - let testComponent: ReactWrapper<{}, {}> - let build: () => Promise> - - beforeEach(async () => { - ;({ store } = createStore()) - client = createClient(store) - - build = async () => - ( - await createTestComponent(, { - store, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.inProgress - }) - ], - path: REGISTRAR_HOME_TAB - }) - )?.component - }) - describe('My drafts', async () => { - const id = `#navigation_${WORKQUEUE_TABS.myDrafts}` - - const requiredScopes = [ - SCOPES.RECORD_DECLARE_BIRTH, - SCOPES.RECORD_DECLARE_BIRTH_MY_JURISDICTION, - SCOPES.RECORD_DECLARE_DEATH, - SCOPES.RECORD_DECLARE_DEATH_MY_JURISDICTION, - SCOPES.RECORD_DECLARE_MARRIAGE, - SCOPES.RECORD_DECLARE_MARRIAGE_MY_JURISDICTION - ] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - const tests = [ - [[requiredScopes[0]], true], - [[requiredScopes[1]], true], - [[requiredScopes[2]], true], - [[requiredScopes[3]], true], - [[requiredScopes[4]], true], - [[requiredScopes[5]], true], - [allOtherScopes, false] - ] - - tests.forEach(([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('In progress', async () => { - const id = `#navigation_${WORKQUEUE_TABS.inProgress}` - - const requiredScopes = [ - SCOPES.RECORD_SUBMIT_FOR_APPROVAL, - SCOPES.RECORD_SUBMIT_FOR_UPDATES, - SCOPES.RECORD_REGISTER - ] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - const tests = [ - [[requiredScopes[0]], true], - [[requiredScopes[1]], true], - [[requiredScopes[2]], true], - [allOtherScopes, false] - ] - - tests.forEach(([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Sent for review', async () => { - const id = `#navigation_${WORKQUEUE_TABS.sentForReview}` - - const requiredScopes = [SCOPES.RECORD_SUBMIT_FOR_REVIEW] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - const tests = [ - [requiredScopes, true], - [allOtherScopes, false] - ] - - tests.forEach(([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Sent for approval', async () => { - const id = `#navigation_${WORKQUEUE_TABS.sentForApproval}` - - const requiredScopes = [ - SCOPES.RECORD_SUBMIT_FOR_APPROVAL, - SCOPES.RECORD_REGISTRATION_REQUEST_CORRECTION - ] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - const tests = [ - [requiredScopes, true], - [allOtherScopes, false] - ] - - tests.forEach(([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Requires update', async () => { - const id = `#navigation_${WORKQUEUE_TABS.requiresUpdate}` - - const requiredScopes = [SCOPES.RECORD_SUBMIT_FOR_UPDATES] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - const tests = [ - [requiredScopes, true], - [allOtherScopes, false] - ] - - tests.forEach(([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Ready for review', async () => { - const id = `#navigation_${WORKQUEUE_TABS.readyForReview}` - - const requiredScopes = [ - SCOPES.RECORD_SUBMIT_FOR_APPROVAL, - SCOPES.RECORD_SUBMIT_FOR_UPDATES, - SCOPES.RECORD_REGISTER - ] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - const tests = [ - [requiredScopes, true], - [allOtherScopes, false] - ] - - tests.forEach(([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Ready to print', async () => { - const id = `#navigation_${WORKQUEUE_TABS.readyToPrint}` - - const requiredScopes = [ - SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES - ] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - const tests = [ - [requiredScopes, true], - [allOtherScopes, false] - ] - - tests.forEach(([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('External validation', async () => { - const id = `#navigation_${WORKQUEUE_TABS.externalValidation}` - - const requiredScopes = [SCOPES.RECORD_REGISTER] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - const tests = [ - [requiredScopes, true], - [allOtherScopes, false] - ] - - tests.forEach(([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes} EXTERNAL_VALIDATION_WORKQUEUE is true in config`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(window.config.FEATURES.EXTERNAL_VALIDATION_WORKQUEUE).toBe(true) - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Ready to issue', async () => { - const id = `#navigation_${WORKQUEUE_TABS.readyToIssue}` - - const requiredScopes = [ - SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES - ] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - const tests = [ - [requiredScopes, true], - [allOtherScopes, false] - ] - - tests.forEach(([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes} and PRINT_IN_ADVANCE is true in config`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect( - store.getState().offline.offlineData.config?.BIRTH.PRINT_IN_ADVANCE - ).toBeTruthy() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Outbox', async () => { - const id = `#navigation_${WORKQUEUE_TABS.outbox}` - - const requiredScopes = [ - SCOPES.RECORD_SUBMIT_INCOMPLETE, - SCOPES.RECORD_SUBMIT_FOR_REVIEW, - SCOPES.RECORD_SUBMIT_FOR_APPROVAL, - SCOPES.RECORD_SUBMIT_FOR_UPDATES, - SCOPES.RECORD_REVIEW_DUPLICATES, - SCOPES.RECORD_REGISTER, - SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES, - SCOPES.RECORD_REGISTRATION_CORRECT, - SCOPES.RECORD_DECLARATION_ARCHIVE, - SCOPES.RECORD_DECLARATION_REINSTATE - ] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - const tests = [ - [requiredScopes, true], - [allOtherScopes, false] - ] - - tests.forEach(([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Organisation', async () => { - const id = `#navigation_${WORKQUEUE_TABS.organisation}` - - const requiredScopes = [ - SCOPES.ORGANISATION_READ_LOCATIONS, - SCOPES.ORGANISATION_READ_LOCATIONS_MY_OFFICE, - SCOPES.ORGANISATION_READ_LOCATIONS_MY_JURISDICTION - ] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - - const tests = [ - [requiredScopes, true], - [allOtherScopes, false] - ] - - tests.forEach(async ([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Team', async () => { - const id = `#navigation_${WORKQUEUE_TABS.team}` - - const requiredScopes = [ - SCOPES.ORGANISATION_READ_LOCATIONS, - SCOPES.ORGANISATION_READ_LOCATIONS_MY_OFFICE, - SCOPES.ORGANISATION_READ_LOCATIONS_MY_JURISDICTION - ] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - - const tests = [ - [requiredScopes, true], - [allOtherScopes, false] - ] - - tests.forEach(async ([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Config', async () => { - const id = `#navigation_${WORKQUEUE_TABS.config}_main` - - const requiredScopes = [SCOPES.CONFIG_UPDATE_ALL] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - - const tests = [ - [requiredScopes, true], - [allOtherScopes, false] - ] - - tests.forEach(async ([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Systems', async () => { - const id = `#navigation_${WORKQUEUE_TABS.systems}` - - const requiredScopes = [SCOPES.CONFIG_UPDATE_ALL] as Scope[] - - const tests = [[requiredScopes, true]] - - tests.forEach(async ([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes} and clicks config expander`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - testComponent - .find(`#navigation_${WORKQUEUE_TABS.config}_main`) - .hostNodes() - .simulate('click') - - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Communications', async () => { - const id = `#navigation_${WORKQUEUE_TABS.communications}_main` - - const requiredScopes = [SCOPES.CONFIG_UPDATE_ALL] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - - const tests = [ - [requiredScopes, true], - [allOtherScopes, false] - ] - - tests.forEach(async ([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Email all users', async () => { - const id = `#navigation_${WORKQUEUE_TABS.emailAllUsers}` - - const requiredScopes = [SCOPES.CONFIG_UPDATE_ALL] as Scope[] - - const tests = [[requiredScopes, true]] - - tests.forEach(async ([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes} and clicks communciation expander`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - testComponent - .find(`#navigation_${WORKQUEUE_TABS.communications}_main`) - .hostNodes() - .simulate('click') - - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) - - describe('Dashboard', async () => { - const id = `#navigation_${WORKQUEUE_TABS.dashboard}_test` - - const requiredScopes = [SCOPES.PERFORMANCE_READ_DASHBOARDS] as Scope[] - - const allOtherScopes = allScopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - - const tests = [ - [requiredScopes, true], - [allOtherScopes, false] - ] - - tests.forEach(async ([scopes, exists]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - testComponent = await build() - expect(testComponent.exists(id)).toBe(exists) - }) - }) - }) -}) diff --git a/packages/client/src/declarations/index.ts b/packages/client/src/declarations/index.ts index d8048e90007..f076b5e2859 100644 --- a/packages/client/src/declarations/index.ts +++ b/packages/client/src/declarations/index.ts @@ -528,7 +528,7 @@ export function createDeclaration(event: EventType, initialData?: IFormData) { } } -export function makeDeclarationReadyToDownload( +function makeDeclarationReadyToDownload( event: EventType, compositionId: string, action: DeclarationAction @@ -597,7 +597,7 @@ export function setInitialDeclarations() { return { type: SET_INITIAL_DECLARATION } } -export const getStorageDeclarationsSuccess = ( +const getStorageDeclarationsSuccess = ( response: string ): IGetStorageDeclarationsSuccessAction => ({ type: GET_DECLARATIONS_SUCCESS, @@ -704,7 +704,7 @@ export function mergeDeclaredDeclarations( declarations.push(...transformedDeclaredDeclarations) } -export async function getDeclarationsOfCurrentUser(): Promise { +async function getDeclarationsOfCurrentUser(): Promise { // returns a 'stringified' IUserData const storageTable = await storage.getItem('USER_DATA') if (!storageTable) { @@ -882,7 +882,7 @@ export async function updateWorkqueueData( } } -export async function writeDeclarationByUser( +async function writeDeclarationByUser( getState: () => IStoreState, userId: string, declaration: IDeclaration @@ -1004,7 +1004,7 @@ export async function writeDeclarationByUserWithoutStateUpdate( return declaration } -export async function deleteDeclarationByUser( +async function deleteDeclarationByUser( userId: string, declarationId: string, state: IDeclarationsState diff --git a/packages/client/src/forms/index.ts b/packages/client/src/forms/index.ts index ae07dc3266e..2c91db5a6a9 100644 --- a/packages/client/src/forms/index.ts +++ b/packages/client/src/forms/index.ts @@ -845,6 +845,7 @@ export interface IDynamicFormField type: any } +/** @knipignore */ export type ViewType = 'form' | 'preview' | 'review' | 'hidden' type Params = Fn extends (...args: infer A) => void ? A : never diff --git a/packages/client/src/hooks/useAuthorization.ts b/packages/client/src/hooks/useAuthorization.ts index 4085aa0f00b..eb162cda7db 100644 --- a/packages/client/src/hooks/useAuthorization.ts +++ b/packages/client/src/hooks/useAuthorization.ts @@ -23,9 +23,9 @@ import { isReviewableDeclaration, isUpdatableDeclaration } from '@client/declarations/utils' -import { isOfficeUnderJurisdiction } from '@client/utils/locationUtils' -import { getOfflineData } from '@client/offline/selectors' +import { isOfficeUnderJurisdictionV2 } from '@client/utils/locationUtils' import { IStoreState } from '@client/store' +import { useLocations } from '@client/v2-events/hooks/useLocations' export const RECORD_ALLOWED_SCOPES = { UPDATE: [ @@ -72,8 +72,10 @@ export function usePermissions() { const userScopes = useSelector(getScope) const currentUser = useSelector(getUserDetails) const userPrimaryOffice = currentUser?.primaryOffice - const locations = useSelector(getOfflineData).locations - const offices = useSelector(getOfflineData).offices + + const { getLocations } = useLocations() + const locations = getLocations.useSuspenseQuery() + const roles = useSelector((store: IStoreState) => store.userForm.userRoles) const roleScopes = (role: string) => @@ -111,11 +113,10 @@ export function usePermissions() { return user.primaryOffice.id === userPrimaryOffice?.id } if (hasScope(SCOPES.USER_READ_MY_JURISDICTION)) { - return isOfficeUnderJurisdiction( + return isOfficeUnderJurisdictionV2( userPrimaryOffice.id, user.primaryOffice.id, - locations, - offices + locations ) } if (hasScope(SCOPES.USER_READ_ONLY_MY_AUDIT)) { @@ -144,11 +145,10 @@ export function usePermissions() { if (roleScopes(user.role.id).includes(SCOPES.USER_UPDATE)) { return false } - return isOfficeUnderJurisdiction( + return isOfficeUnderJurisdictionV2( userPrimaryOffice.id, user.primaryOffice.id, - locations, - offices + locations ) } @@ -173,11 +173,10 @@ export function usePermissions() { } if (hasScope(SCOPES.ORGANISATION_READ_LOCATIONS_MY_JURISDICTION)) { - return isOfficeUnderJurisdiction( + return isOfficeUnderJurisdictionV2( userPrimaryOffice.id, office.id, - locations, - offices + locations ) } return false @@ -191,11 +190,10 @@ export function usePermissions() { return true } if (hasScope(SCOPES.USER_CREATE_MY_JURISDICTION)) { - return isOfficeUnderJurisdiction( + return isOfficeUnderJurisdictionV2( userPrimaryOffice.id, office.id, - locations, - offices + locations ) } return false diff --git a/packages/client/src/i18n/reducer.ts b/packages/client/src/i18n/reducer.ts index b31da6c7c1a..4929ae6cbcb 100644 --- a/packages/client/src/i18n/reducer.ts +++ b/packages/client/src/i18n/reducer.ts @@ -139,12 +139,12 @@ export const intlReducer: LoopReducer = ( ) const languagesWithLocations = formatLocationLanguageState( - Object.values(action.payload.locations), + Object.values(action.payload.locations ?? []), loadedLanguagesState ) const languagesWithFacilities = formatLocationLanguageState( - Object.values(action.payload.facilities), + Object.values(action.payload.facilities ?? []), languagesWithLocations ) diff --git a/packages/client/src/offline/reducer.ts b/packages/client/src/offline/reducer.ts index 1062260f1f4..e1d8c0f4eaf 100644 --- a/packages/client/src/offline/reducer.ts +++ b/packages/client/src/offline/reducer.ts @@ -270,8 +270,6 @@ function delay(cmd: RunCmd, time: number) { function getDataLoadingCommands() { return Cmd.list([ - FACILITIES_CMD, - LOCATIONS_CMD, CONFIG_CMD, CONDITIONALS_CMD, VALIDATORS_CMD, diff --git a/packages/client/src/offline/selectors.ts b/packages/client/src/offline/selectors.ts index 800980b187a..f93438f50ba 100644 --- a/packages/client/src/offline/selectors.ts +++ b/packages/client/src/offline/selectors.ts @@ -22,13 +22,7 @@ function getKey(store: IStoreState, key: K) { export function isOfflineDataLoaded( state: Partial ): state is IOfflineData { - const hasAllRequiredData = - state.facilities && - state.locations && - state.config && - state.forms && - state.templates && - state.languages + const hasAllRequiredData = state.config && state.templates && state.languages const isOfflineDataLoaded = Boolean(hasAllRequiredData) if (isOfflineDataLoaded) merge(window.config, state.config) diff --git a/packages/client/src/setupTests.ts b/packages/client/src/setupTests.ts index cf201192468..3a388ff6e60 100644 --- a/packages/client/src/setupTests.ts +++ b/packages/client/src/setupTests.ts @@ -119,6 +119,8 @@ vi.doMock('@client/forms/user/fieldDefinitions/createUser', () => ({ createUserForm: mockOfflineData.userForms })) +vi.mock('@client/v2-events/hooks/useLocations') + vi.mock('@client/forms/handlebarHelpers', async () => { return { initHandlebarHelpers: () => Promise.resolve(), diff --git a/packages/client/src/tests/mock-drafts.ts b/packages/client/src/tests/mock-drafts.ts index f687f00c90c..87a0a2bcdf4 100644 --- a/packages/client/src/tests/mock-drafts.ts +++ b/packages/client/src/tests/mock-drafts.ts @@ -123,291 +123,3 @@ export const birthDraftData = { registration: registrationDetailsForBirth, documents: { imageUploader: '' } } - -export const birthReviewDraftData = { - _fhirIDMap: { - composition: '11' - }, - child: { - ...childDetails, - _fhirID: '1' - }, - father: { - ...fatherDetailsForBirth, - _fhirID: '2' - }, - informant: { - ...informantDetailsForBirth - }, - mother: { - ...motherDetailsForBirth, - _fhirID: '3' - }, - registration: { - ...registrationDetailsForBirth, - _fhirID: '4' - }, - documents: { - imageUploader: [ - { - data: 'base64-data', - type: 'image/jpeg', - optionValues: ['Mother', 'National ID (front)'], - title: 'Mother', - description: 'National ID (front)' - } - ] - } -} - -const deceasedDetails = { - firstNamesEng: 'Jeff', - familyNameEng: 'Caoes', - gender: 'female', - deceasedBirthDate: '1990-02-02', - nationality: 'FAR', - exactDateOfBirthUnknown: false, - countryPrimaryDeceased: 'FAR', - statePrimaryDeceased: '8f172823-797c-4926-930f-f1896b49ac57', - districtPrimaryDeceased: 'dc1eae5f-f89e-483c-9d19-977fa0febc23', - ruralOrUrbanPrimaryDeceased: 'URBAN', - cityPrimaryDeceased: "Deceased's town", - addressLine1UrbanOptionPrimaryDeceased: "Deceased's res area", - addressLine2UrbanOptionPrimaryDeceased: "Deceased's street", - postalCodePrimaryDeceased: "Deceased's postcode", - addressLine1RuralOptionPrimaryDeceased: '', - internationalStatePrimaryDeceased: '8f172823-797c-4926-930f-f1896b49ac57', - internationalDistrictPrimaryDeceased: 'dc1eae5f-f89e-483c-9d19-977fa0febc23', - internationalCityPrimaryDeceased: "Deceased's town", - internationalAddressLine1PrimaryDeceased: '', - internationalAddressLine2PrimaryDeceased: '', - internationalAddressLine3PrimaryDeceased: '', - internationalPostalCodePrimaryDeceased: "Deceased's postcode", - _fhirID: '50fbd713-c86d-49fe-bc6a-52094b40d8dd' -} - -const informantDetailsForDeath = { - informantType: 'SPOUSE', - firstNamesEng: 'segst', - familyNameEng: 'sbsrtgsr', - informantBirthDate: '1990-12-12', - exactDateOfBirthUnknown: false, - nationality: 'FAR', - primaryAddressSameAsOtherPrimary: true, - countryPrimaryInformant: 'FAR', - statePrimaryInformant: '8f172823-797c-4926-930f-f1896b49ac57', - districtPrimaryInformant: 'dc1eae5f-f89e-483c-9d19-977fa0febc23', - ruralOrUrbanPrimaryInformant: 'URBAN', - cityPrimaryInformant: "Deceased's town", - addressLine1UrbanOptionPrimaryInformant: "Deceased's res area", - addressLine2UrbanOptionPrimaryInformant: "Deceased's street", - postalCodePrimaryInformant: "Deceased's postcode", - addressLine1RuralOptionPrimaryInformant: '', - internationalStatePrimaryInformant: '8f172823-797c-4926-930f-f1896b49ac57', - internationalDistrictPrimaryInformant: 'dc1eae5f-f89e-483c-9d19-977fa0febc23', - internationalCityPrimaryInformant: "Deceased's town", - internationalAddressLine1PrimaryInformant: '', - internationalAddressLine2PrimaryInformant: '', - internationalAddressLine3PrimaryInformant: '', - internationalPostalCodePrimaryInformant: "Deceased's postcode", - registrationPhone: '01733333333', - registrationEmail: 'sesrthsthsr@sdfsgt.com', - _fhirIDMap: { - relatedPerson: 'c9e3e5cb-d483-4db4-afaa-625161826f00', - individual: 'cabeeea7-0f7d-41c3-84ed-8f88e4d617e1' - } -} - -const deathEventDetails = { - deathDate: '2023-07-02', - causeOfDeathEstablished: 'false', - placeOfDeath: 'OTHER', - deathLocation: '4558d280-7b34-45eb-a8b2-c7e98d7328b0', - countryPlaceofdeath: 'FAR', - statePlaceofdeath: '8f172823-797c-4926-930f-f1896b49ac57', - districtPlaceofdeath: 'dc1eae5f-f89e-483c-9d19-977fa0febc23', - ruralOrUrbanPlaceofdeath: 'URBAN', - addressLine1UrbanOptionPlaceofdeath: 'Death res are', - addressLine2UrbanOptionPlaceofdeath: 'Death street', - addressLine3UrbanOptionPlaceofdeath: 'Death number', - addressLine1RuralOptionPlaceofdeath: '', - internationalCityPlaceofdeath: 'Death town', - internationalAddressLine1Placeofdeath: '', - internationalAddressLine2Placeofdeath: '', - internationalAddressLine3Placeofdeath: '' -} - -const marriageEventDetails = { - marriageDate: '2023-07-23', - countryPlaceofmarriage: 'FAR', - statePlaceofmarriage: '8f172823-797c-4926-930f-f1896b49ac57', - districtPlaceofmarriage: 'dc1eae5f-f89e-483c-9d19-977fa0febc23', - ruralOrUrbanPlaceofmarriage: 'URBAN', - addressLine1UrbanOptionPlaceofmarriage: 'Marriage place res area', - addressLine2UrbanOptionPlaceofmarriage: 'Marriage place street', - addressLine3UrbanOptionPlaceofmarriage: 'Marriage place number', - addressLine1RuralOptionPlaceofmarriage: '', - internationalCityPlaceofmarriage: 'Marriage place town', - internationalAddressLine1Placeofmarriage: '', - internationalAddressLine2Placeofmarriage: '', - internationalAddressLine3Placeofmarriage: '', - _fhirID: 'fccf6eac-4dae-43d3-af33-2c977d1daf08' -} - -const registrationDetailsForDeath = { - _fhirID: 'fccf6eac-4dae-43d3-af33-2c977d1daf08', - trackingId: 'DJDTKUQ', - type: 'death', - commentsOrNotes: '' -} - -const groomDetails = { - firstNamesEng: 'sebsrth', - familyNameEng: 'rdryhdryt', - groomBirthDate: '1980-12-23', - nationality: 'FAR', - countryPrimaryGroom: 'FAR', - exactDateOfBirthUnknown: false, - statePrimaryGroom: '8f172823-797c-4926-930f-f1896b49ac57', - districtPrimaryGroom: 'dc1eae5f-f89e-483c-9d19-977fa0febc23', - ruralOrUrbanPrimaryGroom: 'URBAN', - cityPrimaryGroom: 'Groom town', - addressLine1UrbanOptionPrimaryGroom: 'Groom res area', - addressLine2UrbanOptionPrimaryGroom: 'Groom street', - postalCodePrimaryGroom: 'Groom popstco', - addressLine1RuralOptionPrimaryGroom: '', - internationalStatePrimaryGroom: '8f172823-797c-4926-930f-f1896b49ac57', - internationalDistrictPrimaryGroom: 'dc1eae5f-f89e-483c-9d19-977fa0febc23', - internationalCityPrimaryGroom: 'Groom town', - internationalAddressLine1PrimaryGroom: '', - internationalAddressLine2PrimaryGroom: '', - internationalAddressLine3PrimaryGroom: '', - internationalPostalCodePrimaryGroom: 'Groom popstco', - _fhirID: '89113c35-1310-4d8f-9352-0269a04a1c4a' -} - -const brideDetails = { - firstNamesEng: 'srhdyrj', - familyNameEng: 'ftjfyuj', - brideBirthDate: '1990-03-03', - nationality: 'FAR', - countryPrimaryBride: 'FAR', - exactDateOfBirthUnknown: false, - statePrimaryBride: '8f172823-797c-4926-930f-f1896b49ac57', - districtPrimaryBride: 'dc1eae5f-f89e-483c-9d19-977fa0febc23', - ruralOrUrbanPrimaryBride: 'RURAL', - cityPrimaryBride: '', - addressLine1UrbanOptionPrimaryBride: '', - addressLine2UrbanOptionPrimaryBride: '', - postalCodePrimaryBride: '', - addressLine1RuralOptionPrimaryBride: 'Bride village', - internationalStatePrimaryBride: '8f172823-797c-4926-930f-f1896b49ac57', - internationalDistrictPrimaryBride: 'dc1eae5f-f89e-483c-9d19-977fa0febc23', - internationalCityPrimaryBride: '', - internationalAddressLine1PrimaryBride: '', - internationalAddressLine2PrimaryBride: '', - internationalAddressLine3PrimaryBride: '', - internationalPostalCodePrimaryBride: '', - _fhirID: '09a68a88-921f-4eaf-8424-7d9d43e5804c' -} - -const informantDetailsForMarriage = { - informantType: 'GROOM', - otherInformantType: '', - exactDateOfBirthUnknown: '', - registrationPhone: '01733333333', - registrationEmail: 'stheyhyj@segstg.com', - _fhirID: '429445a4-f87c-467f-9d3c-f17f595a1143' -} - -const registrationDetailsForMarriage = { - _fhirID: 'a833a452-3472-408f-87e3-ad7c6e2cdcd9', - trackingId: 'M2LA47X', - registrationNumber: '2023M2LA47X', - type: 'marriage', - groomSignature: 'data:image/png;base64,iVBORw0KGkSuQmCC', - brideSignature: 'data:image/png;base64,iVBORw0KGkSuQmCC', - witnessOneSignature: 'data:image/png;base64,iVBORw0KGkSuQmCC', - witnessTwoSignature: 'data:image/png;base64,iVBORw0KGkSuQmCC', - commentsOrNotes: '', - regStatus: { - type: 'REGISTERED', - statusDate: '2023-03-09T09:22:12.673Z', - officeName: 'Ibombo District Office', - officeAlias: 'Ibombo District Office', - officeAddressLevel3: '', - officeAddressLevel4: '' - } -} - -const witnessOneDetails = { - firstNamesEng: 'Sadman', - familyNameEng: 'Anik', - relationship: 'headOfGroomFamily', - _fhirID: '36972633-1c80-4fb4-a636-17f7dc9c2e14' -} - -const witnessTwoDetails = { - firstNamesEng: 'Edgar', - familyNameEng: 'Samo', - relationship: 'headOfGroomFamily', - _fhirID: '1745b3d2-74fd-4b22-ba62-1c851d632f55' -} - -export const deathReviewDraftData = { - _fhirIDMap: { - composition: '11' - }, - deceased: { - ...deceasedDetails, - _fhirID: '1' - }, - informant: { - ...informantDetailsForDeath, - _fhirID: '1' - }, - deathEvent: deathEventDetails, - registration: { - ...registrationDetailsForDeath, - _fhirID: '4' - }, - documents: { - imageUploader: [ - { - data: 'base64-data', - type: 'image/jpeg', - optionValues: ['Mother', 'National ID (front)'], - title: 'Mother', - description: 'National ID (front)' - } - ] - } -} - -export const marriageReviewDraftData = { - _fhirIDMap: { - composition: '76f6a5b6-03fd-4f68-b419-42cb88348a63' - }, - bride: { ...brideDetails }, - groom: { ...groomDetails }, - informant: { - ...informantDetailsForMarriage - }, - witnessOne: { ...witnessOneDetails }, - witnessTwo: { ...witnessTwoDetails }, - marriageEvent: { ...marriageEventDetails }, - registration: { - ...registrationDetailsForMarriage - }, - documents: { - imageUploader: [ - { - data: 'base64-data', - type: 'image/jpeg', - optionValues: ['Mother', 'National ID (front)'], - title: 'Mother', - description: 'National ID (front)' - } - ] - } -} diff --git a/packages/client/src/tests/util.tsx b/packages/client/src/tests/util.tsx index 1a797901673..f795cef2d60 100644 --- a/packages/client/src/tests/util.tsx +++ b/packages/client/src/tests/util.tsx @@ -66,12 +66,10 @@ import { deserializeFormSection } from '@client/forms/deserializer/deserializer' import * as builtInValidators from '@client/utils/validate' import * as actions from '@client/profile/profileActions' import { createMemoryRouter, RouterProvider } from 'react-router-dom' -import { mockOfflineData } from './mock-offline-data' +import { mockOfflineData, validImageB64String } from './mock-offline-data' export const validToken = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJpYXQiOjE1MzMxOTUyMjgsImV4cCI6MTU0MzE5NTIyNywiYXVkIjpbImdhdGV3YXkiXSwic3ViIjoiMSJ9.G4KzkaIsW8fTkkF-O8DI0qESKeBI332UFlTXRis3vJ6daisu06W5cZsgYhmxhx_n0Q27cBYt2OSOnjgR72KGA5IAAfMbAJifCul8ib57R4VJN8I90RWqtvA0qGjV-sPndnQdmXzCJx-RTumzvr_vKPgNDmHzLFNYpQxcmQHA-N8li-QHMTzBHU4s9y8_5JOCkudeoTMOd_1021EDAQbrhonji5V1EOSY2woV5nMHhmq166I1L0K_29ngmCqQZYi1t6QBonsIowlXJvKmjOH5vXHdCCJIFnmwHmII4BK-ivcXeiVOEM_ibfxMWkAeTRHDshOiErBFeEvqd6VWzKvbKAH0UY-Rvnbh4FbprmO4u4_6Yd2y2HnbweSo-v76dVNcvUS0GFLFdVBt0xTay-mIeDy8CKyzNDOWhmNUvtVi9mhbXYfzzEkwvi9cWwT1M8ZrsWsvsqqQbkRCyBmey_ysvVb5akuabenpPsTAjiR8-XU2mdceTKqJTwbMU5gz-8fgulbTB_9TNJXqQlH7tyYXMWHUY3uiVHWg2xgjRiGaXGTiDgZd01smYsxhVnPAddQOhqZYCrAgVcT1GBFVvhO7CC-rhtNlLl21YThNNZNpJHsCgg31WA9gMQ_2qAJmw2135fAyylO8q7ozRUvx46EezZiPzhCkPMeELzLhQMEIqjo' -export const validImageB64String = - 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAACCAYAAABllJ3tAAAABHNCSVQICAgIfAhkiAAAABl0RVh0U29mdHdhcmUAZ25vbWUtc2NyZWVuc2hvdO8Dvz4AAAAXSURBVAiZY1RWVv7PgAcw4ZNkYGBgAABYyAFsic1CfAAAAABJRU5ErkJggg==' export const inValidImageB64String = 'wee7dfaKGgoAAAANSUhEUgAAAAgAAAACCAYAAABllJ3tAAAABHNCSVQICAgIfAhkiAAAABl0RVh0U29mdHdhcmUAZ25vbWUtc2NyZWVuc2hvdO8Dvz4AAAAXSURBVAiZY1RWVv7PgAcw4ZNkYGBgAABYyAFsic1CfAAAAABJRU5ErkJggg==' @@ -106,25 +104,6 @@ export const REGISTRAR_DEFAULT_SCOPES = [ SCOPES.SEARCH_MARRIAGE ] satisfies Scope[] -export const REGISTRATION_AGENT_DEFAULT_SCOPES = [ - SCOPES.RECORD_DECLARE_BIRTH, - SCOPES.RECORD_DECLARE_DEATH, - SCOPES.RECORD_DECLARE_MARRIAGE, - SCOPES.RECORD_SUBMIT_FOR_APPROVAL, - SCOPES.RECORD_SUBMIT_FOR_UPDATES, - SCOPES.RECORD_DECLARATION_ARCHIVE, - SCOPES.RECORD_DECLARATION_REINSTATE, - SCOPES.RECORD_REGISTRATION_REQUEST_CORRECTION, - SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES, - SCOPES.PERFORMANCE_READ, - SCOPES.PERFORMANCE_READ_DASHBOARDS, - SCOPES.ORGANISATION_READ_LOCATIONS, - SCOPES.ORGANISATION_READ_LOCATIONS_MY_OFFICE, - SCOPES.SEARCH_BIRTH, - SCOPES.SEARCH_DEATH, - SCOPES.SEARCH_MARRIAGE -] satisfies Scope[] - export const ACTION_STATUS_MAP = { [SubmissionAction.SUBMIT_FOR_REVIEW]: SUBMISSION_STATUS.READY_TO_SUBMIT, [SubmissionAction.APPROVE_DECLARATION]: SUBMISSION_STATUS.READY_TO_APPROVE, @@ -378,12 +357,6 @@ export const userDetails: UserDetails = { } } -export const mockUserResponseWithName = { - data: { - getUser: userDetails - } -} - export const mockUserResponse = { data: { getUser: { @@ -1108,41 +1081,6 @@ export const getFileFromBase64String = ( }) } -export async function goToSection(component: ReactWrapper, nth: number) { - for (let i = 0; i < nth; i++) { - await flushPromises() - await waitForElement(component, '#next_section') - component.find('#next_section').hostNodes().simulate('click') - await flushPromises() - component.update() - } -} - -export async function goToEndOfForm(component: ReactWrapper) { - await goToSection(component, 6) - await waitForElement(component, '#review_header') -} - -export async function goToDocumentsSection(component: ReactWrapper) { - await goToSection(component, 4) - await waitForElement(component, '#form_section_id_documents-view-group') -} - -export async function goToFatherSection(component: ReactWrapper) { - await goToSection(component, 3) - await waitForElement(component, '#form_section_id_father-view-group') -} - -export async function goToMotherSection(component: ReactWrapper) { - await goToSection(component, 2) - await waitForElement(component, '#form_section_id_mother-view-group') -} - -export async function goToChildSection(component: ReactWrapper) { - await goToSection(component, 1) - await waitForElement(component, '#form_section_id_child-view-group') -} - export async function getRegisterFormFromStore( store: Store, event: EventType @@ -1161,14 +1099,6 @@ export async function getReviewFormFromStore( return getReviewForm(state)![event] } -export function setPageVisibility(isVisible: boolean) { - // @ts-ignore - document.hidden = !isVisible - const evt = document.createEvent('HTMLEvents') - evt.initEvent('visibilitychange', false, true) - document.dispatchEvent(evt) -} - export function loginAsFieldAgent(store: AppStore) { return store.dispatch( setUserDetails({ diff --git a/packages/client/src/tests/v2-events/locations-mock.ts b/packages/client/src/tests/v2-events/locations-mock.ts new file mode 100644 index 00000000000..c0a6bdbc790 --- /dev/null +++ b/packages/client/src/tests/v2-events/locations-mock.ts @@ -0,0 +1,263 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ +import { UUID, LocationType, Location } from '@opencrvs/commons/client' + +/* + * Central + * | -- Central Provincial Office + * | --- Ibombo + * | -- Chamakubi Health Post + * | -- Ibombo Rural Health Centre + * | -- Chikobo Rural Health Centre + * | -- Chilochabalenje Health Post + * | -- Chipeso Rural Health Centre + * | -- Chisamba Rural Health Centre + * | -- Chitanda Rural Health Centre + * | -- Golden Valley Rural Health Centre + * | -- Ipongo Rural Health Centre + * | -- Itumbwe Health Post + * | -- Kabangalala Rural Health Centre + * | -- Ibombo District Office + * | -- Isamba District Office + * | --- Isango + * | -- Isango District Office + * | --- Isamba + * | --- Itambo + * | --- Ezhi + * Sulaka + * | -- Sulaka Provincial Office + * | --- Ilanga + * | -- Ilanga District Office + * Pualula + * Chuminga + */ +export const V2_DEFAULT_MOCK_LOCATIONS: Location[] = [ + { + id: 'a45b982a-5c7b-4bd9-8fd8-a42d0994054c' as UUID, + name: 'Central', + locationType: LocationType.enum.ADMIN_STRUCTURE, + parentId: null, + validUntil: null, + externalId: 'ydyJb1RAy4U1' + }, + { + id: 'c599b691-fd2d-45e1-abf4-d185de727fb5' as UUID, + name: 'Sulaka', + locationType: LocationType.enum.ADMIN_STRUCTURE, + parentId: null, + validUntil: null, + externalId: 'pQ8nGxWmZ2Q3' + }, + { + id: '7ef2b9c7-5e6d-49f6-ae05-656207d0fc64' as UUID, + name: 'Pualula', + locationType: LocationType.enum.ADMIN_STRUCTURE, + parentId: null, + validUntil: null, + externalId: 'Aq91DweLmT8k' + }, + { + id: '6d1a59df-988c-4021-a846-ccbc021931a7' as UUID, + name: 'Chuminga', + locationType: LocationType.enum.ADMIN_STRUCTURE, + parentId: null, + validUntil: null, + externalId: 'Rw0fYNh2Xk9a' + }, + { + id: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, + name: 'Ibombo', + locationType: LocationType.enum.ADMIN_STRUCTURE, + parentId: 'a45b982a-5c7b-4bd9-8fd8-a42d0994054c' as UUID, + validUntil: null, + externalId: 'k7DsP4vbN1Qe' + }, + { + id: '27160bbd-32d1-4625-812f-860226bfb92a' as UUID, + name: 'Isango', + locationType: LocationType.enum.ADMIN_STRUCTURE, + parentId: 'a45b982a-5c7b-4bd9-8fd8-a42d0994054c' as UUID, + validUntil: null, + externalId: 'Gm3Z9eQpHw4L' + }, + { + id: '967032fd-3f81-478a-826c-30cb8fe121bd' as UUID, + name: 'Isamba', + locationType: LocationType.enum.ADMIN_STRUCTURE, + parentId: 'a45b982a-5c7b-4bd9-8fd8-a42d0994054c' as UUID, + validUntil: null, + externalId: 'sT0xVu1KqJ7r' + }, + { + id: '89a33893-b17d-481d-a26d-6461e7ac1651' as UUID, + name: 'Itambo', + locationType: LocationType.enum.ADMIN_STRUCTURE, + parentId: 'a45b982a-5c7b-4bd9-8fd8-a42d0994054c' as UUID, + validUntil: null, + externalId: 'Nq6Bv2HpL9Te' + }, + { + id: 'd42ab2fe-e7ed-470e-8b31-4fb27f9b8250' as UUID, + name: 'Ezhi', + locationType: LocationType.enum.ADMIN_STRUCTURE, + parentId: 'a45b982a-5c7b-4bd9-8fd8-a42d0994054c' as UUID, + validUntil: null, + externalId: 'xK8cQ0ZRy5Wd' + }, + { + id: '8fbd09d2-212b-47f4-beb3-5e1694931d9f' as UUID, + name: 'Ilanga', + locationType: LocationType.enum.ADMIN_STRUCTURE, + parentId: 'c599b691-fd2d-45e1-abf4-d185de727fb5' as UUID, + validUntil: null, + externalId: 'Cq4Jm1XvN8Ls' + }, + { + id: '423d000f-101b-47c0-8b86-21a908067cee' as UUID, + name: 'Chamakubi Health Post', + locationType: LocationType.enum.HEALTH_FACILITY, + parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, + validUntil: null, + externalId: 'M1nFr8LbC2Qy' + }, + { + id: '4d3279be-d026-420c-88f7-f0a4ae986973' as UUID, + name: 'Ibombo Rural Health Centre', + locationType: LocationType.enum.HEALTH_FACILITY, + parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, + validUntil: null, + externalId: 'bT7pV6YrW0Xc' + }, + { + id: '190902f4-1d77-476a-8947-41145af1db7d' as UUID, + name: 'Chikobo Rural Health Centre', + locationType: LocationType.enum.HEALTH_FACILITY, + parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, + validUntil: null, + externalId: 'zE4qPn2SgJ5d' + }, + { + id: 'f5ecbd9b-a01e-4a65-910e-70e86ab41b71' as UUID, + name: 'Chilochabalenje Health Post', + locationType: LocationType.enum.HEALTH_FACILITY, + parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, + validUntil: null, + externalId: 'H8vDs1MqR4Uf' + }, + { + id: 'dbfc178f-7295-4b90-b28d-111c95b03127' as UUID, + name: 'Chipeso Rural Health Centre', + locationType: LocationType.enum.HEALTH_FACILITY, + parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, + validUntil: null, + externalId: 'Qw3uZ9KfX6Lm' + }, + { + id: '09862bfe-c7ac-46cd-987b-668681533c80' as UUID, + name: 'Chisamba Rural Health Centre', + locationType: LocationType.enum.HEALTH_FACILITY, + parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, + validUntil: null, + externalId: 'Yr0pCg8LdM2s' + }, + { + id: '834ce389-e95b-4fb0-96a0-33e9ab323059' as UUID, + name: 'Chitanda Rural Health Centre', + locationType: LocationType.enum.HEALTH_FACILITY, + parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, + validUntil: null, + externalId: 'tS9gJ4PwB1Qx' + }, + { + id: '0431c433-6062-4a4c-aee9-25271aec61ee' as UUID, + name: 'Golden Valley Rural Health Centre', + locationType: LocationType.enum.HEALTH_FACILITY, + parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, + validUntil: null, + externalId: 'Ld7Qm3XsA8Vr' + }, + { + id: 'bc84d0b6-7ba7-480d-a339-5d9920d90eb2' as UUID, + name: 'Ipongo Rural Health Centre', + locationType: LocationType.enum.HEALTH_FACILITY, + parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, + validUntil: null, + externalId: 'kF2sW9DmH0Bt' + }, + { + id: '4cf1f53b-b730-41d2-8649-dff7eeed970d' as UUID, + name: 'Itumbwe Health Post', + locationType: LocationType.enum.HEALTH_FACILITY, + parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, + validUntil: null, + externalId: 'Ue5Xb3VaC7Pq' + }, + { + id: '4b3676cb-9355-4942-9eb9-2ce46acaf0e0' as UUID, + name: 'Kabangalala Rural Health Centre', + locationType: LocationType.enum.HEALTH_FACILITY, + parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, + validUntil: null, + externalId: 'Pz8Kc1TqH6Jn' + }, + { + id: '6f6186ce-cd5f-4a5f-810a-2d99e7c4ba12' as UUID, + name: 'Central Provincial Office', + locationType: LocationType.enum.CRVS_OFFICE, + parentId: 'a45b982a-5c7b-4bd9-8fd8-a42d0994054c' as UUID, + validUntil: null, + externalId: 'Xr3Df8WpK6Ys' + }, + { + id: '028d2c85-ca31-426d-b5d1-2cef545a4902' as UUID, + name: 'Ibombo District Office', + locationType: LocationType.enum.CRVS_OFFICE, + parentId: '62a0ccb4-880d-4f30-8882-f256007dfff9' as UUID, + validUntil: null, + externalId: 'Sm4Nw7GrD2Vy' + }, + { + id: '62a0ccb4-4f30-4f30-8882-f256007dff9f' as UUID, + name: 'Isamba District Office', + locationType: LocationType.enum.CRVS_OFFICE, + parentId: '967032fd-3f81-478a-826c-30cb8fe121bd' as UUID, + validUntil: null, + externalId: 'Vg1Bq5XeH9Lt' + }, + { + id: '954c93e1-13f7-4435-bb82-35e0e215e07d' as UUID, + name: 'Isango District Office', + locationType: LocationType.enum.CRVS_OFFICE, + parentId: '27160bbd-32d1-4625-812f-860226bfb92a' as UUID, + validUntil: null, + externalId: 'Je7Lm2XqN9Vz' + }, + { + id: '2884f5b9-17b4-49ce-bf4d-f538228935df' as UUID, + name: 'Sulaka Provincial Office', + locationType: LocationType.enum.CRVS_OFFICE, + parentId: 'c599b691-fd2d-45e1-abf4-d185de727fb5' as UUID, + validUntil: null, + externalId: 'Ht2Wp9KcX5Qv' + }, + { + id: '030358c6-54af-44be-821b-8e4af963a49c' as UUID, + name: 'Ilanga District Office', + locationType: LocationType.enum.CRVS_OFFICE, + parentId: '8fbd09d2-212b-47f4-beb3-5e1694931d9f' as UUID, + validUntil: null, + externalId: 'Yp6Ds1WqN3Xz' + } +] + +export const V2_DEFAULT_MOCK_LOCATIONS_MAP = new Map( + V2_DEFAULT_MOCK_LOCATIONS.map((l) => [l.id, l]) +) diff --git a/packages/client/src/utils/locationUtils.ts b/packages/client/src/utils/locationUtils.ts index a698dedd2ef..7c3e530becd 100644 --- a/packages/client/src/utils/locationUtils.ts +++ b/packages/client/src/utils/locationUtils.ts @@ -24,7 +24,7 @@ import { countries } from '@client/utils/countries' import { lookup } from 'country-data' import { getDefaultLanguage } from '@client/i18n/utils' import { camelCase } from 'lodash' -import { UUID } from '@opencrvs/commons/client' +import { Location, UUID } from '@opencrvs/commons/client' export const countryAlpha3toAlpha2 = (isoCode: string): string | undefined => { const alpha2 = @@ -171,6 +171,91 @@ export function generateLocations( return generateSearchableLocations(locationArray, locations, intl, officeId) } +export function generateSearchableLocationsV2( + locations: Location[], + offlineLocations: Map, + intl: IntlShape, + officeId?: UUID +) { + const filteredLocations = officeId + ? getAssociatedLocationsAndOfficesV2(officeId, locations) + : locations + + const generated: ISearchLocation[] = filteredLocations.map( + (location: Location) => { + let locationName = location.name + + if (location.parentId && location.locationType !== 'CRVS_OFFICE') { + const parentLocation = offlineLocations.get(location.parentId) + if (parentLocation) { + locationName += `, ${parentLocation.name}` + } + } + + return { + id: location.id, + searchableText: location.name, + displayLabel: locationName + } + } + ) + return generated +} + +export function generateLocationsV2( + locations: Map, + intl: IntlShape, + filter?: (location: Location) => boolean, + officeId?: UUID +) { + let locationArray = [...locations.values()] + + if (filter) { + locationArray = locationArray.filter(filter) + } + + return generateSearchableLocationsV2(locationArray, locations, intl, officeId) +} + +function isLocationUnderJurisdiction({ + locations, + locationId, + jurisdictionId +}: { + locations: Map + locationId: UUID | null | undefined + jurisdictionId: UUID | null +}): boolean { + if (!jurisdictionId) { + return true + } + if (!locationId) { + return false + } + if (jurisdictionId === locationId) { + return true + } + return isLocationUnderJurisdiction({ + locations, + locationId: locations.get(locationId)?.parentId, + jurisdictionId + }) +} + +export function getOfficesUnderJurisdiction({ + locations, + jurisdictionId +}: { + locations: Map + jurisdictionId: UUID | null +}): Location[] { + return [...locations.values()].filter( + ({ id, locationType }) => + locationType === 'CRVS_OFFICE' && + isLocationUnderJurisdiction({ locations, locationId: id, jurisdictionId }) + ) +} + export function getJurisidictionType( locations: { [key: string]: ILocation }, locationId: string @@ -252,6 +337,23 @@ export function getLocationHierarchy( }) } +export function getLocationHierarchyV2( + locationId: UUID, + locations: Map +): UUID[] { + const parentLocation = locations.get(locationId) + if (!parentLocation) { + return [locationId] + } + const { parentId } = parentLocation + + if (!parentId) { + return [locationId] + } + + return [locationId, ...getLocationHierarchyV2(parentId, locations)] +} + export function isOfficeUnderJurisdiction( officeId: string, otherOfficeId: string, @@ -272,6 +374,25 @@ export function isOfficeUnderJurisdiction( const hierarchy = getLocationHierarchy(otherOfficeLocationId, locations) return Object.values(hierarchy).includes(parentLocation.id) } +export function isOfficeUnderJurisdictionV2( + officeId: string, + otherOfficeId: string, + locations: Map +) { + const office = locations.get(UUID.parse(officeId)) + const otherOffice = locations.get(UUID.parse(otherOfficeId)) + const officeLocationId = office?.parentId + const otherOfficeLocationId = otherOffice?.parentId + if (!officeLocationId || !otherOfficeLocationId) { + return false + } + const parentLocation = locations.get(officeLocationId) + if (!parentLocation) { + return false + } + const hierarchy = getLocationHierarchyV2(otherOfficeLocationId, locations) + return hierarchy.includes(parentLocation.id) +} function getAssociatedLocationsAndOffices( officeId: string, @@ -304,6 +425,38 @@ function getAssociatedLocationsAndOffices( return [office, ...associatedLocations] } +function getAssociatedLocationsAndOfficesV2( + officeId: string, + locations: Location[] +): Location[] { + const office = locations.find( + (location) => + location.id === officeId && location.locationType === 'CRVS_OFFICE' + ) + + if (!office) { + return [] + } + + const associatedLocations: Location[] = locations.filter((location) => { + let currentLocationId = office.parentId + + while (currentLocationId) { + const targetLocationId = currentLocationId + if (location.id === currentLocationId) { + return true + } + + const nextLocation = locations.find((loc) => loc.id === targetLocationId) + currentLocationId = nextLocation?.parentId ?? null + } + + return false + }) + + return [office, ...associatedLocations] +} + export function generateFullAddress( address: Address, offlineData: IOfflineData diff --git a/packages/client/src/utils/persistence/persistenceMiddleware.ts b/packages/client/src/utils/persistence/persistenceMiddleware.ts index 77b872d9081..c2c503faf35 100644 --- a/packages/client/src/utils/persistence/persistenceMiddleware.ts +++ b/packages/client/src/utils/persistence/persistenceMiddleware.ts @@ -93,9 +93,6 @@ export const persistenceMiddleware: Middleware<{}, IStoreState> = userDetails.primaryOffice.id, true ) - for (const query of queriesToPrefetch) { - client.query(query) - } } } } diff --git a/packages/client/src/utils/referenceApi.test.ts b/packages/client/src/utils/referenceApi.test.ts deleted file mode 100644 index de1e0f8bd83..00000000000 --- a/packages/client/src/utils/referenceApi.test.ts +++ /dev/null @@ -1,537 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { referenceApi } from '@client/utils/referenceApi' -import { vi } from 'vitest' -import createFetchMock from 'vitest-fetch-mock' - -vi.unmock('@client/utils/referenceApi') - -const fetch = createFetchMock(vi) -fetch.enableMocks() - -const mockFetchLocations = { - resourceType: 'Bundle', - id: 'ed2915f5-7257-4ae6-8499-4121a42c1a88', - meta: { - lastUpdated: '2022-11-23T16:27:12.436+00:00' - }, - type: 'searchset', - total: 20, - link: [ - { - relation: 'self', - url: 'http://localhost:7070/location?type=ADMIN_STRUCTURE&_count=0&status=active' - }, - { - relation: 'next', - url: 'http://localhost:7070/location?type=ADMIN_STRUCTURE&_count=0&status=active&_getpagesoffset=0' - } - ], - entry: [ - { - fullUrl: - 'http://localhost:7070/location/7dbf10a9-23d9-4038-8b1c-9f6547ab4877/_history/39d24680-f610-45bd-8d74-2becd7defea2', - resource: { - resourceType: 'Location', - identifier: [ - { - system: 'http://opencrvs.org/specs/id/statistical-code', - value: 'ADMIN_STRUCTURE_AWn3s2RqgAN' - }, - { - system: 'http://opencrvs.org/specs/id/jurisdiction-type', - value: 'STATE' - } - ], - name: 'Central', - alias: ['Central'], - description: 'AWn3s2RqgAN', - status: 'active', - mode: 'instance', - partOf: { - reference: 'Location/0' - }, - type: { - coding: [ - { - system: 'http://opencrvs.org/specs/location-type', - code: 'ADMIN_STRUCTURE' - } - ] - }, - physicalType: { - coding: [ - { - code: 'jdn', - display: 'Jurisdiction' - } - ] - }, - extension: [ - { - url: 'http://hl7.org/fhir/StructureDefinition/location-boundary-geojson', - valueAttachment: { - contentType: 'application/geo+json', - data: '' - } - }, - { - url: 'http://opencrvs.org/specs/id/statistics-male-populations', - valueString: - '[{"2007":214275},{"2008":207428},{"2009":225250},{"2010":218551},{"2011":222568},{"2012":224450},{"2013":162537},{"2014":140674},{"2015":321276},{"2016":329269},{"2017":315012},{"2018":329397},{"2019":372808},{"2020":372995},{"2021":387162}]' - }, - { - url: 'http://opencrvs.org/specs/id/statistics-female-populations', - valueString: - '[{"2007":200754},{"2008":196169},{"2009":212379},{"2010":207788},{"2011":210183},{"2012":211088},{"2013":155454},{"2014":138895},{"2015":321542},{"2016":328719},{"2017":311254},{"2018":320242},{"2019":308227},{"2020":329978},{"2021":299071}]' - }, - { - url: 'http://opencrvs.org/specs/id/statistics-total-populations', - valueString: - '[{"2007":414977},{"2008":403572},{"2009":437579},{"2010":426327},{"2011":432706},{"2012":435471},{"2013":317995},{"2014":279569},{"2015":642817},{"2016":657989},{"2017":626267},{"2018":649640},{"2019":681033},{"2020":702973},{"2021":686234}]' - }, - { - url: 'http://opencrvs.org/specs/id/statistics-crude-birth-rates', - valueString: - '[{"2007":9.925},{"2008":10.4875},{"2009":9.024999999999999},{"2010":8.75},{"2011":9.7875},{"2012":9.9375},{"2013":9.450000000000001},{"2014":10.100000000000001},{"2015":9.162500000000001},{"2016":8.8},{"2017":8.225},{"2018":7.875},{"2019":7.1625},{"2020":6.8375},{"2021":6.987499999999999}]' - } - ], - meta: { - lastUpdated: '2022-06-27T12:04:39.175+00:00', - versionId: '39d24680-f610-45bd-8d74-2becd7defea2' - }, - id: '7dbf10a9-23d9-4038-8b1c-9f6547ab4877' - }, - request: { - method: 'PUT', - url: 'Location/7dbf10a9-23d9-4038-8b1c-9f6547ab4877' - } - }, - { - fullUrl: - 'http://localhost:7070/location/8fe4b15b-b5d0-4ff7-9f24-ec24f5d33811/_history/5e9b7837-2958-411c-a62a-582c033605f4', - resource: { - resourceType: 'Location', - identifier: [ - { - system: 'http://opencrvs.org/specs/id/statistical-code', - value: 'ADMIN_STRUCTURE_KozcEjeTyuD' - }, - { - system: 'http://opencrvs.org/specs/id/jurisdiction-type', - value: 'STATE' - } - ], - name: 'Sulaka', - alias: ['Sulaka'], - description: 'KozcEjeTyuD', - status: 'active', - mode: 'instance', - partOf: { - reference: 'Location/0' - }, - type: { - coding: [ - { - system: 'http://opencrvs.org/specs/location-type', - code: 'ADMIN_STRUCTURE' - } - ] - }, - physicalType: { - coding: [ - { - code: 'jdn', - display: 'Jurisdiction' - } - ] - }, - extension: [ - { - url: 'http://hl7.org/fhir/StructureDefinition/location-boundary-geojson', - valueAttachment: { - contentType: 'application/geo+json', - data: '' - } - }, - { - url: 'http://opencrvs.org/specs/id/statistics-male-populations', - valueString: - '[{"2007":202026},{"2008":206165},{"2009":210738},{"2010":211440},{"2011":213754},{"2012":217590},{"2013":192838},{"2014":192150},{"2015":199860},{"2016":198532},{"2017":192892},{"2018":185265},{"2019":195612},{"2020":188431},{"2021":192865}]' - }, - { - url: 'http://opencrvs.org/specs/id/statistics-female-populations', - valueString: - '[{"2007":189094},{"2008":192605},{"2009":197047},{"2010":198450},{"2011":201277},{"2012":205572},{"2013":187141},{"2014":187670},{"2015":198604},{"2016":196495},{"2017":191918},{"2018":200588},{"2019":200633},{"2020":218160},{"2021":221794}]' - }, - { - url: 'http://opencrvs.org/specs/id/statistics-total-populations', - valueString: - '[{"2007":391065},{"2008":398704},{"2009":407703},{"2010":409822},{"2011":414971},{"2012":423118},{"2013":379980},{"2014":379820},{"2015":398463},{"2016":395028},{"2017":384811},{"2018":385854},{"2019":396245},{"2020":406593},{"2021":414659}]' - }, - { - url: 'http://opencrvs.org/specs/id/statistics-crude-birth-rates', - valueString: - '[{"2007":8.85},{"2008":9.525},{"2009":8.9},{"2010":9.45},{"2011":8.5375},{"2012":8.0875},{"2013":8.2125},{"2014":8.1375},{"2015":8.625},{"2016":9.5875},{"2017":9.4},{"2018":8.850000000000001},{"2019":8.65},{"2020":9.0625},{"2021":9.75}]' - } - ], - meta: { - lastUpdated: '2022-06-27T12:04:39.185+00:00', - versionId: '5e9b7837-2958-411c-a62a-582c033605f4' - }, - id: '8fe4b15b-b5d0-4ff7-9f24-ec24f5d33811' - }, - request: { - method: 'PUT', - url: 'Location/8fe4b15b-b5d0-4ff7-9f24-ec24f5d33811' - } - } - ] -} - -const parsedLocations = { - '7dbf10a9-23d9-4038-8b1c-9f6547ab4877': { - id: '7dbf10a9-23d9-4038-8b1c-9f6547ab4877', - name: 'Central', - alias: 'Central', - status: 'active', - physicalType: 'Jurisdiction', - statisticalId: 'AWn3s2RqgAN', - jurisdictionType: 'STATE', - type: 'ADMIN_STRUCTURE', - partOf: 'Location/0' - }, - '8fe4b15b-b5d0-4ff7-9f24-ec24f5d33811': { - id: '8fe4b15b-b5d0-4ff7-9f24-ec24f5d33811', - name: 'Sulaka', - alias: 'Sulaka', - status: 'active', - physicalType: 'Jurisdiction', - statisticalId: 'KozcEjeTyuD', - jurisdictionType: 'STATE', - type: 'ADMIN_STRUCTURE', - partOf: 'Location/0' - } -} - -const mockFetchOffices = { - resourceType: 'Bundle', - id: '2ff926ac-e873-4f8b-b78d-b5a6d6537b07', - meta: { - lastUpdated: '2022-11-23T16:32:52.806+00:00' - }, - type: 'searchset', - total: 17, - link: [ - { - relation: 'self', - url: 'http://localhost:7070/location?type=CRVS_OFFICE&_count=0&status=active' - }, - { - relation: 'next', - url: 'http://localhost:7070/location?type=CRVS_OFFICE&_count=0&status=active&_getpagesoffset=0' - } - ], - entry: [ - { - fullUrl: - 'http://localhost:7070/location/b3de59a0-835d-4c62-b91b-bc86e312d08e/_history/b052793e-87b7-4bf7-9dd3-119cb2cb62a4', - resource: { - resourceType: 'Location', - identifier: [ - { - system: 'http://opencrvs.org/specs/id/internal-id', - value: 'CRVS_OFFICE_2OKicPQMNI' - } - ], - name: 'HQ Office', - alias: ['HQ Office'], - status: 'active', - mode: 'instance', - partOf: { - reference: 'Location/56eafead-7264-4c22-aa38-3dc75ad061b4' - }, - type: { - coding: [ - { - system: 'http://opencrvs.org/specs/location-type', - code: 'CRVS_OFFICE' - } - ] - }, - physicalType: { - coding: [ - { - code: 'bu', - display: 'Building' - } - ] - }, - telecom: [], - address: { - line: [], - district: 'Itambo District', - state: 'Central Province' - }, - meta: { - lastUpdated: '2022-06-27T12:04:35.538+00:00', - versionId: 'b052793e-87b7-4bf7-9dd3-119cb2cb62a4' - }, - id: 'b3de59a0-835d-4c62-b91b-bc86e312d08e' - }, - request: { - method: 'POST', - url: 'Location' - } - }, - { - fullUrl: - 'http://localhost:7070/location/964d9765-dc96-4c7e-81ec-0a5572d4ea68/_history/fcb6da4e-b574-48c1-9709-7396fd521b6c', - resource: { - resourceType: 'Location', - identifier: [ - { - system: 'http://opencrvs.org/specs/id/internal-id', - value: 'CRVS_OFFICE_JEhYJ82xRI' - } - ], - name: 'Isamba District Office', - alias: ['Isamba District Office'], - status: 'active', - mode: 'instance', - partOf: { - reference: 'Location/6f0ae45f-eefa-4b9e-9224-159e66bbee26' - }, - type: { - coding: [ - { - system: 'http://opencrvs.org/specs/location-type', - code: 'CRVS_OFFICE' - } - ] - }, - physicalType: { - coding: [ - { - code: 'bu', - display: 'Building' - } - ] - }, - telecom: [], - address: { - line: [], - district: 'Isamba District', - state: 'Central Province' - }, - meta: { - lastUpdated: '2022-06-27T12:04:35.554+00:00', - versionId: 'fcb6da4e-b574-48c1-9709-7396fd521b6c' - }, - id: '964d9765-dc96-4c7e-81ec-0a5572d4ea68' - }, - request: { - method: 'POST', - url: 'Location' - } - } - ] -} - -const mockFetchFacilities = { - resourceType: 'Bundle', - id: '946900ea-0aa7-487d-9059-2942d2f54743', - meta: { - lastUpdated: '2022-11-23T16:18:58.244+00:00' - }, - type: 'searchset', - total: 301, - link: [ - { - relation: 'self', - url: 'http://localhost:7070/location?type=HEALTH_FACILITY&_count=0&status=active' - }, - { - relation: 'next', - url: 'http://localhost:7070/location?type=HEALTH_FACILITY&_count=0&status=active&_getpagesoffset=0' - } - ], - entry: [ - { - fullUrl: - 'http://localhost:7070/location/3ab39ce5-6d03-4149-8ae2-9f4f0baeadf8/_history/05149216-4084-434c-97e2-2f0a8f1c65df', - resource: { - resourceType: 'Location', - identifier: [ - { - system: 'http://opencrvs.org/specs/id/internal-id', - value: 'HEALTH_FACILITY_di3U5u7F8Y3' - } - ], - name: 'Ibombo Rural Health Centre', - alias: ['Ibombo Rural Health Centre'], - status: 'active', - mode: 'instance', - partOf: { - reference: 'Location/b09122df-81f8-41a0-b5c6-68cba4145cab' - }, - type: { - coding: [ - { - system: 'http://opencrvs.org/specs/location-type', - code: 'HEALTH_FACILITY' - } - ] - }, - physicalType: { - coding: [ - { - code: 'bu', - display: 'Building' - } - ] - }, - telecom: [], - address: { - line: [] - }, - meta: { - lastUpdated: '2022-06-27T12:04:35.698+00:00', - versionId: '05149216-4084-434c-97e2-2f0a8f1c65df' - }, - id: '3ab39ce5-6d03-4149-8ae2-9f4f0baeadf8' - }, - request: { - method: 'POST', - url: 'Location' - } - }, - { - fullUrl: - 'http://localhost:7070/location/7599ea22-0df3-4cd9-9d15-8868487deb4d/_history/c541a21c-82cd-42d2-ad1d-060d176e0766', - resource: { - resourceType: 'Location', - identifier: [ - { - system: 'http://opencrvs.org/specs/id/internal-id', - value: 'HEALTH_FACILITY_B5LpoYehUfI' - } - ], - name: 'Chikobo Rural Health Centre', - alias: ['Chikobo Rural Health Centre'], - status: 'active', - mode: 'instance', - partOf: { - reference: 'Location/b09122df-81f8-41a0-b5c6-68cba4145cab' - }, - type: { - coding: [ - { - system: 'http://opencrvs.org/specs/location-type', - code: 'HEALTH_FACILITY' - } - ] - }, - physicalType: { - coding: [ - { - code: 'bu', - display: 'Building' - } - ] - }, - telecom: [], - address: { - line: [] - }, - meta: { - lastUpdated: '2022-06-27T12:04:35.705+00:00', - versionId: 'c541a21c-82cd-42d2-ad1d-060d176e0766' - }, - id: '7599ea22-0df3-4cd9-9d15-8868487deb4d' - }, - request: { - method: 'POST', - url: 'Location' - } - } - ] -} - -const parsedFacilities = { - 'b3de59a0-835d-4c62-b91b-bc86e312d08e': { - id: 'b3de59a0-835d-4c62-b91b-bc86e312d08e', - name: 'HQ Office', - alias: 'HQ Office', - status: 'active', - physicalType: 'Building', - jurisdictionType: '', - type: 'CRVS_OFFICE', - statisticalId: '', - partOf: 'Location/56eafead-7264-4c22-aa38-3dc75ad061b4' - }, - '964d9765-dc96-4c7e-81ec-0a5572d4ea68': { - id: '964d9765-dc96-4c7e-81ec-0a5572d4ea68', - name: 'Isamba District Office', - alias: 'Isamba District Office', - status: 'active', - physicalType: 'Building', - jurisdictionType: '', - type: 'CRVS_OFFICE', - statisticalId: '', - partOf: 'Location/6f0ae45f-eefa-4b9e-9224-159e66bbee26' - }, - '3ab39ce5-6d03-4149-8ae2-9f4f0baeadf8': { - id: '3ab39ce5-6d03-4149-8ae2-9f4f0baeadf8', - name: 'Ibombo Rural Health Centre', - alias: 'Ibombo Rural Health Centre', - status: 'active', - physicalType: 'Building', - jurisdictionType: '', - type: 'HEALTH_FACILITY', - statisticalId: '', - partOf: 'Location/b09122df-81f8-41a0-b5c6-68cba4145cab' - }, - '7599ea22-0df3-4cd9-9d15-8868487deb4d': { - id: '7599ea22-0df3-4cd9-9d15-8868487deb4d', - name: 'Chikobo Rural Health Centre', - alias: 'Chikobo Rural Health Centre', - status: 'active', - physicalType: 'Building', - jurisdictionType: '', - type: 'HEALTH_FACILITY', - statisticalId: '', - partOf: 'Location/b09122df-81f8-41a0-b5c6-68cba4145cab' - } -} - -describe('referenceApi', () => { - beforeEach(() => { - fetch.resetMocks() - }) - - it('retrieves the locations from the server', async () => { - fetch.mockResponseOnce(JSON.stringify(mockFetchLocations)) - - const data = await referenceApi.loadLocations() - expect(data).toEqual(parsedLocations) - }) - - it('retrieves the facilities from the server', async () => { - fetch.mockResponses( - [JSON.stringify(mockFetchOffices), { status: 200 }], - [JSON.stringify(mockFetchFacilities), { status: 200 }] - ) - - const data = await referenceApi.loadFacilities() - expect(data).toEqual(parsedFacilities) - }) -}) diff --git a/packages/client/src/utils/referenceApi.ts b/packages/client/src/utils/referenceApi.ts index 5fc61618604..3d2c161b827 100644 --- a/packages/client/src/utils/referenceApi.ts +++ b/packages/client/src/utils/referenceApi.ts @@ -310,38 +310,7 @@ async function loadContent(): Promise { } async function loadLocations(): Promise { - const url = `${window.config.API_GATEWAY_URL}location?type=ADMIN_STRUCTURE&_count=0` - - const res = await fetch(url, { - method: 'GET', - headers: { - Authorization: `Bearer ${getToken()}` - } - }) - - if (res && res.status !== 200) { - throw Error(res.statusText) - } - - const response = await res.json() - const locations = { - data: response.entry.reduce( - (accumulator: { [key: string]: ILocation }, entry: fhir.BundleEntry) => { - if (!entry.resource || !entry.resource.id) { - throw new Error('Resource in entry not valid') - } - - accumulator[entry.resource.id] = generateLocationResource( - entry.resource as fhir.Location - ) - - return accumulator - }, - {} - ) - } - - return locations.data + return {} } function generateLocationResource(fhirLocation: fhir.Location): ILocation { @@ -381,45 +350,7 @@ function generateLocationResource(fhirLocation: fhir.Location): ILocation { } async function loadFacilities(): Promise { - const resCRVSOffices = await fetch( - `${window.config.API_GATEWAY_URL}location?type=CRVS_OFFICE&_count=0` - ) - const resHealthFacilities = await fetch( - `${window.config.API_GATEWAY_URL}location?type=HEALTH_FACILITY&_count=0` - ) - - const locationBundleCRVSOffices = await resCRVSOffices.json() - const locationBundleHealthFacilities = await resHealthFacilities.json() - - const facilities = locationBundleCRVSOffices.entry.reduce( - (accumulator: { [key: string]: ILocation }, entry: fhir.BundleEntry) => { - if (!entry.resource || !entry.resource.id) { - throw new Error('Resource in entry not valid') - } - - accumulator[entry.resource.id] = generateLocationResource( - entry.resource as fhir.Location - ) - return accumulator - }, - {} - ) - - locationBundleHealthFacilities.entry.reduce( - (accumulator: { [key: string]: ILocation }, entry: fhir.BundleEntry) => { - if (!entry.resource || !entry.resource.id) { - throw new Error('Resource in entry not valid') - } - - accumulator[entry.resource.id] = generateLocationResource( - entry.resource as fhir.Location - ) - return accumulator - }, - facilities - ) - - return facilities + return {} } export const referenceApi = { diff --git a/packages/client/src/v2-events/features/events/registered-fields/Search.interaction.stories.tsx b/packages/client/src/v2-events/features/events/registered-fields/Search.interaction.stories.tsx index 30aa47798be..ba392f1129e 100644 --- a/packages/client/src/v2-events/features/events/registered-fields/Search.interaction.stories.tsx +++ b/packages/client/src/v2-events/features/events/registered-fields/Search.interaction.stories.tsx @@ -178,24 +178,27 @@ export const InvalidValue_NoRecordsFound: StoryObj = play: async ({ canvasElement }) => { const canvas = within(canvasElement) - await userEvent.type( - await canvas.findByTestId('text__firstname'), - 'firstname' - ) + const firstname = await canvas.findByTestId('text__firstname') + const surname = await canvas.findByTestId('text__surname') - await userEvent.type( - await canvas.findByTestId('text__surname'), - 'surname' - ) + await userEvent.type(firstname, 'firstname') - await userEvent.type( - await canvas.findByTestId('search-input'), - '456988542' - ) + firstname.blur() + + await userEvent.type(surname, 'surname') + + const searchInput = await canvas.findByTestId('search-input') + + await userEvent.type(searchInput, '456988542') + + searchInput.blur() await canvas.findByTestId('search-input-error') - await userEvent.type(await canvas.findByTestId('search-input'), '1') + await userEvent.type(searchInput, '1') + + searchInput.blur() + await waitFor(async () => expect( canvas.queryByTestId('search-input-error') @@ -212,13 +215,9 @@ export const InvalidValue_NoRecordsFound: StoryObj = { timeout: 3000 } ) - // names should not clear because search was not successfull - await expect(await canvas.findByTestId('text__firstname')).toHaveValue( - 'firstname' - ) - await expect(await canvas.findByTestId('text__surname')).toHaveValue( - 'surname' - ) + // names should not clear because search was not successful + await expect(firstname).toHaveValue('firstname') + await expect(surname).toHaveValue('surname') }, render: function Component(args) { return ( diff --git a/packages/client/src/v2-events/features/team/Team.stories.tsx b/packages/client/src/v2-events/features/team/Team.stories.tsx index 190cadc1433..4074c8b860c 100644 --- a/packages/client/src/v2-events/features/team/Team.stories.tsx +++ b/packages/client/src/v2-events/features/team/Team.stories.tsx @@ -12,7 +12,7 @@ import type { Meta, StoryObj } from '@storybook/react' import { TestUserRole } from '@opencrvs/commons/client' import { ROUTES, routesConfig } from '@client/v2-events/routes' import * as V1_LEGACY_ROUTES from '@client/navigation/routes' -import { V2_DEFAULT_MOCK_LOCATIONS } from '../../../../.storybook/default-request-handlers' +import { V2_DEFAULT_MOCK_LOCATIONS } from '@client/tests/v2-events/locations-mock' import { TeamPage } from './Team' const meta: Meta = { diff --git a/packages/config/src/handlers/locations/hierarchy.ts b/packages/client/src/v2-events/hooks/__mocks__/useLocations.ts similarity index 58% rename from packages/config/src/handlers/locations/hierarchy.ts rename to packages/client/src/v2-events/hooks/__mocks__/useLocations.ts index 03c52150409..dcb2dc6bdd4 100644 --- a/packages/config/src/handlers/locations/hierarchy.ts +++ b/packages/client/src/v2-events/hooks/__mocks__/useLocations.ts @@ -8,12 +8,15 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ -import { ServerRoute } from '@hapi/hapi' -import { resolveLocationParents } from './locationTreeSolver' -import { UUID } from '@opencrvs/commons' -export const locationHierarchyHandler: ServerRoute['handler'] = async (req) => { - const { locationId } = req.params as { locationId: UUID } +import { V2_DEFAULT_MOCK_LOCATIONS_MAP } from "@client/tests/v2-events/locations-mock"; - return await resolveLocationParents(locationId) +export { getLeafLocationIds } from "@client/v2-events/hooks/useLocations" + +export function useLocations() { + return { + getLocations: { + useSuspenseQuery: () => V2_DEFAULT_MOCK_LOCATIONS_MAP + } + } } diff --git a/packages/client/src/views/AdvancedSearch/AdvancedSearchResult.test.tsx b/packages/client/src/views/AdvancedSearch/AdvancedSearchResult.test.tsx deleted file mode 100644 index 9b015a5f778..00000000000 --- a/packages/client/src/views/AdvancedSearch/AdvancedSearchResult.test.tsx +++ /dev/null @@ -1,272 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ - -import { ADVANCED_SEARCH_RESULT } from '@client/navigation/routes' -import { - BOOKMARK_ADVANCED_SEARCH_RESULT_MUTATION, - REMOVE_ADVANCED_SEARCH_RESULT_BOOKMARK_MUTATION -} from '@client/profile/mutations' -import { getStorageUserDetailsSuccess } from '@client/profile/profileActions' -import { setAdvancedSearchParam } from '@client/search/advancedSearch/actions' -import { advancedSearchInitialState } from '@client/search/advancedSearch/reducer' -import { SEARCH_EVENTS } from '@client/search/queries' -import { AppStore, createStore } from '@client/store' -import { - createTestComponent, - flushPromises, - userDetails -} from '@client/tests/util' -import { waitForElement } from '@client/tests/wait-for-element' -import { EventType } from '@client/utils/gateway' -import { ReactWrapper } from 'enzyme' -import * as React from 'react' -import { AdvancedSearchResult } from './AdvancedSearchResult' - -const graphqlMock = [ - { - request: { - operationName: null, - query: SEARCH_EVENTS, - variables: { - advancedSearchParameters: { - event: 'death', - registrationStatuses: ['IN_PROGRESS'], - deceasedFirstNames: 'Iliyas' - }, - count: 10, - skip: 0 - } - }, - result: { - data: { - searchEvents: { - totalItems: 1, - results: [ - { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e95', - type: EventType.Death, - __typename: 'X', - registration: { - __typename: 'X', - status: 'DECLARED', - trackingId: 'DW0UTHR', - registrationNumber: null, - duplicates: [], - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1' - }, - dateOfDeath: '2007-01-01', - deceasedName: [ - { - __typename: 'X', - firstNames: 'Iliyas', - familyName: 'Khan' - }, - { - __typename: 'X', - firstNames: 'ইলিয়াস', - familyName: 'খান' - } - ], - - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - dateOfBirth: '', - childName: [] - } - ], - __typename: 'EventSearchResultSet' - } - } - } - }, - { - request: { - query: BOOKMARK_ADVANCED_SEARCH_RESULT_MUTATION, - variables: { - bookmarkSearchInput: { - userId: '123', - name: 'Death Search', - parameters: { - event: 'death', - registrationStatuses: ['IN_PROGRESS'], - deceasedFirstNames: 'Iliyas' - } - } - } - }, - result: { - data: { - bookmarkAdvancedSearch: { - searchList: [ - { - name: 'New Query', - searchId: 'c3ff3952-c137-4a82-8cde-d052885b9654', - __typename: 'BookmarkedSeachItem', - parameters: { - event: 'death' - } - } - ] - } - } - } - }, - { - request: { - query: REMOVE_ADVANCED_SEARCH_RESULT_BOOKMARK_MUTATION, - variables: { - removeBookmarkedSearchInput: { - userId: '123', - searchId: '1321356' - } - } - }, - result: { - data: { - removeBookmarkedAdvancedSearch: { - searchList: [] - } - } - } - } -] - -describe('AdvancedSearchResult Bookmark', () => { - let component: ReactWrapper<{}, {}> - let store: AppStore - - beforeEach(async () => { - ;({ store } = createStore()) - await flushPromises() - - store.dispatch( - setAdvancedSearchParam({ - ...advancedSearchInitialState, - event: 'death', - deceasedFirstNames: 'Iliyas', - registrationStatuses: ['IN_PROGRESS'] - }) - ) - - store.dispatch(getStorageUserDetailsSuccess(JSON.stringify(userDetails))) - ;({ component } = await createTestComponent(, { - store, - initialEntries: [ADVANCED_SEARCH_RESULT], - path: ADVANCED_SEARCH_RESULT, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - graphqlMocks: graphqlMock as any - })) - - await waitForElement(component, 'AdvancedSearchResultComp') - }) - - it('AdvancedSearchResultComp page loads properly', async () => { - expect(component.exists('AdvancedSearchResultComp')).toBeTruthy() - }) - - it('Show search results on page', async () => { - expect(component.find('#toggleIconEmpty').hostNodes()).toHaveLength(1) - expect(component.find('#toggleIconFill').hostNodes()).toHaveLength(0) - }) - - it('will open save bookmark modal after clicking empty toggle button', async () => { - component.find('#toggleIconEmpty').hostNodes().simulate('click') - component.update() - expect(component.find('#bookmarkModal').hostNodes()).toHaveLength(1) - }) - - it('should disable confirm button if no query name is provided', async () => { - store.dispatch( - setAdvancedSearchParam({ - ...advancedSearchInitialState, - event: 'death', - deceasedFirstNames: 'Iliyas', - registrationStatuses: ['IN_PROGRESS'] - }) - ) - component.update() - component.find('#toggleIconEmpty').hostNodes().simulate('click') - component.update() - expect( - component.find('#bookmark_advanced_search_result').hostNodes().props() - .disabled - ).toBeTruthy() - }) - - it('should enable confirm button if query name is provided', async () => { - component.find('#toggleIconEmpty').hostNodes().simulate('click') - component.update() - component - .find('#queryName') - .hostNodes() - .simulate('change', { - target: { name: 'queryName', value: 'Death Search' } - }) - component.update() - expect( - component.find('#bookmark_advanced_search_result').hostNodes().props() - .disabled - ).toBeFalsy() - }) - - it('should bookmark advanced search result & show notification if click on confirm button', async () => { - await flushPromises() - - component.find('#toggleIconEmpty').hostNodes().simulate('click') - component.update() - component - .find('#queryName') - .hostNodes() - .simulate('change', { - target: { name: 'queryName', value: 'Death Search' } - }) - component.update() - component - .find('#bookmark_advanced_search_result') - .hostNodes() - .simulate('click') - - await flushPromises() - component.update() - expect( - component.find('#success-save-bookmark-notification').hostNodes() - ).toHaveLength(1) - }) - - it('should remove bookmark advanced search & show notification if click on confirm button', async () => { - await flushPromises() - - store.dispatch( - setAdvancedSearchParam({ - ...advancedSearchInitialState, - event: 'death', - deceasedFirstNames: 'Iliyas', - registrationStatuses: ['IN_PROGRESS'], - searchId: '1321356' - }) - ) - component.update() - component.find('#toggleIconFill').hostNodes().simulate('click') - component.update() - - component - .find('#remove_advanced_search_bookmark') - .hostNodes() - .simulate('click') - - await flushPromises() - component.update() - await flushPromises() - expect( - component.find('#success-save-bookmark-notification').hostNodes() - ).toHaveLength(1) - }) -}) diff --git a/packages/client/src/views/OfficeHome/OfficeHome.test.tsx b/packages/client/src/views/OfficeHome/OfficeHome.test.tsx deleted file mode 100644 index 2a330076666..00000000000 --- a/packages/client/src/views/OfficeHome/OfficeHome.test.tsx +++ /dev/null @@ -1,493 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ - -import { queries } from '@client/profile/queries' -import { storage } from '@client/storage' -import { createStore } from '@client/store' -import { - createTestComponent, - mockUserResponse, - flushPromises, - setScopes, - REGISTRAR_DEFAULT_SCOPES -} from '@client/tests/util' -import { createClient } from '@client/utils/apolloClient' -import { OfficeHome } from '@client/views/OfficeHome/OfficeHome' -import { Spinner } from '@opencrvs/components/lib/Spinner' -import { merge } from 'lodash' -import * as React from 'react' - -import { waitFor, waitForElement } from '@client/tests/wait-for-element' -import { SELECTOR_ID } from './inProgress/InProgress' -import { WORKQUEUE_TABS } from '@client/components/interface/WorkQueueTabs' -import { Scope, SCOPES, scopes } from '@opencrvs/commons/client' -import { vi } from 'vitest' -import { REGISTRAR_HOME_TAB } from '@client/navigation/routes' -import { formatUrl } from '@client/navigation' - -const mockFetchUserDetails = vi.fn() -const mockListSyncController = vi.fn() - -const nameObj = { - data: { - getUser: { - name: [ - { - use: 'en', - firstNames: 'Mohammad', - familyName: 'Ashraful', - __typename: 'HumanName' - }, - { use: 'bn', firstNames: '', familyName: '', __typename: 'HumanName' } - ], - role: { - _id: '778464c0-08f8-4fb7-8a37-b86d1efc462a', - labels: [ - { - lang: 'en', - label: 'DISTRICT_REGISTRAR' - } - ] - } - } - } -} -merge(mockUserResponse, nameObj) -mockFetchUserDetails.mockReturnValue(mockUserResponse) -queries.fetchUserDetails = mockFetchUserDetails - -storage.getItem = vi.fn() -storage.setItem = vi.fn() - -let { store } = createStore() -let client = createClient(store) -beforeEach(async () => { - ;({ store } = createStore()) - client = createClient(store) - setScopes(REGISTRAR_DEFAULT_SCOPES, store) -}) - -describe('OfficeHome related tests', () => { - it('sets loading state while waiting for data', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.inProgress - }) - ] - } - ) - - expect( - testComponent.containsMatchingElement( - Spinner as unknown as React.ReactElement - ) - ).toBe(true) - }) - - describe('should load data', () => { - beforeEach(() => { - mockListSyncController.mockReturnValue({ - data: { - inProgressTab: { totalItems: 5, results: [] }, - notificationTab: { totalItems: 2, results: [] }, - reviewTab: { totalItems: 3, results: [] }, - rejectTab: { totalItems: 4, results: [] }, - approvalTab: { totalItems: 0, results: [] }, - printTab: { totalItems: 1, results: [] }, - externalValidationTab: { totalItems: 6, results: [] } - } - }) - client.query = mockListSyncController - }) - it('renders page with five tabs', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.inProgress - }) - ] - } - ) - - await waitForElement(testComponent, '#navigation_progress') - await waitForElement(testComponent, '#navigation_readyForReview') - await waitForElement(testComponent, '#navigation_requiresUpdate') - await waitForElement(testComponent, '#navigation_print') - await waitForElement(testComponent, '#navigation_waitingValidation') - }) - - it('renders tabs with count', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.inProgress - }) - ] - } - ) - await flushPromises() - - const app = testComponent - await waitForElement(app, '#navigation_progress') - await waitFor(() => - app - .find('#navigation_progress') - .hostNodes() - .text() - .includes('In progress7') - ) - expect( - app.find('#navigation_readyForReview').hostNodes().text() - ).toContain('Ready for review3') - expect( - app.find('#navigation_requiresUpdate').hostNodes().text() - ).toContain('Requires update4') - expect( - app.find('#navigation_waitingValidation').hostNodes().text() - ).toContain('In external validation6') - expect(app.find('#navigation_print').hostNodes().text()).toContain( - 'Ready to print1' - ) - }) - }) - describe('shows no-record message if error there is no data', () => { - beforeEach(() => { - mockListSyncController.mockReturnValue({ - data: { - inProgressTab: { totalItems: 0, results: [] }, - notificationTab: { totalItems: 0, results: [] }, - reviewTab: { totalItems: 0, results: [] }, - rejectTab: { totalItems: 0, results: [] }, - approvalTab: { totalItems: 0, results: [] }, - printTab: { totalItems: 0, results: [] }, - externalValidationTab: { totalItems: 0, results: [] } - } - }) - client.query = mockListSyncController - }) - it('shows no-record message in inProgress drafts tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.inProgress - }) - ] - } - ) - await waitForElement(testComponent, '#no-record') - }) - it('shows no-record message in inProgress fieldagent drafts tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.inProgress, - selectorId: SELECTOR_ID.fieldAgentDrafts - }) - ] - } - ) - await waitForElement(testComponent, '#no-record') - }) - it('shows no-record message in inProgress hospital drafts tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client - } - ) - await waitForElement(testComponent, '#no-record') - }) - it('shows no-record message in review tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.readyForReview - }) - ] - } - ) - await waitForElement(testComponent, '#no-record') - }) - it('shows no-record message in reject tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.requiresUpdate - }) - ] - } - ) - await waitForElement(testComponent, '#no-record') - }) - it('shows no-record message in approval tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.sentForApproval - }) - ] - } - ) - await waitForElement(testComponent, '#no-record') - }) - it('shows no-record message in print tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.readyToPrint - }) - ] - } - ) - await waitForElement(testComponent, '#no-record') - }) - - it('shows no-record message in externalValidation tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.externalValidation - }) - ] - } - ) - - await waitForElement(testComponent, '#no-record') - }) - }) - - describe('shows error message if error occurs while querying', () => { - beforeEach(() => { - mockListSyncController.mockReturnValue({ - error: true - }) - client.query = mockListSyncController - }) - it('shows error message in inProgress fieldagent drafts tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.inProgress, - selectorId: SELECTOR_ID.fieldAgentDrafts - }) - ] - } - ) - - await waitForElement(testComponent, '#search-result-error-text-count') - }) - it('shows error message in inProgress hospital drafts tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.inProgress, - selectorId: SELECTOR_ID.hospitalDrafts - }) - ] - } - ) - - await waitForElement(testComponent, '#search-result-error-text-count') - }) - it('shows error message in review tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.readyForReview - }) - ] - } - ) - - await waitForElement(testComponent, '#search-result-error-text-count') - }) - it('shows error message in reject tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.requiresUpdate - }) - ] - } - ) - - await waitForElement(testComponent, '#search-result-error-text-count') - }) - it('shows error message in approval tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.sentForApproval - }) - ] - } - ) - - await waitForElement(testComponent, '#search-result-error-text-count') - }) - it('shows error message in print tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.readyToPrint - }) - ] - } - ) - - await waitForElement(testComponent, '#search-result-error-text-count') - }) - it('shows error message in externalValidation tab', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.externalValidation - }) - ] - } - ) - - await waitForElement(testComponent, '#search-result-error-text-count') - }) - }) - - describe('new event button should be visible when the user has the correct scopes', () => { - const build = async () => - await createTestComponent(, { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.inProgress - }) - ] - }) - - const requiredScopes = [ - SCOPES.RECORD_DECLARE_BIRTH, - SCOPES.RECORD_DECLARE_BIRTH_MY_JURISDICTION, - SCOPES.RECORD_DECLARE_DEATH, - SCOPES.RECORD_DECLARE_DEATH_MY_JURISDICTION, - SCOPES.RECORD_DECLARE_MARRIAGE, - SCOPES.RECORD_DECLARE_MARRIAGE_MY_JURISDICTION - ] as Scope[] - - const allOtherScopes = scopes.filter( - (scope) => !requiredScopes.includes(scope) - ) - const tests = [ - [[requiredScopes[0]], true], - [[requiredScopes[1]], true], - [[requiredScopes[2]], true], - [[requiredScopes[3]], true], - [[requiredScopes[4]], true], - [[requiredScopes[5]], true], - [allOtherScopes, false] - ] - tests.forEach(([scopes, visible]) => { - it(`should render when user has correct scopes ${scopes}`, async () => { - setScopes(scopes as Scope[], store) - const testComponent = await build() - - expect(testComponent.component.exists('#new_event_declaration')).toBe( - visible - ) - }) - }) - }) -}) diff --git a/packages/client/src/views/OfficeHome/inProgress/InProgress.test.tsx b/packages/client/src/views/OfficeHome/inProgress/InProgress.test.tsx deleted file mode 100644 index 8c4eee4faca..00000000000 --- a/packages/client/src/views/OfficeHome/inProgress/InProgress.test.tsx +++ /dev/null @@ -1,595 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { WORKQUEUE_TABS } from '@client/components/interface/WorkQueueTabs' -import { - createDeclaration, - DOWNLOAD_STATUS, - IDeclaration, - makeDeclarationReadyToDownload, - storeDeclaration -} from '@client/declarations' -import { DownloadAction } from '@client/forms' -import { formatUrl } from '@client/navigation' -import { - REGISTRAR_HOME_TAB, - REVIEW_EVENT_PARENT_FORM_PAGE -} from '@client/navigation/routes' -import { AppStore, createStore } from '@client/store' -import { createTestComponent, resizeWindow } from '@client/tests/util' -import { formattedDuration } from '@client/utils/date-formatting' -import { EventType } from '@client/utils/gateway' -import type { - GQLBirthEventSearchSet, - GQLDeathEventSearchSet -} from '@client/utils/gateway-deprecated-do-not-use' -import { Workqueue } from '@opencrvs/components/lib/Workqueue' -import * as React from 'react' -import { InProgress, SELECTOR_ID } from './InProgress' - -const { store } = createStore() - -describe('In Progress tab', () => { - it('redirects to different route upon selection', async () => { - const { component: app, router } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - - app.find(`#tab_${SELECTOR_ID.hospitalDrafts}`).hostNodes().simulate('click') - app.update() - expect(router.state.location.pathname).toContain( - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.inProgress, - selectorId: SELECTOR_ID.hospitalDrafts - }) - ) - app - .find(`#tab_${SELECTOR_ID.fieldAgentDrafts}`) - .hostNodes() - .simulate('click') - app.update() - expect(router.state.location.pathname).toContain( - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.inProgress, - selectorId: SELECTOR_ID.fieldAgentDrafts - }) - ) - }) - - it('renders two selectors with count for each', async () => { - const { component: app } = await createTestComponent( - {}} - />, - { store } - ) - - expect(app.find('#tab_field-agents').hostNodes().text()).toContain( - 'Field agents (5)' - ) - expect(app.find('#tab_hospitals').hostNodes().text()).toContain( - 'Hospitals (3)' - ) - }) - - describe('When the remote drafts selector is selected', () => { - it('renders all items returned from graphql query in inProgress tab', async () => { - const TIME_STAMP = '1562912635549' - const drafts: IDeclaration[] = [] - drafts.push(createDeclaration(EventType.Birth)) - const { component: testComponent } = await createTestComponent( - {}} - />, - { store } - ) - - const data = testComponent - .find(Workqueue) - .prop>>('content') - const EXPECTED_NOTIFICATION_SENT_DATE = formattedDuration( - Number(TIME_STAMP) - ) - expect(data[0].id).toBe('956281c9-1f47-4c26-948a-970dd23c4094') - expect(data[0].name).toBe('k m abdullah al amin khan') - expect(data[0].notificationSent).toBe(EXPECTED_NOTIFICATION_SENT_DATE) - expect(data[0].event).toBe('Death') - }) - - it('Should render pagination in progress tab if data is more than 10', async () => { - const drafts: IDeclaration[] = [] - drafts.push(createDeclaration(EventType.Birth)) - const { component: testComponent } = await createTestComponent( - {}} - />, - { store } - ) - - const pagiBtn = testComponent.find('#pagination_container') - - expect(pagiBtn.hostNodes()).toHaveLength(1) - testComponent - .find('#pagination button') - .last() - .hostNodes() - .simulate('click') - }) - - it('redirects to recordAudit page when item is clicked', async () => { - const TIME_STAMP = '1562912635549' - const drafts: IDeclaration[] = [] - drafts.push(createDeclaration(EventType.Birth)) - const { component: testComponent, router } = await createTestComponent( - {}} - />, - { store } - ) - - testComponent.find('#name_0').hostNodes().simulate('click') - - testComponent.update() - - expect(router.state.location.pathname).toContain( - '/record-audit/notificationTab/956281c9-1f47-4c26-948a-970dd23c4094' - ) - }) - - describe('handles download status', () => { - let store: AppStore - - const TIME_STAMP = '1562912635549' - const declarationId = 'e302f7c5-ad87-4117-91c1-35eaf2ea7be8' - const inprogressProps = { - selectorId: SELECTOR_ID.fieldAgentDrafts, - registrarLocationId: '0627c48a-c721-4ff9-bc6e-1fba59a2332a', - queryData: { - inProgressData: { - totalItems: 1, - results: [ - { - id: declarationId, - type: EventType.Birth, - registration: { - trackingId: 'BQ2IDOP', - modifiedAt: TIME_STAMP, - status: 'IN_PROGRESS' - }, - childName: [ - { - use: 'en', - firstNames: 'Anik', - familyName: 'Hoque' - } - ] - } as GQLBirthEventSearchSet - ] - }, - notificationData: {} - }, - isFieldAgent: false, - paginationId: { - fieldAgentId: 1, - healthSystemId: 1 - }, - pageSize: 10, - onPageChange: (_pageId: number) => {} - } - - beforeEach(() => { - const newStore = createStore() - store = newStore.store - }) - - it('renders download button when not downloaded', async () => { - const downloadableDeclaration = makeDeclarationReadyToDownload( - EventType.Birth, - declarationId, - DownloadAction.LOAD_REVIEW_DECLARATION - ) - downloadableDeclaration.downloadStatus = undefined - store.dispatch(storeDeclaration(downloadableDeclaration)) - const { component: testComponent } = await createTestComponent( - , - { store } - ) - - expect( - testComponent.find('#ListItemAction-0-icon').hostNodes() - ).toHaveLength(1) - }) - it('renders loading indicator when declaration is being downloaded', async () => { - const downloadableDeclaration = makeDeclarationReadyToDownload( - EventType.Birth, - declarationId, - DownloadAction.LOAD_REVIEW_DECLARATION - ) - downloadableDeclaration.downloadStatus = DOWNLOAD_STATUS.DOWNLOADING - store.dispatch(storeDeclaration(downloadableDeclaration)) - const { component: testComponent } = await createTestComponent( - , - { store } - ) - - expect( - testComponent.find('#action-loading-ListItemAction-0').hostNodes() - ).toHaveLength(1) - }) - it('renders update button when download succeeds', async () => { - const downloadableDeclaration = makeDeclarationReadyToDownload( - EventType.Birth, - declarationId, - DownloadAction.LOAD_REVIEW_DECLARATION - ) - downloadableDeclaration.downloadStatus = DOWNLOAD_STATUS.DOWNLOADED - store.dispatch(storeDeclaration(downloadableDeclaration)) - const { component: testComponent, router } = await createTestComponent( - , - { store } - ) - - expect( - testComponent.find('#ListItemAction-0-Update').hostNodes() - ).toHaveLength(1) - - testComponent - .find('#ListItemAction-0-Update') - .hostNodes() - .simulate('click') - - testComponent.update() - - expect(router.state.location.pathname).toContain( - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId, - pageId: 'review', - event: EventType.Birth - }) - ) - }) - it('renders error when download fails', async () => { - const downloadableDeclaration = makeDeclarationReadyToDownload( - EventType.Birth, - declarationId, - DownloadAction.LOAD_REVIEW_DECLARATION - ) - downloadableDeclaration.downloadStatus = DOWNLOAD_STATUS.FAILED - store.dispatch(storeDeclaration(downloadableDeclaration)) - const { component: testComponent } = await createTestComponent( - , - { store } - ) - expect( - testComponent.find('#ListItemAction-0-icon-failed').hostNodes() - ).toHaveLength(1) - }) - }) - }) - - describe('When Notification (Hospital) tab is selected', () => { - it('Should render all items returned from graphQL', async () => { - const TIME_STAMP = '1562912635549' - const birthNotificationSentDateStr = '2019-10-20T11:03:20.660Z' - const { component: testComponent } = await createTestComponent( - {}} - />, - { store } - ) - - const data = testComponent - .find(Workqueue) - .prop>>('content') - const EXPECTED_NOTIFICATION_SENT_DATE = formattedDuration( - new Date(birthNotificationSentDateStr) - ) - - expect(data[0].id).toBe('f0a1ca2c-6a14-4b9e-a627-c3e2e110587e') - expect(data[0].name).toBe('anik hoque') - expect(data[0].notificationSent).toBe(EXPECTED_NOTIFICATION_SENT_DATE) - expect(data[0].event).toBe('Birth') - }) - }) - - describe('Tablet tests', () => { - const { store } = createStore() - - beforeAll(async () => { - resizeWindow(800, 1280) - }) - - afterAll(() => { - resizeWindow(1024, 768) - }) - - it('redirects to recordAudit page if item is clicked', async () => { - const TIME_STAMP = '1562912635549' - const declarationId = 'e302f7c5-ad87-4117-91c1-35eaf2ea7be8' - - const { component: testComponent, router } = await createTestComponent( - {}} - />, - { store } - ) - - testComponent.find('#name_0').hostNodes().simulate('click') - - testComponent.update() - - expect(router.state.location.pathname).toContain( - '/record-audit/inProgressTab/e302f7c5-ad87-4117-91c1-35eaf2ea7be8' - ) - }) - }) -}) diff --git a/packages/client/src/views/OfficeHome/myDrafts/MyDrafts.test.tsx b/packages/client/src/views/OfficeHome/myDrafts/MyDrafts.test.tsx deleted file mode 100644 index b9301916a3f..00000000000 --- a/packages/client/src/views/OfficeHome/myDrafts/MyDrafts.test.tsx +++ /dev/null @@ -1,243 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { - createDeclaration, - IDeclaration, - storeDeclaration, - SUBMISSION_STATUS -} from '@client/declarations' -import { createStore } from '@client/store' -import { createTestComponent } from '@client/tests/util' -import { formattedDuration } from '@client/utils/date-formatting' -import { EventType } from '@client/utils/gateway' -import { Workqueue } from '@opencrvs/components/lib/Workqueue' -import * as React from 'react' -import { MyDrafts } from './MyDrafts' - -describe('My drafts tab', () => { - it('renders all items returned from local storage', async () => { - const { store } = createStore() - const TIME_STAMP = 1562912635549 - const drafts: IDeclaration[] = [ - { - id: 'e302f7c5-ad87-4117-91c1-35eaf2ea7be8', - data: { - registration: { - informantType: 'MOTHER', - informant: 'MOTHER_ONLY', - registrationPhone: '01722222222', - whoseContactDetails: 'MOTHER' - }, - child: { - firstNamesEng: 'Anik', - familyNameEng: 'Hoque' - } - }, - event: EventType.Birth, - submissionStatus: SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT], - modifiedOn: TIME_STAMP - }, - { - id: 'e6605607-92e0-4625-87d8-c168205bdde7', - event: EventType.Birth, - modifiedOn: TIME_STAMP, - submissionStatus: SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT], - data: { - registration: { - informantType: 'MOTHER', - informant: 'MOTHER_ONLY', - registrationPhone: '01722222222', - whoseContactDetails: 'MOTHER' - }, - child: { - firstNamesEng: 'Anik', - familyNameEng: 'Hoque' - } - } - }, - { - id: 'cc66d69c-7f0a-4047-9283-f066571830f1', - data: { - deceased: { - firstNamesEng: 'Anik', - familyNameEng: 'Hoque' - } - }, - event: EventType.Death, - submissionStatus: SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT], - modifiedOn: TIME_STAMP - }, - - { - id: '607afa75-4fb0-4785-9388-724911d62809', - data: { - deceased: { - firstNamesEng: 'Anik', - familyNameEng: 'Hoque' - } - }, - event: EventType.Death, - submissionStatus: SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT], - modifiedOn: TIME_STAMP - } - ] - const { component: testComponent } = await createTestComponent( - {}} - />, - { store } - ) - - for (const draft of drafts) { - store.dispatch(storeDeclaration(draft)) - } - - testComponent.update() - - const data = testComponent - .find(Workqueue) - .prop>>('content') - const EXPECTED_LAST_UPDATE = formattedDuration(TIME_STAMP) - - expect(data.length).toBe(drafts.length) - expect(data[0].id).toBe('e302f7c5-ad87-4117-91c1-35eaf2ea7be8') - expect(data[0].name).toBe('hoque anik') - expect(data[0].lastUpdated).toBe(EXPECTED_LAST_UPDATE) - expect(data[0].event).toBe('Birth') - expect(data[0].actions).toBeDefined() - }) - - it('Should render pagination in drafts tab if data is more than 10', async () => { - const { store } = createStore() - const { component: testComponent } = await createTestComponent( - {}} - />, - { store } - ) - - for (let i = 0; i < 12; i++) { - store.dispatch(storeDeclaration(createDeclaration(EventType.Birth))) - } - - testComponent.update() - const pagiBtn = testComponent.find('#pagination_container') - - expect(pagiBtn.hostNodes()).toHaveLength(1) - testComponent - .find('#pagination button') - .last() - .hostNodes() - .simulate('click') - }) - - it('redirects user to detail page on update click', async () => { - const { store } = createStore() - const TIME_STAMP = 1562912635549 - const drafts: IDeclaration[] = [ - { - id: 'e302f7c5-ad87-4117-91c1-35eaf2ea7be8', - event: EventType.Birth, - modifiedOn: TIME_STAMP, - submissionStatus: SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT], - data: { - registration: { - contactPoint: { - value: 'MOTHER', - nestedFields: { - registrationPhone: '01722222222' - } - } - }, - child: { - firstNamesEng: 'Anik', - firstNames: 'অনিক', - familyNameEng: 'Hoque', - familyName: 'অনিক' - } - } - }, - { - id: 'bd22s7c5-ad87-4117-91c1-35eaf2ese32bw', - event: EventType.Birth, - submissionStatus: SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT], - modifiedOn: TIME_STAMP, - data: { - child: { - familyNameEng: 'Hoque' - } - } - }, - { - id: 'cc66d69c-7f0a-4047-9283-f066571830f1', - event: EventType.Death, - modifiedOn: TIME_STAMP, - submissionStatus: SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT], - data: { - deceased: { - firstNamesEng: 'Anik', - familyNameEng: 'Hoque' - } - } - }, - { - id: 'cc66d69c-7f0a-4047-9283-f066571830f2', - event: EventType.Death, - modifiedOn: TIME_STAMP, - submissionStatus: SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT], - data: { - deceased: { - familyNameEng: 'Hoque' - } - } - }, - { - id: 'cc66d69c-7f0a-4047-9283-f066571830f4', - event: EventType.Death, - modifiedOn: TIME_STAMP + 1, - submissionStatus: SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT], - data: { - '': {} - } - } - ] - const { component: testComponent, router } = await createTestComponent( - {}} - />, - { store } - ) - - for (const draft of drafts) { - store.dispatch(storeDeclaration(draft)) - } - - testComponent.update() - - expect( - testComponent.find('#ListItemAction-0-Update').hostNodes() - ).toHaveLength(1) - - testComponent.find('#ListItemAction-0-Update').hostNodes().simulate('click') - - testComponent.update() - - expect(router.state.location.pathname).toContain( - '/drafts/cc66d69c-7f0a-4047-9283-f066571830f4' - ) - }) -}) diff --git a/packages/client/src/views/OfficeHome/outbox/Outbox.test.tsx b/packages/client/src/views/OfficeHome/outbox/Outbox.test.tsx deleted file mode 100644 index 75d8f9b3ae7..00000000000 --- a/packages/client/src/views/OfficeHome/outbox/Outbox.test.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { - createDeclaration, - storeDeclaration, - SUBMISSION_STATUS -} from '@client/declarations' -import { AppStore } from '@client/store' -import { - ALLOWED_STATUS_FOR_RETRY, - INPROGRESS_STATUS -} from '@client/SubmissionController' -import { - createTestComponent, - createTestStore, - mockDeclarationData -} from '@client/tests/util' -import { EventType } from '@client/utils/gateway' -import { ReactWrapper } from 'enzyme' -import * as React from 'react' -import { Outbox } from './Outbox' - -function storeOutboxDeclaration( - store: AppStore, - submissionStatus: SUBMISSION_STATUS -) { - const declaration = createDeclaration(EventType.Birth, mockDeclarationData) - declaration.submissionStatus = submissionStatus - store.dispatch(storeDeclaration(declaration)) -} -describe('outbox component tests', () => { - let testComponent: ReactWrapper< - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - any, - Readonly<{}>, - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - React.Component<{}, {}, any> - > - let store: AppStore - - beforeAll(async () => { - const testStore = await createTestStore() - store = testStore.store - - const { component } = await createTestComponent(, { - store - }) - - testComponent = component - }) - - describe('when there is no data', () => { - it('render fallback text', () => { - expect(testComponent.find('#no-record').hostNodes().text()).toContain( - 'No records require processing' - ) - }) - }) - - describe('when there declarations with different submission statuses', () => { - const outboxSubmissionStatuses = [ - ...ALLOWED_STATUS_FOR_RETRY, - ...INPROGRESS_STATUS - ] - beforeAll(() => { - outboxSubmissionStatuses.forEach((status) => - storeOutboxDeclaration(store, status) - ) - testComponent.update() - }) - it('renders all declarations', () => { - expect( - testComponent - .find(`#row_${outboxSubmissionStatuses.length - 1}`) - .hostNodes() - ).toHaveLength(1) - }) - }) -}) diff --git a/packages/client/src/views/OfficeHome/readyForReview/readyForReview.test.tsx b/packages/client/src/views/OfficeHome/readyForReview/readyForReview.test.tsx deleted file mode 100644 index 483c9446526..00000000000 --- a/packages/client/src/views/OfficeHome/readyForReview/readyForReview.test.tsx +++ /dev/null @@ -1,817 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { - DOWNLOAD_STATUS, - makeDeclarationReadyToDownload, - storeDeclaration, - modifyDeclaration -} from '@client/declarations' -import { DownloadAction } from '@client/forms' -import { EventType } from '@client/utils/gateway' -import { queries } from '@client/profile/queries' -import { createStore } from '@client/store' -import { - createTestComponent, - mockUserResponse, - REGISTRAR_DEFAULT_SCOPES, - resizeWindow, - setScopes, - TestComponentWithRouteMock -} from '@client/tests/util' -import { waitForElement, waitFor } from '@client/tests/wait-for-element' -import { createClient } from '@client/utils/apolloClient' -import { REGISTRATION_HOME_QUERY } from '@client/views/OfficeHome/queries' -import { OfficeHome } from '@client/views/OfficeHome/OfficeHome' -import { EVENT_STATUS } from '@client/workqueue' -import { Workqueue } from '@opencrvs/components/lib/Workqueue' -import { ApolloClient } from '@apollo/client' -import { merge } from 'lodash' -import * as React from 'react' -import { ReadyForReview } from './ReadyForReview' -import type { - GQLBirthEventSearchSet, - GQLDeathEventSearchSet -} from '@client/utils/gateway-deprecated-do-not-use' -import { formattedDuration } from '@client/utils/date-formatting' -import { birthDeclarationForReview } from '@client/tests/mock-graphql-responses' -import { vi, Mock } from 'vitest' - -const nameObj = { - data: { - getUser: { - name: [ - { - use: 'en', - firstNames: 'Mohammad', - familyName: 'Ashraful', - __typename: 'HumanName' - }, - { use: 'bn', firstNames: '', familyName: '', __typename: 'HumanName' } - ], - role: 'DISTRICT_REGISTRAR' - } - } -} - -const mockListSyncController = vi.fn() - -const mockSearchData = { - id: 'e302f7c5-ad87-4117-91c1-35eaf2ea7be8', - type: EventType.Birth, - registration: { - status: 'DECLARED', - contactNumber: '01622688231', - trackingId: 'BW0UTHR', - registrationNumber: null, - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1', - duplicates: null, - createdAt: '2018-05-23T14:44:58+02:00', - modifiedAt: '2018-05-23T14:44:58+02:00' - }, - dateOfBirth: '2010-10-10', - childName: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ], - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - child: { - id: 'FAKE_ID', - name: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ], - birthDate: '2010-10-10' - }, - deceased: { - name: [ - { - use: '', - firstNames: '', - familyName: '' - } - ], - deceased: { - deathDate: '' - } - }, - informant: { - individual: { - telecom: [ - { - system: '', - use: '', - value: '' - } - ] - } - }, - dateOfDeath: null, - deceasedName: null, - createdAt: '2018-05-23T14:44:58+02:00' -} -/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ -const searchData: any = [] -for (let i = 0; i < 14; i++) { - searchData.push(mockSearchData) -} -merge(mockUserResponse, nameObj) - -const mockDeclarationDateStr = '2019-10-20T11:03:20.660Z' -const mockReviewTabData = { - totalItems: 2, - results: [ - { - id: '9a55d213-ad9f-4dcd-9418-340f3a7f6269', - type: EventType.Birth, - registration: { - status: 'DECLARED', - contactNumber: '01622688231', - trackingId: 'BW0UTHR', - registrationNumber: undefined, - eventLocationId: undefined, - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1', - createdAt: '1544188309380', - modifiedAt: '1544188309380' - }, - operationHistories: [ - { - operationType: 'DECLARED', - operatedOn: mockDeclarationDateStr, - operatorRole: 'LOCAL_REGISTRAR', - operatorName: [ - { - firstNames: 'Mohammad', - familyName: 'Ashraful', - use: 'en' - }, - { - firstNames: '', - familyName: '', - use: 'bn' - } - ], - operatorOfficeName: 'Alokbali Union Parishad', - operatorOfficeAlias: ['আলোকবালী ইউনিয়ন পরিষদ'] - } - ], - dateOfBirth: '2010-10-10', - childName: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ] - } as GQLBirthEventSearchSet, - { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e95', - type: EventType.Death, - registration: { - status: 'VALIDATED', - trackingId: 'DW0UTHR', - registrationNumber: undefined, - eventLocationId: undefined, - contactNumber: undefined, - duplicates: ['308c35b4-04f8-4664-83f5-9790e790cd33'], - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1', - createdAt: '1544188309380', - modifiedAt: '1544188309380' - }, - dateOfDeath: '2007-01-01', - deceasedName: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ] - } as GQLDeathEventSearchSet - ] -} - -describe('OfficeHome sent for review tab related tests', () => { - let store: ReturnType['store'] - let apolloClient: ApolloClient<{}> - - beforeEach(async () => { - ;(queries.fetchUserDetails as Mock).mockReturnValue(mockUserResponse) - const createdStore = createStore() - store = createdStore.store - - apolloClient = createClient(store) - - setScopes(REGISTRAR_DEFAULT_SCOPES, store) - }) - - it('should show pagination bar if items more than 11 in ReviewTab', async () => { - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - - const pagination = await waitForElement( - testComponent, - '#pagination_container' - ) - - expect(pagination.hostNodes()).toHaveLength(1) - - testComponent - .find('#pagination button') - .last() - .hostNodes() - .simulate('click') - expect(testComponent.exists('#page-number-2')).toBeTruthy() - }) - - it('renders all items returned from graphql query in ready for review', async () => { - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - - const workqueue = await waitForElement(testComponent, Workqueue) - - const data = workqueue.prop>>('content') - const EXPECTED_DATE_OF_DECLARATION = formattedDuration( - new Date(mockDeclarationDateStr) - ) - - expect(data.length).toBe(2) - expect(data[0].id).toBe('9a55d213-ad9f-4dcd-9418-340f3a7f6269') - expect(data[0].dateOfEvent).toBe('8 years ago') - expect(data[0].sentForReview).toBe(EXPECTED_DATE_OF_DECLARATION) - expect(data[0].name).toBe('khan iliyas') - expect(data[0].trackingId).toBe('BW0UTHR') - expect(data[0].event).toBe('Birth') - expect(data[0].actions).toBeDefined() - }) - - it('returns an empty array incase of invalid graphql query response', async () => { - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - - const workqueue = await waitForElement(testComponent, Workqueue) - const data = workqueue.prop>>('content') - expect(data.length).toBe(0) - }) - - it('redirects to recordAudit page if row is clicked', async () => { - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent, router } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - const element = await waitForElement(testComponent, '#name_0') - element.hostNodes().simulate('click') - - await waitFor(() => - router.state.location.pathname.includes( - '/record-audit/reviewTab/e302f7c5-ad87-4117-91c1-35eaf2ea7be8' - ) - ) - }) - - describe('handles download status', () => { - let testComponent: TestComponentWithRouteMock - let createdTestComponent: TestComponentWithRouteMock - beforeEach(async () => { - Date.now = vi.fn(() => 1554055200000) - - mockListSyncController - .mockReturnValueOnce({ - data: { - inProgressTab: { totalItems: 0, results: [] }, - notificationTab: { totalItems: 0, results: [] }, - reviewTab: mockReviewTabData, - rejectTab: { totalItems: 0, results: [] }, - approvalTab: { totalItems: 0, results: [] }, - printTab: { totalItems: 0, results: [] }, - externalValidationTab: { totalItems: 0, results: [] } - }, - initialSyncDone: true - }) - .mockReturnValueOnce({ - data: { - fetchBirthRegistration: birthDeclarationForReview - } - }) - apolloClient.query = mockListSyncController - - createdTestComponent = await createTestComponent(, { - store, - apolloClient - }) - - testComponent = createdTestComponent - }) - //TODO:: FAILED TEST - it.skip('downloads declaration after clicking download button', async () => { - await waitForElement(testComponent.component, '#ListItemAction-0-icon') - testComponent.component - .find('#ListItemAction-0-icon') - .hostNodes() - .simulate('click') - testComponent.component.update() - expect( - testComponent.component.find('#assignment').hostNodes() - ).toHaveLength(1) - - testComponent.component.find('#assign').hostNodes().simulate('click') - - expect( - testComponent.component - .find('#action-loading-ListItemAction-0') - .hostNodes() - ).toHaveLength(1) - - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - testComponent.component.update() - - const action = await waitForElement( - testComponent.component, - '#ListItemAction-0-Review' - ) - action.hostNodes().simulate('click') - - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - testComponent.component.update() - expect(testComponent.router.state.location.pathname).toBe( - '/reviews/9a55d213-ad9f-4dcd-9418-340f3a7f6269/events/birth/parent/review' - ) - }) - //TODO:: FAILED TEST - it.skip('shows error when download is failed', async () => { - const downloadedDeclaration = makeDeclarationReadyToDownload( - EventType.Death, - 'bc09200d-0160-43b4-9e2b-5b9e90424e95', - DownloadAction.LOAD_REVIEW_DECLARATION - ) - downloadedDeclaration.downloadStatus = DOWNLOAD_STATUS.FAILED - store.dispatch(storeDeclaration(downloadedDeclaration)) - - testComponent.component.update() - - expect( - testComponent.component - .find('#ListItemAction-1-icon-failed') - .hostNodes() - ).toHaveLength(1) - }) - }) - - it('check the validate icon', async () => { - const TIME_STAMP = '1544188309380' - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - - const props = testComponent.find('#declaration_icon').first().props().color - expect(props).toBe('grey') - }) - - describe.skip('handles download status for possible duplicate declaration', () => { - let testComponent: TestComponentWithRouteMock - let createdTestComponent: TestComponentWithRouteMock - beforeAll(async () => { - Date.now = vi.fn(() => 1554055200000) - const graphqlMocks = [ - { - request: { - query: REGISTRATION_HOME_QUERY, - variables: { - declarationLocationId: '2a83cf14-b959-47f4-8097-f75a75d1867f', - count: 10, - reviewStatuses: [EVENT_STATUS.DECLARED, EVENT_STATUS.VALIDATED], - inProgressSkip: 0, - reviewSkip: 0, - rejectSkip: 0, - approvalSkip: 0, - externalValidationSkip: 0, - printSkip: 0 - } - }, - result: { - data: { - inProgressTab: { totalItems: 0, results: [] }, - notificationTab: { totalItems: 0, results: [] }, - reviewTab: mockReviewTabData, - rejectTab: { totalItems: 0, results: [] }, - approvalTab: { totalItems: 0, results: [] }, - externalValidationTab: { totalItems: 0, results: [] }, - printTab: { totalItems: 0, results: [] } - } - } - } - ] - - createdTestComponent = await createTestComponent( - // @ts-ignore - , - { store, graphqlMocks } - ) - - setScopes(REGISTRAR_DEFAULT_SCOPES, store) - testComponent = createdTestComponent - }) - - it('starts downloading after clicking download button', async () => { - const downloadButton = await waitForElement( - testComponent.component, - '#ListItemAction-1-icon' - ) - - downloadButton.hostNodes().simulate('click') - testComponent.component.update() - - expect( - testComponent.component - .find('#action-loading-ListItemAction-1') - .hostNodes() - ).toHaveLength(1) - }) - - it('shows review button when download is complete', async () => { - const downloadedDeclaration = makeDeclarationReadyToDownload( - EventType.Death, - 'bc09200d-0160-43b4-9e2b-5b9e90424e95', - DownloadAction.LOAD_REVIEW_DECLARATION - ) - downloadedDeclaration.downloadStatus = DOWNLOAD_STATUS.DOWNLOADED - store.dispatch(modifyDeclaration(downloadedDeclaration)) - - const action = await waitForElement( - testComponent.component, - '#ListItemAction-1-Review' - ) - - expect(action.hostNodes()).toHaveLength(1) - action.hostNodes().simulate('click') - - await waitFor(() => - testComponent.router.state.location.pathname.includes( - '/duplicates/bc09200d-0160-43b4-9e2b-5b9e90424e95' - ) - ) - }) - - it('shows error when download is failed', async () => { - const downloadedDeclaration = makeDeclarationReadyToDownload( - EventType.Death, - 'bc09200d-0160-43b4-9e2b-5b9e90424e95', - DownloadAction.LOAD_REVIEW_DECLARATION - ) - downloadedDeclaration.downloadStatus = DOWNLOAD_STATUS.FAILED - store.dispatch(modifyDeclaration(downloadedDeclaration)) - - testComponent.component.update() - - const errorIcon = await waitForElement( - testComponent.component, - '#ListItemAction-1-download-failed' - ) - - expect(errorIcon.hostNodes()).toHaveLength(1) - }) - }) -}) - -describe('Tablet tests', () => { - let { store } = createStore() - - beforeAll(async () => { - const s = createStore() - store = s.store - - resizeWindow(800, 1280) - }) - - afterEach(() => { - resizeWindow(1024, 768) - }) - - it('redirects to recordAudit page if item is clicked', async () => { - const TIME_STAMP = '1544188309380' - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent, router } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - - setScopes(REGISTRAR_DEFAULT_SCOPES, store) - - const row = await waitForElement(testComponent, '#name_0') - row.hostNodes().simulate('click') - - expect(router.state.location.pathname).toContain( - '/record-audit/reviewTab/e302f7c5-ad87-4117-91c1-35eaf2ea7be8' - ) - }) -}) diff --git a/packages/client/src/views/OfficeHome/readyToPrint/readyToPrint.test.tsx b/packages/client/src/views/OfficeHome/readyToPrint/readyToPrint.test.tsx deleted file mode 100644 index 6134a3d68bd..00000000000 --- a/packages/client/src/views/OfficeHome/readyToPrint/readyToPrint.test.tsx +++ /dev/null @@ -1,806 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { - DOWNLOAD_STATUS, - makeDeclarationReadyToDownload, - storeDeclaration -} from '@client/declarations' -import { DownloadAction } from '@client/forms' -import { EventType } from '@client/utils/gateway' -import { queries } from '@client/profile/queries' -import { storage } from '@client/storage' -import { createStore } from '@client/store' -import { - createTestComponent, - mockUserResponse, - REGISTRAR_DEFAULT_SCOPES, - resizeWindow, - setScopes, - TestComponentWithRouteMock -} from '@client/tests/util' -import { waitForElement } from '@client/tests/wait-for-element' -import { createClient } from '@client/utils/apolloClient' -import { OfficeHome } from '@client/views/OfficeHome/OfficeHome' -import { Workqueue } from '@opencrvs/components/lib/Workqueue' -import { merge } from 'lodash' -import * as React from 'react' -import { ReadyToPrint } from './ReadyToPrint' -import type { - GQLBirthEventSearchSet, - GQLDeathEventSearchSet -} from '@client/utils/gateway-deprecated-do-not-use' -import { formattedDuration } from '@client/utils/date-formatting' -import { vi } from 'vitest' -import { formatUrl } from '@client/navigation' -import { REGISTRAR_HOME_TAB } from '@client/navigation/routes' - -const mockFetchUserDetails = vi.fn() -const mockListSyncController = vi.fn() - -const nameObj = { - data: { - getUser: { - name: [ - { - use: 'en', - firstNames: 'Mohammad', - familyName: 'Ashraful', - __typename: 'HumanName' - }, - { use: 'bn', firstNames: '', familyName: '', __typename: 'HumanName' } - ], - role: { - _id: '778464c0-08f8-4fb7-8a37-b86d1efc462a', - labels: [ - { - lang: 'en', - label: 'DISTRICT_REGISTRAR' - } - ] - } - } - } -} - -const mockUserData = { - id: '956281c9-1f47-4c26-948a-970dd23c4094', - type: EventType.Birth, - registration: { - status: 'REGISTERED', - contactNumber: '01622688231', - trackingId: 'BW0UTHR', - registrationNumber: '20190203323443BW0UTHR', - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1', - duplicates: null, - createdAt: '2018-05-23T14:44:58+02:00', - modifiedAt: '2018-05-23T14:44:58+02:00' - }, - dateOfBirth: '2010-10-10', - childName: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ], - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - child: { - id: 'FAKE_ID', - name: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ], - birthDate: '2010-10-10' - }, - deceased: { - name: [ - { - use: '', - firstNames: '', - familyName: '' - } - ], - deceased: { - deathDate: '' - } - }, - informant: { - individual: { - telecom: [ - { - system: '', - use: '', - value: '' - } - ] - } - }, - dateOfDeath: null, - deceasedName: null, - createdAt: '2018-05-23T14:44:58+02:00' -} -/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ -const userData: any = [] -for (let i = 0; i < 14; i++) { - userData.push(mockUserData) -} -merge(mockUserResponse, nameObj) -mockFetchUserDetails.mockReturnValue(mockUserResponse) -queries.fetchUserDetails = mockFetchUserDetails - -const mockPrintTabData = { - totalItems: 2, - results: [ - { - id: '956281c9-1f47-4c26-948a-970dd23c4094', - type: EventType.Death, - registration: { - status: 'REGISTERED', - contactNumber: undefined, - trackingId: 'DG6PECX', - registrationNumber: '20196816020000113', - eventLocationId: undefined, - registeredLocationId: 'd8cfd240-4b5a-4557-9df7-b1591a11d843', - duplicates: [null], - createdAt: '1574696143372', - modifiedAt: undefined - }, - operationHistories: [ - { - operationType: 'REGISTERED', - operatedOn: '2019-10-20T11:03:20.660Z', - operatorRole: 'LOCAL_REGISTRAR', - operatorName: [ - { - firstNames: 'Mohammad', - familyName: 'Ashraful', - use: 'en' - }, - { - firstNames: '', - familyName: null, - use: 'bn' - } - ], - operatorOfficeName: 'Alokbali Union Parishad', - operatorOfficeAlias: ['আলোকবালী ইউনিয়ন পরিষদ'] - } - ], - dateOfDeath: '2019-01-18', - deceasedName: [ - { - use: 'bn', - firstNames: 'ক ম আব্দুল্লাহ আল আমিন ', - familyName: 'খান' - }, - { - use: 'en', - firstNames: 'K M Abdullah al amin', - familyName: 'Khan' - } - ] - } as GQLDeathEventSearchSet, - { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e95', - type: EventType.Death, - registration: { - status: 'REGISTERED', - trackingId: 'DW0UTHR', - registrationNumber: '2019333494B8I0NEB9', - eventLocationId: undefined, - contactNumber: '01622688231', - duplicates: ['308c35b4-04f8-4664-83f5-9790e790cd33'], - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1', - createdAt: '2007-01-01', - modifiedAt: '2007-01-01' - }, - dateOfDeath: '2007-01-01', - deceasedName: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ] - } as GQLDeathEventSearchSet - ] -} - -storage.getItem = vi.fn() -storage.setItem = vi.fn() - -describe('RegistrarHome ready to print tab related tests', () => { - const { store } = createStore() - const client = createClient(store) - - beforeAll(async () => { - setScopes(REGISTRAR_DEFAULT_SCOPES, store) - }) - - it('renders all items returned from graphql query in ready for print', async () => { - const TIME_STAMP = '1544188309380' - Date.now = vi.fn(() => 1554055200000) - - const birthEventRegisteredDate = '2019-10-20T11:03:20.660Z' - const birthEventSearchSet: GQLBirthEventSearchSet = { - id: '956281c9-1f47-4c26-948a-970dd23c4094', - type: EventType.Birth, - registration: { - status: 'REGISTERED', - contactNumber: '01622688231', - trackingId: 'BW0UTHR', - registrationNumber: '2019333494BBONT7U7', - eventLocationId: undefined, - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1', - duplicates: [null], - createdAt: TIME_STAMP, - modifiedAt: TIME_STAMP - }, - operationHistories: [ - { - operationType: 'REGISTERED', - operatedOn: birthEventRegisteredDate, - operatorRole: 'LOCAL_REGISTRAR', - operatorName: [ - { - firstNames: 'Mohammad', - familyName: 'Ashraful', - use: 'en' - }, - { - firstNames: '', - familyName: '', - use: 'bn' - } - ], - operatorOfficeName: 'Alokbali Union Parishad', - operatorOfficeAlias: ['আলোকবালী ইউনিয়ন পরিষদ'] - } - ], - dateOfBirth: '2010-10-10', - childName: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ] - } - - const deathEventSearchSet: GQLDeathEventSearchSet = { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e95', - type: EventType.Death, - registration: { - status: 'REGISTERED', - trackingId: 'DW0UTHR', - registrationNumber: '2019333494B8I0NEB9', - eventLocationId: undefined, - contactNumber: '01622688231', - duplicates: ['308c35b4-04f8-4664-83f5-9790e790cd33'], - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1', - createdAt: TIME_STAMP, - modifiedAt: undefined - }, - dateOfDeath: '2007-01-01', - deceasedName: [ - { - firstNames: 'Zayed', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ] - } - - const { component: testComponent } = await createTestComponent( - // @ts-ignore - , - { store } - ) - - const element = await waitForElement(testComponent, Workqueue) - const data = element.prop('content') - const EXPECTED_DATE_OF_DECLARATION = formattedDuration( - new Date(birthEventRegisteredDate) - ) - - expect(data.length).toBe(2) - expect(data[0].id).toBe('956281c9-1f47-4c26-948a-970dd23c4094') - expect(data[0].registered).toBe(EXPECTED_DATE_OF_DECLARATION) - expect(data[0].trackingId).toBe('BW0UTHR') - expect(data[0].event).toBe('Birth') - expect(data[0].actions).toBeDefined() - }) - - it('returns an empty array incase of invalid graphql query response', async () => { - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent } = await createTestComponent( - // @ts-ignore - , - { store } - ) - - testComponent.update() - const data = testComponent.find(Workqueue).prop('content') - expect(data.length).toBe(0) - }) - - it('should show pagination bar if items are more than 11 in ready for print tab', async () => { - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - - const element = await waitForElement(testComponent, '#pagination_container') - - expect(element.hostNodes()).toHaveLength(1) - - testComponent - .find('#pagination button') - .last() - .hostNodes() - .simulate('click') - - expect(testComponent.exists('#page-number-2')).toBeTruthy() - }) - - describe('When a row is clicked', () => { - it('renders expanded area for ready to print', async () => { - const { component: testComponent, router } = await createTestComponent( - // @ts-ignore - , - { store } - ) - - // wait for mocked data to load mockedProvider - // after sorting (by default name) row's order will be changed - await waitForElement(testComponent, '#name_0') - - testComponent.update() - testComponent.find('#name_0').hostNodes().simulate('click') - - await waitForElement(testComponent, '#name_0') - testComponent.update() - - expect(router.state.location.pathname).toContain( - '/record-audit/printTab/956281c9-1f47-4c26-948a-970dd23c4094' - ) - }) - }) - - describe('handles download status', () => { - let testComponent: TestComponentWithRouteMock - - beforeEach(async () => { - Date.now = vi.fn(() => 1554055200000) - mockListSyncController - .mockReturnValueOnce({ - data: { - inProgressTab: { totalItems: 0, results: [] }, - notificationTab: { totalItems: 0, results: [] }, - reviewTab: { totalItems: 0, results: [] }, - rejectTab: { totalItems: 0, results: [] }, - approvalTab: { totalItems: 0, results: [] }, - printTab: mockPrintTabData, - externalValidationTab: { totalItems: 0, results: [] } - } - }) - .mockReturnValueOnce({ - data: { - fetchDeathRegistration: { - _fhirIDMap: { - composition: '956281c9-1f47-4c26-948a-970dd23c4094' - }, - id: '956281c9-1f47-4c26-948a-970dd23c4094', - deceased: { - id: 'a6cce2e1-10df-42d0-bbc9-8e037b0bf14e', - name: [ - { - use: 'bn', - firstNames: 'ক ম আব্দুল্লাহ আল আমিন ', - familyName: 'খান' - }, - { - use: 'en', - firstNames: 'K M Abdullah al amin', - familyName: 'Khan' - } - ], - birthDate: '1988-06-16', - gender: 'male', - maritalStatus: 'MARRIED', - nationality: ['BGD'], - identifier: [ - { - id: '1020617910288', - type: 'NATIONAL_ID', - otherType: null - } - ], - deceased: { - deathDate: '2019-01-18' - }, - address: [ - { - type: 'PRIMARY_ADDRESS', - line: [ - '40 Ward', - '', - 'Bahadur street', - 'f4d236c5-6328-4e8e-a45b-e307720b7cdf', - '', - '2612765c-f5a7-4291-9191-7625dd76fa82' - ], - district: '18dd420e-daec-4d35-9a44-fb58b5185923', - state: 'e93b10bc-1318-4dcb-b8b6-35c7532a0a90', - city: null, - postalCode: '1024', - country: 'BGD' - }, - { - type: 'SECONDARY_ADDRESS', - line: [ - '40', - '', - 'My street', - 'f4d236c5-6328-4e8e-a45b-e307720b7cdf', - '', - '2612765c-f5a7-4291-9191-7625dd76fa82' - ], - district: '18dd420e-daec-4d35-9a44-fb58b5185923', - state: 'e93b10bc-1318-4dcb-b8b6-35c7532a0a90', - city: null, - postalCode: '1024', - country: 'BGD' - } - ] - }, - informant: { - id: 'c7e17721-bccf-4dfb-8f85-d6311d1da1bc', - relationship: 'OTHER', - otherRelationship: 'Friend', - individual: { - id: '7ac8d0a6-a391-42f9-add4-dec27279474d', - identifier: [ - { - id: '1020607917288', - type: 'NATIONAL_ID', - otherType: null - } - ], - name: [ - { - use: 'bn', - firstNames: 'জামাল উদ্দিন খান', - familyName: 'খান' - }, - { - use: 'en', - firstNames: 'Jamal Uddin Khan', - familyName: 'Khan' - } - ], - nationality: ['BGD'], - birthDate: '1956-10-17', - telecom: null, - address: [ - { - type: 'SECONDARY_ADDRESS', - line: [ - '48', - '', - 'My street', - 'f4d236c5-6328-4e8e-a45b-e307720b7cdf', - '', - '2612765c-f5a7-4291-9191-7625dd76fa82' - ], - district: '18dd420e-daec-4d35-9a44-fb58b5185923', - state: 'e93b10bc-1318-4dcb-b8b6-35c7532a0a90', - city: null, - postalCode: '1024', - country: 'BGD' - }, - { - type: 'PRIMARY_ADDRESS', - line: [ - '40 Ward', - '', - 'Bahadur street', - 'f4d236c5-6328-4e8e-a45b-e307720b7cdf', - '', - '2612765c-f5a7-4291-9191-7625dd76fa82' - ], - district: '18dd420e-daec-4d35-9a44-fb58b5185923', - state: 'e93b10bc-1318-4dcb-b8b6-35c7532a0a90', - city: null, - postalCode: '1024', - country: 'BGD' - } - ] - } - }, - father: { - id: '7ac8d0a6-a391-42f9-add4-dec27279589', - name: [ - { - use: 'bn', - firstNames: 'মোক্তার', - familyName: 'আলী' - }, - { - use: 'en', - firstNames: 'Moktar', - familyName: 'Ali' - } - ] - }, - mother: { - id: '7ac8d0a6-a391-42f9-add4-decds589', - name: [ - { - use: 'bn', - firstNames: 'মরিউম', - familyName: 'আলী' - }, - { - use: 'en', - firstNames: 'Morium', - familyName: 'Ali' - } - ] - }, - spouse: { - id: '7ac8d0a6-a391-42f9-add4-sssdec27279589', - name: [ - { - use: 'bn', - firstNames: 'রেহানা', - familyName: 'আলী' - }, - { - use: 'en', - firstNames: 'Rehana', - familyName: 'Ali' - } - ] - }, - registration: { - id: 'ba1cb210-b98f-46e1-a185-4c8df2971064', - contact: 'OTHER', - contactRelationship: 'Colleague', - contactPhoneNumber: '+8801678945638', - attachments: null, - status: [ - { - comments: null, - type: 'REGISTERED', - location: { - name: 'Alokbali', - alias: ['আলো্কবালী'] - }, - office: { - name: 'Alokbali Union Parishad', - alias: ['আলোকবালী ইউনিয়ন পরিষদ'], - address: { - district: 'Narsingdi', - state: 'Dhaka' - }, - partOf: 'Location/8cbc862a-b817-4c29-a490-4a8767ff023c' - }, - timestamp: null - }, - { - comments: null, - type: 'DECLARED', - location: { - name: 'Alokbali', - alias: ['আলো্কবালী'] - }, - office: { - name: 'Alokbali Union Parishad', - alias: ['আলোকবালী ইউনিয়ন পরিষদ'], - address: { - district: 'Narsingdi', - state: 'Dhaka' - }, - partOf: 'Location/8cbc862a-b817-4c29-a490-4a8767ff023c' - }, - timestamp: null - } - ], - type: 'DEATH', - trackingId: 'DG6PECX', - registrationNumber: '20196816020000113' - }, - eventLocation: { - id: '7a503721-a258-49ef-9fb9-aa77c970d19b', - type: 'PRIVATE_HOME', - address: { - type: null, - line: [ - '40', - '', - 'My street', - 'f4d236c5-6328-4e8e-a45b-e307720b7cdf', - '', - '2612765c-f5a7-4291-9191-7625dd76fa82' - ], - district: '18dd420e-daec-4d35-9a44-fb58b5185923', - state: 'e93b10bc-1318-4dcb-b8b6-35c7532a0a90', - city: null, - postalCode: '1024', - country: 'BGD' - } - }, - mannerOfDeath: 'HOMICIDE', - causeOfDeathMethod: null, - causeOfDeath: 'Chronic Obstructive Pulmonary Disease' - } - } - }) - client.query = mockListSyncController - - testComponent = await createTestComponent(, { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB, - initialEntries: [formatUrl(REGISTRAR_HOME_TAB, { tabId: 'print' })] - }) - }) - - it('downloads declaration after clicking download button', async () => { - testComponent.component.update() - const downloadButton = await waitForElement( - testComponent.component, - '#ListItemAction-0-icon' - ) - - downloadButton.hostNodes().simulate('click') - - testComponent.component.update() - - expect( - testComponent.component.find('#assignment').hostNodes() - ).toHaveLength(1) - - testComponent.component.find('#assign').hostNodes().simulate('click') - - const action = await waitForElement( - testComponent.component, - '#ListItemAction-0-Print' - ) - - action.hostNodes().simulate('click') - - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - - testComponent.component.update() - expect(testComponent.router.state.location.pathname).toBe( - '/cert/collector/956281c9-1f47-4c26-948a-970dd23c4094/death/certCollector' - ) - }) - - it('shows error when download is failed', async () => { - const downloadedDeclaration = makeDeclarationReadyToDownload( - EventType.Death, - 'bc09200d-0160-43b4-9e2b-5b9e90424e95', - DownloadAction.LOAD_CERTIFICATE_DECLARATION - ) - downloadedDeclaration.downloadStatus = DOWNLOAD_STATUS.FAILED - store.dispatch(storeDeclaration(downloadedDeclaration)) - - testComponent.component.update() - - const errorIcon = await waitForElement( - testComponent.component, - '#ListItemAction-1-icon-failed' - ) - expect(errorIcon.hostNodes()).toHaveLength(1) - }) - }) -}) - -describe('Tablet tests', () => { - const { store } = createStore() - - beforeAll(async () => { - setScopes(REGISTRAR_DEFAULT_SCOPES, store) - resizeWindow(800, 1280) - }) - - afterEach(() => { - resizeWindow(1024, 768) - }) - - it('redirects to recordAudit page if item is clicked', async () => { - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent, router } = await createTestComponent( - // @ts-ignore - , - { store } - ) - - testComponent.update() - const element = await waitForElement(testComponent, '#name_0') - element.hostNodes().simulate('click') - - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - testComponent.update() - - expect(router.state.location.pathname).toContain( - '/record-audit/printTab/956281c9-1f47-4c26-948a-970dd23c4094' - ) - }) -}) diff --git a/packages/client/src/views/OfficeHome/requiresUpdate/requiresUpdate.test.tsx b/packages/client/src/views/OfficeHome/requiresUpdate/requiresUpdate.test.tsx deleted file mode 100644 index 380d9667bd4..00000000000 --- a/packages/client/src/views/OfficeHome/requiresUpdate/requiresUpdate.test.tsx +++ /dev/null @@ -1,585 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { - DOWNLOAD_STATUS, - makeDeclarationReadyToDownload, - storeDeclaration -} from '@client/declarations' -import { DownloadAction } from '@client/forms' -import { EventType } from '@client/utils/gateway' -import { queries } from '@client/profile/queries' -import { storage } from '@client/storage' -import { createStore } from '@client/store' -import { - createTestComponent, - mockUserResponse, - resizeWindow, - REGISTRATION_AGENT_DEFAULT_SCOPES, - setScopes, - REGISTRAR_DEFAULT_SCOPES, - TestComponentWithRouteMock -} from '@client/tests/util' -import { waitForElement } from '@client/tests/wait-for-element' -import { createClient } from '@client/utils/apolloClient' -import { OfficeHome } from '@client/views/OfficeHome/OfficeHome' -import { Workqueue } from '@opencrvs/components/lib/Workqueue' -import { merge } from 'lodash' -import * as React from 'react' -import { RequiresUpdate } from './RequiresUpdate' -import type { - GQLBirthEventSearchSet, - GQLDeathEventSearchSet -} from '@client/utils/gateway-deprecated-do-not-use' -import { formattedDuration } from '@client/utils/date-formatting' -import { WORKQUEUE_TABS } from '@client/components/interface/WorkQueueTabs' -import { birthDeclarationForReview } from '@client/tests/mock-graphql-responses' -import { vi } from 'vitest' -import { formatUrl } from '@client/navigation' -import { REGISTRAR_HOME_TAB_PAGE } from '@client/navigation/routes' - -const mockFetchUserDetails = vi.fn() -const mockListSyncController = vi.fn() - -const nameObj = { - data: { - getUser: { - name: [ - { - use: 'en', - firstNames: 'Mohammad', - familyName: 'Ashraful', - __typename: 'HumanName' - }, - { use: 'bn', firstNames: '', familyName: '', __typename: 'HumanName' } - ], - role: { - _id: '778464c0-08f8-4fb7-8a37-b86d1efc462a', - labels: [ - { - lang: 'en', - label: 'DISTRICT_REGISTRAR' - } - ] - } - } - } -} - -const TIME_STAMP = '1544188309380' - -const mockUserData = { - id: 'e302f7c5-ad87-4117-91c1-35eaf2ea7be8', - type: EventType.Birth, - registration: { - status: 'REJECTED', - contactNumber: '01622688231', - trackingId: 'BW0UTHR', - registrationNumber: null, - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1', - duplicates: null, - createdAt: TIME_STAMP, - modifiedAt: TIME_STAMP - }, - dateOfBirth: '2010-10-10', - childName: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ], - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - child: { - id: 'FAKE_ID', - name: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ], - birthDate: '2010-10-10' - }, - deceased: { - name: [ - { - use: '', - firstNames: '', - familyName: '' - } - ], - deceased: { - deathDate: '' - } - }, - informant: { - individual: { - telecom: [ - { - system: '', - use: '', - value: '' - } - ] - } - }, - dateOfDeath: null, - deceasedName: null, - createdAt: '2018-05-23T14:44:58+02:00' -} -/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ -const userData: any = [] -for (let i = 0; i < 14; i++) { - userData.push(mockUserData) -} -merge(mockUserResponse, nameObj) -mockFetchUserDetails.mockReturnValue(mockUserResponse) -queries.fetchUserDetails = mockFetchUserDetails - -storage.getItem = vi.fn() -storage.setItem = vi.fn() - -describe('OfficeHome sent for update tab related tests', () => { - const { store } = createStore() - const client = createClient(store) - - beforeAll(async () => { - setScopes(REGISTRATION_AGENT_DEFAULT_SCOPES, store) - }) - - it('renders all items returned from graphql query in sent for update tab', async () => { - const TIME_STAMP = '1544188309380' - - const birthEventRejectedDate = '2019-10-20T11:03:20.660Z' - - const { component: testComponent } = await createTestComponent( - // @ts-ignore - , - { store } - ) - - const table = await waitForElement(testComponent, Workqueue) - const data = table.prop('content') - const EXPECTED_DATE_OF_REJECTION = formattedDuration( - new Date('2021-10-20T11:03:20.660Z') - ) - - expect(data.length).toBe(2) - expect(data[1].id).toBe('e302f7c5-ad87-4117-91c1-35eaf2ea7be8') - expect(data[1].contactNumber).toBe('01622688231') - expect(data[1].sentForUpdates).toBe(EXPECTED_DATE_OF_REJECTION) - expect(data[1].event).toBe('Birth') - expect(data[1].actions).toBeDefined() - }) - - it('returns an empty array incase of invalid graphql query response', async () => { - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent } = await createTestComponent( - // @ts-ignore - , - { store } - ) - - const table = await waitForElement(testComponent, Workqueue) - - const data = table.prop('content') - expect(data.length).toBe(0) - }) - - describe('handles download status', () => { - let testComponent: TestComponentWithRouteMock - let createdTestComponent: TestComponentWithRouteMock - beforeEach(async () => { - const TIME_STAMP = '1544188309380' - Date.now = vi.fn(() => 1554055200000) - - mockListSyncController - .mockReturnValueOnce({ - data: { - inProgressTab: { totalItems: 0, results: [] }, - notificationTab: { totalItems: 0, results: [] }, - reviewTab: { totalItems: 0, results: [] }, - rejectTab: { - totalItems: 2, - results: [ - { - id: '9a55d213-ad9f-4dcd-9418-340f3a7f6269', - type: EventType.Birth, - registration: { - status: 'REJECTED', - contactNumber: '01622688231', - trackingId: 'BW0UTHR', - registrationNumber: null, - eventLocationId: null, - registeredLocationId: - '308c35b4-04f8-4664-83f5-9790e790cde1', - duplicates: null, - createdAt: TIME_STAMP, - modifiedAt: TIME_STAMP + 1 - }, - dateOfBirth: '2010-10-10', - childName: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ], - dateOfDeath: null, - deceasedName: null - }, - { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e95', - type: EventType.Death, - registration: { - status: 'REJECTED', - trackingId: 'DW0UTHR', - registrationNumber: null, - eventLocationId: null, - contactNumber: '01622688231', - duplicates: ['308c35b4-04f8-4664-83f5-9790e790cd33'], - registeredLocationId: - '308c35b4-04f8-4664-83f5-9790e790cde1', - createdAt: TIME_STAMP, - modifiedAt: TIME_STAMP - }, - dateOfBirth: null, - childName: null, - dateOfDeath: '2007-01-01', - deceasedName: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ] - } - ] - }, - approvalTab: { totalItems: 0, results: [] }, - printTab: { totalItems: 0, results: [] }, - externalValidationTab: { totalItems: 0, results: [] } - } - }) - .mockReturnValueOnce({ - data: { - fetchBirthRegistration: birthDeclarationForReview - } - }) - client.query = mockListSyncController - - createdTestComponent = await createTestComponent( - // @ts-ignore - , - { - store, - apolloClient: client, - path: REGISTRAR_HOME_TAB_PAGE, - initialEntries: [ - formatUrl(REGISTRAR_HOME_TAB_PAGE, { - tabId: WORKQUEUE_TABS.requiresUpdate - }) - ] - } - ) - testComponent = createdTestComponent - setScopes(REGISTRATION_AGENT_DEFAULT_SCOPES, store) - }) - - it('downloads the declaration after clicking download button', async () => { - const downloadButton = await waitForElement( - testComponent.component, - '#ListItemAction-0-icon' - ) - - downloadButton.hostNodes().simulate('click') - - testComponent.component.update() - - expect( - testComponent.component.find('#assignment').hostNodes() - ).toHaveLength(1) - testComponent.component.find('#assign').hostNodes().simulate('click') - - expect( - testComponent.component - .find('#action-loading-ListItemAction-0') - .hostNodes() - ) - - const action = await waitForElement( - testComponent.component, - '#ListItemAction-0-Update' - ) - action.hostNodes().simulate('click') - - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - testComponent.component.update() - - expect(testComponent.router.state.location.pathname).toBe( - '/reviews/9a55d213-ad9f-4dcd-9418-340f3a7f6269/events/birth/parent/review' - ) - }) - - it('shows error when download is failed', async () => { - const downloadedDeclaration = makeDeclarationReadyToDownload( - EventType.Death, - 'bc09200d-0160-43b4-9e2b-5b9e90424e95', - DownloadAction.LOAD_REVIEW_DECLARATION - ) - downloadedDeclaration.downloadStatus = DOWNLOAD_STATUS.FAILED - store.dispatch(storeDeclaration(downloadedDeclaration)) - testComponent.component.update() - expect( - testComponent.component - .find('#ListItemAction-1-icon-failed') - .hostNodes() - ).toHaveLength(1) - }) - }) -}) - -describe('Tablet tests', () => { - const { store } = createStore() - - beforeAll(async () => { - setScopes(REGISTRAR_DEFAULT_SCOPES, store) - resizeWindow(800, 1280) - }) - - afterEach(() => { - resizeWindow(1024, 768) - }) - - it('redirects to recordAudit page if item is clicked', async () => { - const TIME_STAMP = '1544188309380' - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent, router } = await createTestComponent( - // @ts-ignore - , - { store } - ) - - const element = await waitForElement(testComponent, '#name_0') - element.hostNodes().simulate('click') - - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - testComponent.update() - - expect(router.state.location.pathname).toContain( - '/record-audit/rejectTab/e302f7c5-ad87-4117-91c1-35eaf2ea7be8' - ) - }) -}) diff --git a/packages/client/src/views/OfficeHome/sentForReview/SentForReview.test.tsx b/packages/client/src/views/OfficeHome/sentForReview/SentForReview.test.tsx deleted file mode 100644 index 0158c537b03..00000000000 --- a/packages/client/src/views/OfficeHome/sentForReview/SentForReview.test.tsx +++ /dev/null @@ -1,553 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ - -import { AppStore, createStore } from '@client/store' -import { - createTestComponent, - mockUserResponse, - REGISTRATION_AGENT_DEFAULT_SCOPES, - resizeWindow, - setScopes -} from '@client/tests/util' -import { waitForElement } from '@client/tests/wait-for-element' -import { Workqueue } from '@opencrvs/components/lib/Workqueue' - -import { merge } from 'lodash' -import * as React from 'react' -import { SentForReview } from './SentForReview' -import type { - GQLBirthEventSearchSet, - GQLDeathEventSearchSet -} from '@client/utils/gateway-deprecated-do-not-use' -import { formattedDuration } from '@client/utils/date-formatting' -import { vi } from 'vitest' -import { EventType } from '@client/utils/gateway' - -const nameObj = { - data: { - getUser: { - name: [ - { - use: 'en', - firstNames: 'Mohammad', - familyName: 'Ashraful', - __typename: 'HumanName' - }, - { use: 'bn', firstNames: '', familyName: '', __typename: 'HumanName' } - ], - role: 'DISTRICT_REGISTRAR' - } - } -} - -const mockUserData = { - id: 'e302f7c5-ad87-4117-91c1-35eaf2ea7be8', - type: EventType.Birth, - registration: { - status: 'DECLARED', - contactNumber: '01622688231', - trackingId: 'BW0UTHR', - registrationNumber: null, - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1', - duplicates: null, - createdAt: '2018-05-23T14:44:58+02:00', - modifiedAt: '2018-05-23T14:44:58+02:00' - }, - dateOfBirth: '2010-10-10', - childName: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ], - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - child: { - id: 'FAKE_ID', - name: [ - { - firstNames: 'Iliyas', - familyName: 'Khan', - use: 'en' - }, - { - firstNames: 'ইলিয়াস', - familyName: 'খান', - use: 'bn' - } - ], - birthDate: '2010-10-10' - }, - deceased: { - name: [ - { - use: '', - firstNames: '', - familyName: '' - } - ], - deceased: { - deathDate: '' - } - }, - informant: { - individual: { - telecom: [ - { - system: '', - use: '', - value: '' - } - ] - } - }, - dateOfDeath: null, - deceasedName: null, - createdAt: '2018-05-23T14:44:58+02:00' -} -/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ -const userData: any = [] -for (let i = 0; i < 14; i++) { - userData.push(mockUserData) -} -merge(mockUserResponse, nameObj) - -describe('RegistrationHome sent for approval tab related tests', () => { - let store: AppStore - - beforeEach(async () => { - ;({ store } = createStore()) - setScopes(REGISTRATION_AGENT_DEFAULT_SCOPES, store) - }) - - it('renders all items returned from graphql query in sent for approval', async () => { - const TIME_STAMP = '1544188309380' - Date.now = vi.fn(() => 1554055200000) - - const sentForApprovalDate = '2019-10-20T11:03:20.660Z' - const { component: testComponent } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - - testComponent.update() - const data = testComponent.find(Workqueue).prop('content') - const EXPECTED_DATE_OF_DECLARATION = formattedDuration( - new Date(sentForApprovalDate) - ) - - expect(data.length).toBe(2) - expect(data[0].id).toBe('e302f7c5-ad87-4117-91c1-35eaf2ea7be8') - expect(data[0].eventTimeElapsed).toBe('8 years ago') - expect(data[0].sentForApproval).toBe(EXPECTED_DATE_OF_DECLARATION) - expect(data[0].name).toBe('khan iliyas') - expect(data[0].trackingId).toBe('BW0UTHR') - expect(data[0].event).toBe('Birth') - expect(data[0].actions).toBeDefined() - }) - - it('returns an empty array incase of invalid graphql query response', async () => { - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - - const data = (await waitForElement(testComponent, Workqueue)).prop( - 'content' - ) - expect(data.length).toBe(0) - }) - - it('should show pagination if items more than 10 in Approval Tab', async () => { - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - - expect( - testComponent.find('#pagination_container').hostNodes() - ).toHaveLength(1) - - testComponent - .find('#pagination button') - .last() - .hostNodes() - .simulate('click') - }) - - it('should show pagination and page number as per need ', async () => { - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - - expect( - testComponent.find('#pagination_container').hostNodes() - ).toHaveLength(1) - expect(testComponent.exists('#page-number-2')).toBeTruthy() - }) - - it('redirect to recordAudit page if item is clicked on desktop view ', async () => { - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent, router } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - - testComponent.update() - const element = await waitForElement(testComponent, '#name_0') - element.hostNodes().simulate('click') - - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - testComponent.update() - - expect(router.state.location.pathname).toContain( - '/record-audit/approvalTab/e302f7c5-ad87-4117-91c1-35eaf2ea7be8' - ) - }) -}) - -describe('Tablet tests', () => { - const { store } = createStore() - - beforeAll(async () => { - setScopes(REGISTRATION_AGENT_DEFAULT_SCOPES, store) - resizeWindow(800, 1280) - }) - - afterEach(() => { - resizeWindow(1024, 768) - }) - - it('redirects to recordAudit page if item is clicked', async () => { - vi.clearAllMocks() - const TIME_STAMP = '1544188309380' - Date.now = vi.fn(() => 1554055200000) - - const { component: testComponent, router } = await createTestComponent( - {}} - loading={false} - error={false} - />, - { store } - ) - - testComponent.update() - const element = await waitForElement(testComponent, '#name_0') - element.hostNodes().simulate('click') - - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - testComponent.update() - - expect(router.state.location.pathname).toContain( - '/record-audit/approvalTab/e302f7c5-ad87-4117-91c1-35eaf2ea7be8' - ) - }) -}) diff --git a/packages/client/src/views/Organisation/AdministrativeLevels.test.tsx b/packages/client/src/views/Organisation/AdministrativeLevels.test.tsx index 0f5b9469fb5..7fbcd77e5d7 100644 --- a/packages/client/src/views/Organisation/AdministrativeLevels.test.tsx +++ b/packages/client/src/views/Organisation/AdministrativeLevels.test.tsx @@ -19,9 +19,9 @@ import { import { SCOPES } from '@opencrvs/commons/client' import { AdministrativeLevels } from './AdministrativeLevels' import { setUserDetails } from '@client/profile/profileActions' - import { ORGANISATIONS_INDEX } from '@client/navigation/routes' import { formatUrl } from '@client/navigation' +import { V2_DEFAULT_MOCK_LOCATIONS } from '@client/tests/v2-events/locations-mock' describe('for user with read organisation in my jurisdiction scope', () => { let store: AppStore @@ -32,14 +32,23 @@ describe('for user with read organisation in my jurisdiction scope', () => { }) it('link should be enabled if office is under jurisdiction', async () => { - const userOfficeId = 'da672661-eb0a-437b-aa7a-a6d9a1711dd1' + const centralProvincialOffice = V2_DEFAULT_MOCK_LOCATIONS.find( + (location) => location.name === 'Central Provincial Office' + ) + const ibomboDistrict = V2_DEFAULT_MOCK_LOCATIONS.find( + (location) => location.name === 'Ibombo' + ) + + if (!centralProvincialOffice || !ibomboDistrict) { + throw new Error('Required mock locations not found') + } const { component } = await createTestComponent(, { store, path: ORGANISATIONS_INDEX, initialEntries: [ formatUrl(ORGANISATIONS_INDEX, { - locationId: '7a18cb4c-38f3-449f-b3dc-508473d485f3' + locationId: ibomboDistrict.id }) ] }) @@ -47,14 +56,14 @@ describe('for user with read organisation in my jurisdiction scope', () => { store.dispatch( setUserDetails({ loading: false, - data: fetchUserMock(userOfficeId), + data: fetchUserMock(centralProvincialOffice.id), networkStatus: NetworkStatus.ready }) ) component.update() expect( component - .find({ children: 'Moktarpur Union Parishad' }) + .find({ children: 'Ibombo District Office' }) .hostNodes() .first() .prop('disabled') @@ -62,28 +71,38 @@ describe('for user with read organisation in my jurisdiction scope', () => { }) it('link should be disabled if office is not under jurisdiction', async () => { - const userOfficeId = 'da672661-eb0a-437b-aa7a-a6d9a1711dd1' + const centralProvincialOffice = V2_DEFAULT_MOCK_LOCATIONS.find( + (location) => location.name === 'Central Provincial Office' + ) + const ilangaDistrict = V2_DEFAULT_MOCK_LOCATIONS.find( + (location) => location.name === 'Ilanga' + ) + + if (!centralProvincialOffice || !ilangaDistrict) { + throw new Error('Required mock locations not found') + } const { component } = await createTestComponent(, { store, path: ORGANISATIONS_INDEX, initialEntries: [ formatUrl(ORGANISATIONS_INDEX, { - locationId: '6e1f3bce-7bcb-4bf6-8e35-0d9facdf158b' + locationId: ilangaDistrict.id }) ] }) + store.dispatch( setUserDetails({ loading: false, - data: fetchUserMock(userOfficeId), + data: fetchUserMock(centralProvincialOffice.id), networkStatus: NetworkStatus.ready }) ) component.update() expect( component - .find({ children: 'Dhaka Union Parishad' }) + .find({ children: 'Ilanga District Office' }) .hostNodes() .first() .prop('disabled') @@ -100,14 +119,23 @@ describe('for user with read organisation scope', () => { }) it('link should be enabled if office is under jurisdiction', async () => { - const userOfficeId = 'da672661-eb0a-437b-aa7a-a6d9a1711dd1' + const centralProvincialOffice = V2_DEFAULT_MOCK_LOCATIONS.find( + (location) => location.name === 'Central Provincial Office' + ) + const ibomboDistrict = V2_DEFAULT_MOCK_LOCATIONS.find( + (location) => location.name === 'Ibombo' + ) + + if (!centralProvincialOffice || !ibomboDistrict) { + throw new Error('Required mock locations not found') + } const { component } = await createTestComponent(, { store, path: ORGANISATIONS_INDEX, initialEntries: [ formatUrl(ORGANISATIONS_INDEX, { - locationId: '7a18cb4c-38f3-449f-b3dc-508473d485f3' + locationId: ibomboDistrict.id }) ] }) @@ -115,14 +143,14 @@ describe('for user with read organisation scope', () => { store.dispatch( setUserDetails({ loading: false, - data: fetchUserMock(userOfficeId), + data: fetchUserMock(centralProvincialOffice.id), networkStatus: NetworkStatus.ready }) ) component.update() expect( component - .find({ children: 'Moktarpur Union Parishad' }) + .find({ children: 'Ibombo District Office' }) .hostNodes() .first() .prop('disabled') @@ -130,14 +158,23 @@ describe('for user with read organisation scope', () => { }) it('link should be enabled even if office is not under jurisdiction', async () => { - const userOfficeId = 'da672661-eb0a-437b-aa7a-a6d9a1711dd1' + const centralProvincialOffice = V2_DEFAULT_MOCK_LOCATIONS.find( + (location) => location.name === 'Central Provincial Office' + ) + const ilangaDistrict = V2_DEFAULT_MOCK_LOCATIONS.find( + (location) => location.name === 'Ilanga' + ) + + if (!centralProvincialOffice || !ilangaDistrict) { + throw new Error('Required mock locations not found') + } const { component } = await createTestComponent(, { store, path: ORGANISATIONS_INDEX, initialEntries: [ formatUrl(ORGANISATIONS_INDEX, { - locationId: '6e1f3bce-7bcb-4bf6-8e35-0d9facdf158b' + locationId: ilangaDistrict.id }) ] }) @@ -145,14 +182,14 @@ describe('for user with read organisation scope', () => { store.dispatch( setUserDetails({ loading: false, - data: fetchUserMock(userOfficeId), + data: fetchUserMock(centralProvincialOffice.id), networkStatus: NetworkStatus.ready }) ) component.update() expect( component - .find({ children: 'Dhaka Union Parishad' }) + .find({ children: 'Ilanga District Office' }) .hostNodes() .first() .prop('disabled') @@ -169,14 +206,23 @@ describe('for user with read organisation my office scope', () => { }) it("link should be enabled if user's office", async () => { - const userOfficeId = '0d8474da-0361-4d32-979e-af91f012340a' + const ibomboDistrictOffice = V2_DEFAULT_MOCK_LOCATIONS.find( + (location) => location.name === 'Ibombo District Office' + ) + const ibomboDistrict = V2_DEFAULT_MOCK_LOCATIONS.find( + (location) => location.name === 'Ibombo' + ) + + if (!ibomboDistrictOffice || !ibomboDistrict) { + throw new Error('Required mock locations not found') + } const { component } = await createTestComponent(, { store, path: ORGANISATIONS_INDEX, initialEntries: [ formatUrl(ORGANISATIONS_INDEX, { - locationId: '7a18cb4c-38f3-449f-b3dc-508473d485f3' + locationId: ibomboDistrict.id }) ] }) @@ -184,14 +230,14 @@ describe('for user with read organisation my office scope', () => { store.dispatch( setUserDetails({ loading: false, - data: fetchUserMock(userOfficeId), + data: fetchUserMock(ibomboDistrictOffice.id), networkStatus: NetworkStatus.ready }) ) component.update() expect( component - .find({ children: 'Moktarpur Union Parishad' }) + .find({ children: 'Ibombo District Office' }) .hostNodes() .first() .prop('disabled') @@ -199,29 +245,38 @@ describe('for user with read organisation my office scope', () => { }) it('link should be disabled for other offices', async () => { - const userOfficeId = '0d8474da-0361-4d32-979e-af91f012340a' + const ibomboDistrictOffice = V2_DEFAULT_MOCK_LOCATIONS.find( + (location) => location.name === 'Ibombo District Office' + ) + const isangoDistrict = V2_DEFAULT_MOCK_LOCATIONS.find( + (location) => location.name === 'Isango' + ) + + if (!ibomboDistrictOffice || !isangoDistrict) { + throw new Error('Required mock locations not found') + } const { component } = await createTestComponent(, { store, path: ORGANISATIONS_INDEX, initialEntries: [ formatUrl(ORGANISATIONS_INDEX, { - locationId: '5926982b-845c-4463-80aa-cbfb86762e0a' + locationId: isangoDistrict.id }) ] }) + store.dispatch( setUserDetails({ loading: false, - data: fetchUserMock(userOfficeId), + data: fetchUserMock(ibomboDistrictOffice.id), networkStatus: NetworkStatus.ready }) ) component.update() - expect( component - .find({ children: 'Comilla Union Parishad' }) + .find({ children: 'Isango District Office' }) .hostNodes() .first() .prop('disabled') diff --git a/packages/client/src/views/Organisation/AdministrativeLevels.tsx b/packages/client/src/views/Organisation/AdministrativeLevels.tsx index 052c5674f11..91d933f5a65 100644 --- a/packages/client/src/views/Organisation/AdministrativeLevels.tsx +++ b/packages/client/src/views/Organisation/AdministrativeLevels.tsx @@ -26,19 +26,17 @@ import { Icon } from '@opencrvs/components/lib' import { IBreadCrumbData } from '@opencrvs/components/src/Breadcrumb' -import { useSelector } from 'react-redux' -import { IStoreState } from '@client/store' -import { ILocation } from '@client/offline/reducer' import { useParams, useNavigate } from 'react-router-dom' import { formatUrl, generatePerformanceHomeUrl } from '@client/navigation' import { Button } from '@opencrvs/components/lib/Button' import startOfMonth from 'date-fns/startOfMonth' import subMonths from 'date-fns/subMonths' import styled from 'styled-components' -import { getLocalizedLocationName } from '@client/utils/locationUtils' import { usePermissions } from '@client/hooks/useAuthorization' import * as routes from '@client/navigation/routes' import { stringify } from 'querystring' +import { useLocations } from '@client/v2-events/hooks/useLocations' +import { Location, UUID } from '@opencrvs/commons/client' const DEFAULT_PAGINATION_LIST_SIZE = 10 @@ -47,7 +45,7 @@ type IRouteProps = { } type IGetNewLevel = { - childLocations: ILocation[] + childLocations: Location[] breadCrumb: IBreadCrumbData[] } @@ -101,67 +99,58 @@ export function AdministrativeLevels({ const { locationId } = useParams() const { canAccessOffice } = usePermissions() const navigate = useNavigate() + const { getLocations } = useLocations() - const getNewLevel = - (currentlySelectedLocation?: string) => - (store: IStoreState): IGetNewLevel => { - const location = currentlySelectedLocation ?? '0' - const locations = store.offline.offlineData.locations as { - [key: string]: ILocation - } - const offices = store.offline.offlineData.offices as { - [key: string]: ILocation - } - - const childLocations = Object.values(locations) - .filter( - (s) => s.status === 'active' && s.partOf === `Location/${location}` - ) - .concat( - Object.values(offices).filter( - (s) => s.status === 'active' && s.partOf === `Location/${location}` - ) - ) - - let dataOfBreadCrumb: IBreadCrumbData[] = [ - { - label: intl.formatMessage(constantsMessages.countryName), - paramId: '' - } - ] + const getNewLevel = ( + currentlySelectedLocation: UUID | null + ): IGetNewLevel => { + const location = currentlySelectedLocation ?? null - if (currentlySelectedLocation) { - let currentLocationId = currentlySelectedLocation - const LocationBreadCrumb: IBreadCrumbData[] | null = [] - do { - const currentOffice = locations[currentLocationId] + const locations = getLocations.useSuspenseQuery() - if (currentOffice) { - LocationBreadCrumb.push({ - label: getLocalizedLocationName(intl, currentOffice), - paramId: currentOffice.id - }) - currentLocationId = currentOffice.partOf.split('/')[1] - } else { - currentLocationId = '' - } - } while (currentLocationId !== '') + const childLocations = [...locations.values()].filter( + ({ parentId, validUntil, locationType }) => + (validUntil === null || new Date(validUntil) > new Date()) && + parentId === location && + locationType && + ['ADMIN_STRUCTURE', 'CRVS_OFFICE'].includes(locationType) + ) - dataOfBreadCrumb = [ - ...dataOfBreadCrumb, - ...LocationBreadCrumb.reverse() - ] + let dataOfBreadCrumb: IBreadCrumbData[] = [ + { + label: intl.formatMessage(constantsMessages.countryName), + paramId: '' } + ] + + if (currentlySelectedLocation) { + let currentLocationId: UUID | null = currentlySelectedLocation + const LocationBreadCrumb: IBreadCrumbData[] | null = [] + do { + const currentOffice = locations.get(currentLocationId) + + if (currentOffice) { + LocationBreadCrumb.push({ + label: currentOffice.name, + paramId: currentOffice.id + }) + currentLocationId = currentOffice.parentId + } else { + currentLocationId = null + } + } while (currentLocationId !== null) - return { - breadCrumb: dataOfBreadCrumb, - childLocations - } + dataOfBreadCrumb = [...dataOfBreadCrumb, ...LocationBreadCrumb.reverse()] } - const dataLocations = useSelector( - getNewLevel(locationId) - ) + return { + breadCrumb: dataOfBreadCrumb, + childLocations + } + } + + const dataLocations = getNewLevel(UUID.safeParse(locationId).data ?? null) + const totalNumber = dataLocations.childLocations.length const [currentPageNumber, setCurrentPageNumber] = React.useState(1) @@ -199,20 +188,20 @@ export function AdministrativeLevels({ (currentPageNumber - 1) * DEFAULT_PAGINATION_LIST_SIZE, currentPageNumber * DEFAULT_PAGINATION_LIST_SIZE ) - .map((level: ILocation, index: number) => ( + .map((level: Location, index: number) => ( { setCurrentPageNumber(1) changeLevelAction(e, level.id) }} > - {getLocalizedLocationName(intl, level)} + {level.name} - ) : level.type === 'CRVS_OFFICE' ? ( + ) : level.locationType === 'CRVS_OFFICE' ? ( @@ -224,33 +213,10 @@ export function AdministrativeLevels({ }) } > - {getLocalizedLocationName(intl, level)} + {level.name} ) : null } - actions={ - - } /> )) ) : ( diff --git a/packages/client/src/views/Performance/FieldAgentList.test.tsx b/packages/client/src/views/Performance/FieldAgentList.test.tsx deleted file mode 100644 index af9a033e91b..00000000000 --- a/packages/client/src/views/Performance/FieldAgentList.test.tsx +++ /dev/null @@ -1,163 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { AppStore } from '@client/store' -import { - createRouterProps, - createTestComponent, - createTestStore -} from '@client/tests/util' -import { waitForElement } from '@client/tests/wait-for-element' -import { FETCH_FIELD_AGENTS_WITH_PERFORMANCE_DATA } from '@client/views/SysAdmin/Performance/queries' -import { ReactWrapper } from 'enzyme' -import * as React from 'react' -import { FieldAgentList } from './FieldAgentList' -import { vi } from 'vitest' - -describe('Field agent list tests', () => { - let component: ReactWrapper<{}, {}> - let store: AppStore - - beforeAll(async () => { - Date.now = vi.fn(() => 1487076708000) - ;({ store } = await createTestStore()) - }) - - beforeEach(async () => { - const { location } = createRouterProps( - '/performance/field-agents', - { isNavigatedInsideApp: false }, - { - matchParams: {}, - search: { - locationId: 'bfe8306c-0910-48fe-8bf5-0db906cf3155', - timeEnd: new Date(1487076708000).toISOString(), - timeStart: new Date(1455454308000).toISOString(), - event: 'birth' - } - } - ) - const graphqlMock = [ - { - request: { - query: FETCH_FIELD_AGENTS_WITH_PERFORMANCE_DATA, - variables: { - timeStart: '2016-02-14T12:51:48.000Z', - timeEnd: '2017-02-14T12:51:48.000Z', - locationId: 'bfe8306c-0910-48fe-8bf5-0db906cf3155', - status: 'active', - event: 'BIRTH', - count: 25, - sort: 'asc', - skip: 0 - } - }, - result: { - data: { - searchFieldAgents: { - results: [ - { - practitionerId: '1', - fullName: 'Sakib Al Hasan', - role: { - label: { - id: 'userRole.hospitalClerk', - defaultMessage: 'Hospital Clerk', - description: 'Name for user role Hospital Clerk', - __typename: 'I18nMessage' - }, - __typename: 'UserRole' - }, - status: 'active', - primaryOfficeId: '1', - creationDate: '1488076708000', - totalNumberOfDeclarationStarted: 12, - totalNumberOfInProgressAppStarted: 3, - totalNumberOfRejectedDeclarations: 1782, - averageTimeForDeclaredDeclarations: 2 - }, - { - practitionerId: '2', - fullName: 'Naeem Hossain', - role: { - label: { - id: 'userRole.hospitalClerk', - defaultMessage: 'Hospital Clerk', - description: 'Name for user role Hospital Clerk', - __typename: 'I18nMessage' - }, - __typename: 'UserRole' - }, - status: 'active', - primaryOfficeId: '1', - creationDate: '1487076708000', - totalNumberOfDeclarationStarted: 0, - totalNumberOfInProgressAppStarted: 0, - totalNumberOfRejectedDeclarations: 0, - averageTimeForDeclaredDeclarations: 0 - } - ], - totalItems: 2 - } - } - } - } - ] - const { component: testComponent } = await createTestComponent( - , - { - store, - graphqlMocks: graphqlMock, - initialEntries: [location.pathname + '?' + location.search] - } - ) - component = testComponent - }) - - it('renders without crashing', async () => { - await waitForElement(component, '#field-agent-list') - }) - it('toggles sorting order of the list', async () => { - const firstRowElement = await waitForElement(component, '#row_0') - const toggleSortActionElement = await waitForElement( - component, - '#totalDeclarations-label' - ) - expect(firstRowElement.hostNodes().childAt(0).text()).toBe('Sakib Al Hasan') - - toggleSortActionElement.hostNodes().simulate('click') - - expect(firstRowElement.hostNodes().childAt(0).text()).toBe('Naeem Hossain') - }) - - it('For graphql errors it renders with error components', async () => { - const { location } = createRouterProps( - '/performance/field-agents', - { isNavigatedInsideApp: false }, - { - matchParams: {}, - search: { - locationId: 'bfe8306c-0910-48fe-8bf5-0db906cf3155', - timeEnd: new Date(1487076708000).toISOString(), - timeStart: new Date(1455454308000).toISOString() - } - } - ) - - const testErrorComponent = await createTestComponent(, { - store, - initialEntries: [location.pathname + '?' + location.search] - }) - await waitForElement( - testErrorComponent.component, - '#field-agent-error-list' - ) - }) -}) diff --git a/packages/client/src/views/Performance/FieldAgentList.tsx b/packages/client/src/views/Performance/FieldAgentList.tsx index 61694d1df82..f917f65eaf4 100644 --- a/packages/client/src/views/Performance/FieldAgentList.tsx +++ b/packages/client/src/views/Performance/FieldAgentList.tsx @@ -407,17 +407,6 @@ function FieldAgentListComponent(props: IProps) { }) }) }} - locationFilter={ - window.config.FIELD_AGENT_AUDIT_LOCATIONS - ? ({ jurisdictionType }) => - Boolean( - jurisdictionType && - window.config.FIELD_AGENT_AUDIT_LOCATIONS.split( - ',' - ).includes(jurisdictionType) - ) - : undefined - } /> { diff --git a/packages/client/src/views/Performance/RegistrationsList.test.tsx b/packages/client/src/views/Performance/RegistrationsList.test.tsx deleted file mode 100644 index 8e5e8afc9fc..00000000000 --- a/packages/client/src/views/Performance/RegistrationsList.test.tsx +++ /dev/null @@ -1,202 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { AppStore } from '@client/store' -import { - createRouterProps, - createTestComponent, - createTestStore -} from '@client/tests/util' -import { waitForElement } from '@client/tests/wait-for-element' -import { FETCH_REGISTRATIONS } from '@client/views/SysAdmin/Performance/queries' -import { ReactWrapper } from 'enzyme' -import * as React from 'react' -import { vi } from 'vitest' -import { RegistrationList } from './RegistrationsList' - -describe('Registrations List test', () => { - let component: ReactWrapper<{}, {}> - let store: AppStore - - beforeAll(async () => { - Date.now = vi.fn(() => 1667806908949) - ;({ store } = await createTestStore()) - }) - - beforeEach(async () => { - const { location } = createRouterProps( - '/performance/registrations', - { isNavigatedInsideApp: false }, - { - matchParams: {}, - search: { - timeStart: '2021-11-04T14:08:46.761Z', - timeEnd: '2022-11-30T17:59:59.999Z', - event: 'BIRTH', - filterBy: 'by_time', - locationId: 'c9c4d6e9-981c-4646-98fe-4014fddebd5e', - currentPageNumber: '1' - } - } - ) - const graphqlMock = [ - { - request: { - query: FETCH_REGISTRATIONS, - variables: { - timeStart: '2021-11-04T14:08:46.761Z', - timeEnd: '2022-11-30T17:59:59.999Z', - event: 'BIRTH', - skip: 0, - size: 10, - filterBy: 'by_time', - locationId: 'c9c4d6e9-981c-4646-98fe-4014fddebd5e' - } - }, - result: { - data: { - getRegistrationsListByFilter: { - __typename: 'TotalMetricsByTime', - results: [ - { - total: 0, - delayed: 0, - late: 0, - home: 0, - healthFacility: 0, - month: '2022-11-01', - time: '1667239200000', - __typename: 'EventMetricsByTime' - }, - { - total: 13, - delayed: 1, - late: 2, - home: 1, - healthFacility: 12, - month: '2022-10-01', - time: '1664560800000', - __typename: 'EventMetricsByTime' - }, - { - total: 2, - delayed: 0, - late: 0, - home: 0, - healthFacility: 2, - month: '2022-09-01', - time: '1661968800000', - __typename: 'EventMetricsByTime' - }, - { - total: 0, - delayed: 0, - late: 0, - home: 0, - healthFacility: 0, - month: '2022-08-01', - time: '1659290400000', - __typename: 'EventMetricsByTime' - }, - { - total: 0, - delayed: 0, - late: 0, - home: 0, - healthFacility: 0, - month: '2022-07-01', - time: '1656612000000', - __typename: 'EventMetricsByTime' - }, - { - total: 0, - delayed: 0, - late: 0, - home: 0, - healthFacility: 0, - month: '2022-06-01', - time: '1654020000000', - __typename: 'EventMetricsByTime' - }, - { - total: 0, - delayed: 0, - late: 0, - home: 0, - healthFacility: 0, - month: '2022-05-01', - time: '1651341600000', - __typename: 'EventMetricsByTime' - }, - { - total: 0, - delayed: 0, - late: 0, - home: 0, - healthFacility: 0, - month: '2022-04-01', - time: '1648749600000', - __typename: 'EventMetricsByTime' - }, - { - total: 0, - delayed: 0, - late: 0, - home: 0, - healthFacility: 0, - month: '2022-03-01', - time: '1646071200000', - __typename: 'EventMetricsByTime' - }, - { - total: 0, - delayed: 0, - late: 0, - home: 0, - healthFacility: 0, - month: '2022-02-01', - time: '1643652000000', - __typename: 'EventMetricsByTime' - } - ], - total: 14 - } - } - } - } - ] - const { component: testComponent } = await createTestComponent( - , - { - store, - graphqlMocks: graphqlMock, - initialEntries: [location.pathname + '?' + location.search] - } - ) - component = testComponent - }) - - it('renders without crashing', async () => { - await waitForElement(component, '#registrations-list') - }) - - it('toggles sorting order of the list', async () => { - const firstRowElement = await waitForElement(component, '#row_0') - const toggleSortActionElement = await waitForElement( - component, - '#month-label' - ) - expect(firstRowElement.hostNodes().childAt(0).text()).toBe('November 2022') - - toggleSortActionElement.hostNodes().simulate('click') - - expect(firstRowElement.hostNodes().childAt(0).text()).toBe('February 2022') - }) -}) diff --git a/packages/client/src/views/PrintCertificate/PDFUtils.test.ts b/packages/client/src/views/PrintCertificate/PDFUtils.test.ts deleted file mode 100644 index bfcac8d8482..00000000000 --- a/packages/client/src/views/PrintCertificate/PDFUtils.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { downloadFile } from '@client/views/PrintCertificate/PDFUtils' -import { validImageB64String } from '@client/tests/mock-offline-data' -import { vi } from 'vitest' - -describe('PDFUtils related tests', () => { - it('downloadFile functionality', () => { - const createElementMock = vi.fn() - const setAttributeMock = vi.fn() - const onClickMock = vi.fn() - const anchor = document.createElement('a') - anchor.setAttribute = setAttributeMock - anchor.onclick = onClickMock() - createElementMock.mockReturnValueOnce(anchor) - document.createElement = createElementMock - downloadFile('image/svg+xml', validImageB64String, 'test.svg') - const linkSource = `data:image/svg+xml;base64,${window.btoa( - validImageB64String - )}` - expect(createElementMock).toHaveBeenCalledWith('a') - expect(setAttributeMock).toHaveBeenCalledWith('href', linkSource) - expect(onClickMock).toHaveBeenCalled() - }) -}) diff --git a/packages/client/src/views/PrintCertificate/PDFUtils.ts b/packages/client/src/views/PrintCertificate/PDFUtils.ts index 460691d9ef1..19f6848c3b3 100644 --- a/packages/client/src/views/PrintCertificate/PDFUtils.ts +++ b/packages/client/src/views/PrintCertificate/PDFUtils.ts @@ -309,15 +309,3 @@ export function svgToPdfTemplate( return pdfTemplate } - -export function downloadFile( - contentType: string, - data: string, - fileName: string -) { - const linkSource = `data:${contentType};base64,${window.btoa(data)}` - const downloadLink = document.createElement('a') - downloadLink.setAttribute('href', linkSource) - downloadLink.setAttribute('download', fileName) - downloadLink.click() -} diff --git a/packages/client/src/views/PrintCertificate/Payment.test.tsx b/packages/client/src/views/PrintCertificate/Payment.test.tsx deleted file mode 100644 index 9e3f83499a5..00000000000 --- a/packages/client/src/views/PrintCertificate/Payment.test.tsx +++ /dev/null @@ -1,197 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import * as React from 'react' -import { createStore } from '@client/store' -import { - createTestComponent, - mockDeclarationData, - mockDeathDeclarationData, - mockUserResponse, - flushPromises, - setScopes, - REGISTRATION_AGENT_DEFAULT_SCOPES -} from '@client/tests/util' -import { storeDeclaration } from '@client/declarations' -import { EventType } from '@client/utils/gateway' -import { Payment } from './Payment' -import { queries } from '@client/profile/queries' -import { Mock } from 'vitest' -import { - PRINT_CERTIFICATE_PAYMENT, - REGISTRAR_HOME_TAB -} from '@client/navigation/routes' -import { formatUrl } from '@client/navigation' -import { WORKQUEUE_TABS } from '@client/components/interface/WorkQueueTabs' -;(queries.fetchUserDetails as Mock).mockReturnValue(mockUserResponse) - -describe('verify collector tests', () => { - const { store } = createStore() - - const birthDeclaration = { - id: 'mockBirth1234', - data: { - ...mockDeclarationData, - history: [ - { - date: '2022-03-21T08:16:24.467+00:00', - regStatus: 'REGISTERED', - reinstated: false - } - ] - }, - event: EventType.Birth - } - - const deathDeclaration = { - id: 'mockDeath1234', - data: { - ...mockDeathDeclarationData, - history: [ - { - date: '2022-03-21T08:16:24.467+00:00', - regStatus: 'REGISTERED', - reinstated: false - } - ] - }, - event: EventType.Death - } - - describe('in case of birth declaration', () => { - beforeAll(async () => { - setScopes(REGISTRATION_AGENT_DEFAULT_SCOPES, store) - await flushPromises() - // @ts-ignore - store.dispatch( - storeDeclaration({ - ...birthDeclaration, - // @ts-ignore - data: { - ...birthDeclaration.data, - registration: { - ...birthDeclaration.data.registration, - certificates: [ - { - collector: { - type: 'MOTHER' - }, - hasShowedVerifiedDocument: true, - certificateTemplateId: 'birth-certificate' - } - ] - } - } - }) - ) - }) - - it('when mother is collector renders Payment component', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - path: PRINT_CERTIFICATE_PAYMENT, - initialEntries: [ - formatUrl(PRINT_CERTIFICATE_PAYMENT, { - registrationId: 'mockBirth1234', - eventType: EventType.Birth - }) - ] - } - ) - - expect(testComponent.find('#service').hostNodes().text()).toContain( - 'Birth' - ) - - expect(testComponent.find('#amountDue').hostNodes().text()).toContain( - '15' - ) - - testComponent.find('#Continue').hostNodes().simulate('click') - }) - - /* - - // Commenting out this test because receipt templates are not currently configurable - - it('print payment receipt', async () => { - const printMoneyReceiptSpy = vi.spyOn(PDFUtils, 'printMoneyReceipt') - const {router: testComponent} = await createTestComponent( - , - { store, initialEntries: [formatUrl('/', { - })] } - ) - - testComponent.find('#print-receipt').hostNodes().simulate('click') - - expect(printMoneyReceiptSpy).toBeCalled() - })*/ - - it('invalid declaration id', async () => { - const { router } = await createTestComponent(, { - store, - path: PRINT_CERTIFICATE_PAYMENT, - initialEntries: [ - formatUrl(PRINT_CERTIFICATE_PAYMENT, { - registrationId: 'mockBirth', - eventType: EventType.Birth - }) - ] - }) - expect(router.state.location.pathname).toEqual( - formatUrl(REGISTRAR_HOME_TAB, { - tabId: WORKQUEUE_TABS.readyToPrint, - selectorId: '' - }) - ) - }) - }) - - describe('in case of death declaration renders payment component', () => { - beforeAll(() => { - // @ts-ignore - store.dispatch(storeDeclaration(deathDeclaration)) - }) - - it('when informant is collector', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - path: PRINT_CERTIFICATE_PAYMENT, - initialEntries: [ - formatUrl(PRINT_CERTIFICATE_PAYMENT, { - registrationId: 'mockDeath1234', - eventType: EventType.Death - }) - ] - } - ) - - expect(testComponent.find('#service').hostNodes().text()).toContain( - 'Death' - ) - }) - }) -}) diff --git a/packages/client/src/views/PrintCertificate/ReviewCertificateAction.test.tsx b/packages/client/src/views/PrintCertificate/ReviewCertificateAction.test.tsx deleted file mode 100644 index 0b04271dece..00000000000 --- a/packages/client/src/views/PrintCertificate/ReviewCertificateAction.test.tsx +++ /dev/null @@ -1,336 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import * as React from 'react' -import { createStore } from '@client/store' -import { storeDeclaration, IDeclaration } from '@client/declarations' -import { - createTestComponent, - mockDeclarationData, - mockDeathDeclarationData, - mockMarriageDeclarationData, - flushPromises, - loginAsFieldAgent -} from '@client/tests/util' -import { ReviewCertificate } from './ReviewCertificateAction' -import { ReactWrapper } from 'enzyme' -import { IFormSectionData } from '@client/forms' -import { EventType } from '@client/utils/gateway' -import { cloneDeep } from 'lodash' -import { waitForElement } from '@client/tests/wait-for-element' -import { formatUrl } from '@client/navigation' -import { REVIEW_CERTIFICATE } from '@client/navigation/routes' - -const deathDeclaration = { - id: 'mockDeath1234', - data: { - ...mockDeathDeclarationData, - history: [ - { - date: '2022-03-21T08:16:24.467+00:00', - regStatus: 'REGISTERED', - reinstated: false - } - ] - }, - event: EventType.Death -} - -describe('when user wants to review death certificate', () => { - it('displays the "Confirm & Print" button', async () => { - const { store } = createStore() - - loginAsFieldAgent(store) - - const { component } = await createTestComponent(, { - store, - path: REVIEW_CERTIFICATE, - initialEntries: [ - { - pathname: formatUrl(REVIEW_CERTIFICATE, { - registrationId: 'mockDeath1234', - eventType: EventType.Death - }), - state: { isNavigatedInsideApp: false } - } - ] - }) - - // @ts-ignore - store.dispatch(storeDeclaration(deathDeclaration)) - component.update() - - const confirmBtn = component.find('#confirm-print') - const confirmBtnExist = !!confirmBtn.hostNodes().length - expect(confirmBtnExist).toBe(true) - }) -}) - -describe('back button behavior tests of review certificate action', () => { - const mockBirthDeclarationData = { - ...cloneDeep(mockDeclarationData), - history: [ - { - date: '2022-03-21T08:16:24.467+00:00', - regStatus: 'REGISTERED', - reinstated: false - } - ] - } - mockBirthDeclarationData.registration.certificates[0] = { - //@ts-ignore - collector: { - type: 'PRINT_IN_ADVANCE' - }, - certificateTemplateId: 'death-certificate' - } - - it('takes user history back when navigated from inside app', async () => { - const { store } = createStore() - - loginAsFieldAgent(store) - const birthDeclaration = { - id: 'asdhdqe2472487jsdfsdf', - data: mockBirthDeclarationData, - event: EventType.Birth - } - store.dispatch( - // @ts-ignore - storeDeclaration(birthDeclaration) - ) - const { component, router } = await createTestComponent( - , - { - store, - path: '*', - initialEntries: [ - { - pathname: formatUrl(REVIEW_CERTIFICATE, { - registrationId: 'asdhdqe2472487jsdfsdf', - eventType: EventType.Birth - }), - state: { isNavigatedInsideApp: true } - }, - { - pathname: '', - state: { isNavigatedInsideApp: true } - } - ] - } - ) - - component.find('#action_page_back_button').hostNodes().simulate('click') - expect(router.state.location.pathname).toBe( - formatUrl(REVIEW_CERTIFICATE, { - registrationId: 'asdhdqe2472487jsdfsdf', - eventType: EventType.Birth - }) - ) - }) - - it('takes user to registration home when navigated from external link', async () => { - const { store } = createStore() - - loginAsFieldAgent(store) - store.dispatch( - // @ts-ignore - storeDeclaration({ - id: 'asdhdqe2472487jsdfsdf', - data: mockBirthDeclarationData, - event: EventType.Birth - } as IDeclaration) - ) - const { component, router } = await createTestComponent( - , - { - store, - initialEntries: [ - { - pathname: formatUrl(REVIEW_CERTIFICATE, { - registrationId: 'asdhdqe2472487jsdfsdf', - eventType: EventType.Birth - }), - state: { isNavigatedInsideApp: false } - } - ] - } - ) - - component.find('#action_page_back_button').hostNodes().simulate('click') - await flushPromises() - expect(router.state.location.pathname).toContain( - '/registration-home/print/' - ) - }) -}) - -describe('when user wants to review birth certificate', () => { - let component: ReactWrapper<{}, {}> - - beforeEach(async () => { - const { store } = createStore() - - const mockBirthDeclarationData = cloneDeep(mockDeclarationData) - mockBirthDeclarationData.registration.certificates[0] = { - //@ts-ignore - collector: { - type: 'PRINT_IN_ADVANCE' - }, - certificateTemplateId: 'birth-certificate' - } - loginAsFieldAgent(store) - await flushPromises() - store.dispatch( - storeDeclaration({ - id: 'asdhdqe2472487jsdfsdf', - data: { - ...mockBirthDeclarationData, - history: [ - { - date: '2022-03-21T08:16:24.467+00:00', - regStatus: 'REGISTERED', - reinstated: false - } - ] as unknown as IFormSectionData - }, - event: EventType.Birth - }) - ) - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: REVIEW_CERTIFICATE, - initialEntries: [ - { - pathname: formatUrl(REVIEW_CERTIFICATE, { - registrationId: 'asdhdqe2472487jsdfsdf', - eventType: EventType.Birth - }), - state: { isNavigatedInsideApp: false } - } - ] - } - ) - await flushPromises() - testComponent.update() - - component = testComponent - }) - - it('displays have the Continue and print Button', () => { - const confirmBtnExist = !!component.find('#confirm-print').hostNodes() - .length - expect(confirmBtnExist).toBe(true) - }) - - it('shows the Confirm Print Modal', () => { - const confirmBtn = component.find('#confirm-print').hostNodes() - confirmBtn.simulate('click') - component.update() - const modalIsDisplayed = !!component - .find('#confirm-print-modal') - .hostNodes().length - expect(modalIsDisplayed).toBe(true) - }) - - it('closes the modal on clicking the print the button', async () => { - const confirmBtn = await waitForElement(component, '#confirm-print') - confirmBtn.hostNodes().simulate('click') - component.update() - component.find('#print-certificate').hostNodes().simulate('click') - component.update() - - const modalIsClosed = !!component.find('#confirm-print-modal').hostNodes() - .length - - expect(modalIsClosed).toBe(false) - }) -}) - -describe('when user wants to review marriage certificate', () => { - let component: ReactWrapper<{}, {}> - - beforeEach(async () => { - const { store } = createStore() - - const mockMarriageData = cloneDeep(mockMarriageDeclarationData) - - loginAsFieldAgent(store) - await flushPromises() - store.dispatch( - storeDeclaration({ - id: '1234896128934719', - data: { - ...mockMarriageData, - history: [ - { - date: '2022-03-21T08:16:24.467+00:00', - regStatus: 'REGISTERED', - reinstated: false - } - ] as unknown as IFormSectionData - }, - event: EventType.Marriage - }) - ) - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: REVIEW_CERTIFICATE, - initialEntries: [ - { - pathname: formatUrl(REVIEW_CERTIFICATE, { - registrationId: '1234896128934719', - eventType: EventType.Birth - }), - state: { isNavigatedInsideApp: false } - } - ] - } - ) - await flushPromises() - testComponent.update() - - component = testComponent - }) - - it('displays have the Continue and print Button', () => { - const confirmBtnExist = !!component.find('#confirm-print').hostNodes() - .length - expect(confirmBtnExist).toBe(true) - }) - - it('shows the Confirm Print Modal', () => { - const confirmBtn = component.find('#confirm-print').hostNodes() - confirmBtn.simulate('click') - component.update() - const modalIsDisplayed = !!component - .find('#confirm-print-modal') - .hostNodes().length - expect(modalIsDisplayed).toBe(true) - }) - - it('closes the modal on clicking the print the button', async () => { - const confirmBtn = await waitForElement(component, '#confirm-print') - confirmBtn.hostNodes().simulate('click') - component.update() - component.find('#print-certificate').hostNodes().simulate('click') - component.update() - - const modalIsClosed = !!component.find('#confirm-print-modal').hostNodes() - .length - - expect(modalIsClosed).toBe(false) - }) -}) diff --git a/packages/client/src/views/PrintCertificate/VerifyCollector.test.tsx b/packages/client/src/views/PrintCertificate/VerifyCollector.test.tsx deleted file mode 100644 index b2549e07d26..00000000000 --- a/packages/client/src/views/PrintCertificate/VerifyCollector.test.tsx +++ /dev/null @@ -1,254 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import * as React from 'react' -import { createStore } from '@client/store' -import { - createTestComponent, - mockDeclarationData, - mockDeathDeclarationData, - TestComponentWithRouteMock -} from '@client/tests/util' -import { VerifyCollector } from './VerifyCollector' -import { storeDeclaration } from '@client/declarations' -import { EventType } from '@client/utils/gateway' -import { formatUrl } from '@client/navigation' -import { VERIFY_COLLECTOR } from '@client/navigation/routes' - -describe('verify collector tests', () => { - const { store } = createStore() - - const birthDeclaration = { - id: 'mockBirth1234', - data: { - ...mockDeclarationData, - history: [ - { - date: '2022-03-21T08:16:24.467+00:00', - regStatus: 'REGISTERED', - reinstated: false - } - ] - }, - event: EventType.Birth - } - - const deathDeclaration = { - id: 'mockDeath1234', - data: { - ...mockDeathDeclarationData, - history: [ - { - date: '2022-03-21T08:16:24.467+00:00', - regStatus: 'REGISTERED', - reinstated: false - } - ] - }, - event: EventType.Death - } - - describe('in case of birth declaration', () => { - beforeAll(async () => { - // @ts-ignore - store.dispatch(storeDeclaration(birthDeclaration)) - }) - - it('when mother is collector renders idVerifier component', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - path: VERIFY_COLLECTOR, - initialEntries: [ - formatUrl(VERIFY_COLLECTOR, { - registrationId: 'mockBirth1234', - eventType: EventType.Birth, - collector: 'mother' - }) - ] - } - ) - - expect(testComponent.find('#idVerifier').hostNodes()).toHaveLength(1) - }) - - it('should take user go back', async () => { - const { component: testComponent, router } = await createTestComponent( - , - { - store, - path: VERIFY_COLLECTOR, - initialEntries: [ - '/', - formatUrl(VERIFY_COLLECTOR, { - registrationId: 'mockBirth1234', - eventType: EventType.Birth, - collector: 'mother' - }) - ] - } - ) - - testComponent - .find('#action_page_back_button') - .hostNodes() - .simulate('click') - - await new Promise((resolve) => { - setTimeout(resolve, 500) - }) - - testComponent.update() - - expect(router.state.location.pathname).toBe('/') - }) - - describe('when informant is collector', () => { - let testComponent: TestComponentWithRouteMock - beforeAll(() => { - // @ts-ignore - store.dispatch(storeDeclaration(deathDeclaration)) - }) - beforeEach(async () => { - testComponent = await createTestComponent(, { - store, - path: VERIFY_COLLECTOR, - initialEntries: [ - formatUrl(VERIFY_COLLECTOR, { - registrationId: 'mockDeath1234', - eventType: EventType.Death, - collector: 'informant' - }) - ] - }) - }) - - it('renders idVerifier compomnent', () => { - expect( - testComponent.component.find('#idVerifier').hostNodes() - ).toHaveLength(1) - }) - - it('clicking on yes button takes user to review certificate if there is no fee', () => { - testComponent.component - .find('#idVerifier') - .find('#verifyPositive') - .hostNodes() - .simulate('click') - - expect(testComponent.router.state.location.pathname).toContain('review') - }) - - describe('when father is collector', () => { - let testComponent: TestComponentWithRouteMock - beforeAll(() => { - // @ts-ignore - store.dispatch(storeDeclaration(birthDeclaration)) - }) - beforeEach(async () => { - testComponent = await createTestComponent(, { - store, - path: VERIFY_COLLECTOR, - initialEntries: [ - formatUrl(VERIFY_COLLECTOR, { - registrationId: 'mockBirth1234', - eventType: EventType.Death, - collector: 'father' - }) - ] - }) - }) - - it('clicking on send button on modal takes user to payment if there is fee', () => { - testComponent.component - .find('#idVerifier') - .find('#verifyNegative') - .hostNodes() - .simulate('click') - - testComponent.component.update() - - testComponent.component - .find('#withoutVerificationPrompt') - .find('#send') - .hostNodes() - .simulate('click') - - expect(testComponent.router.state.location.pathname).toContain( - 'payment' - ) - }) - }) - - it('clicking on no button shows up modal', () => { - testComponent.component - .find('#idVerifier') - .find('#verifyNegative') - .hostNodes() - .simulate('click') - - testComponent.component.update() - - expect( - testComponent.component.find('#withoutVerificationPrompt').hostNodes() - ).toHaveLength(1) - }) - - it('clicking on cancel button hides the modal', () => { - testComponent.component - .find('#idVerifier') - .find('#verifyNegative') - .hostNodes() - .simulate('click') - - testComponent.component.update() - - testComponent.component - .find('#withoutVerificationPrompt') - .find('#cancel') - .hostNodes() - .simulate('click') - - testComponent.component.update() - - expect( - testComponent.component.find('#withoutVerificationPrompt').hostNodes() - ).toHaveLength(0) - }) - }) - }) - - describe('in case of death declaration renders idVerifier component', () => { - beforeAll(() => { - // @ts-ignore - store.dispatch(storeDeclaration(deathDeclaration)) - }) - - it('when informant is collector', async () => { - const { component: testComponent } = await createTestComponent( - , - { - store, - path: VERIFY_COLLECTOR, - initialEntries: [ - formatUrl(VERIFY_COLLECTOR, { - registrationId: 'mockDeath1234', - eventType: EventType.Death, - collector: 'informant' - }) - ] - } - ) - - expect(testComponent.find('#idVerifier').hostNodes()).toHaveLength(1) - }) - }) -}) diff --git a/packages/client/src/views/PrintCertificate/collectorForm/CollectorForm.test.tsx b/packages/client/src/views/PrintCertificate/collectorForm/CollectorForm.test.tsx index 53eaa0b261f..23674aaea66 100644 --- a/packages/client/src/views/PrintCertificate/collectorForm/CollectorForm.test.tsx +++ b/packages/client/src/views/PrintCertificate/collectorForm/CollectorForm.test.tsx @@ -13,7 +13,6 @@ import { createTestComponent, selectOption, getFileFromBase64String, - validImageB64String, inValidImageB64String, mockDeclarationData, mockDeathDeclarationData, @@ -22,6 +21,7 @@ import { flushPromises } from '@client/tests/util' import { ReactWrapper } from 'enzyme' +import { validImageB64String } from '@client/tests/mock-offline-data' import * as React from 'react' import { CollectorForm } from './CollectorForm' import { waitFor, waitForElement } from '@client/tests/wait-for-element' diff --git a/packages/client/src/views/RecordAudit/ActionMenu.test.tsx b/packages/client/src/views/RecordAudit/ActionMenu.test.tsx deleted file mode 100644 index bf7bd9ae544..00000000000 --- a/packages/client/src/views/RecordAudit/ActionMenu.test.tsx +++ /dev/null @@ -1,2940 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ - -import * as React from 'react' -import { - createTestComponent, - flushPromises, - setScopes -} from '@client/tests/util' -import { createStore } from '@client/store' -import { ReactWrapper } from 'enzyme' -import { - DOWNLOAD_STATUS, - IDeclaration, - SUBMISSION_STATUS -} from '@client/declarations' -import { ActionMenu } from './ActionMenu' -import { SCOPES } from '@opencrvs/commons/client' -import { EventType } from '@client/utils/gateway' -import { vi } from 'vitest' - -const defaultDeclaration = { - id: '65c48a2b-68dd-4a7e-8868-d2bb1fd27844', - name: 'Elissabe Sandrava', - type: EventType.Birth, - status: SUBMISSION_STATUS.REGISTERED, - assignment: { - practitionerId: '73da21a1-4b8b-4174-83ca-122d829cb6ec', - firstName: 'Kennedy', - lastName: 'Mweene', - officeName: 'Ibombo District Office', - avatarURL: - 'https://eu.ui-avatars.com/api/?background=DEE5F2&color=222&name=Kennedy Mweene', - __typename: 'AssignmentData' - }, - trackingId: 'BYSQC5A', - dateOfBirth: '', - placeOfBirth: '' -} as unknown as IDeclaration - -const draftBirthDownloaded = { - id: '65c48a2b-68dd-4a7e-8868-d2bb1fd27844', - data: {}, - event: EventType.Birth, - action: 'load declaration data for review', - downloadStatus: DOWNLOAD_STATUS.DOWNLOADED -} as unknown as IDeclaration - -const draftDeathDownloaded = { - id: '65c48a2b-68dd-4a7e-8868-d2bb1fd27844', - data: {}, - event: EventType.Death, - action: 'load declaration data for review', - downloadStatus: DOWNLOAD_STATUS.DOWNLOADED -} as unknown as IDeclaration - -const draftBirthNotDownloaded = { - id: '65c48a2b-68dd-4a7e-8868-d2bb1fd27844', - data: {}, - event: EventType.Birth, - action: 'load declaration data for review', - downloadStatus: DOWNLOAD_STATUS.READY_TO_DOWNLOAD -} as unknown as IDeclaration - -const draftDeathNotDownloaded = { - id: '65c48a2b-68dd-4a7e-8868-d2bb1fd27844', - data: {}, - event: EventType.Death, - action: 'load declaration data for review', - downloadStatus: DOWNLOAD_STATUS.READY_TO_DOWNLOAD -} as unknown as IDeclaration - -enum ACTION_STATUS { - HIDDEN = 'Hidden', - ENABLED = 'Enabled', - DISABLED = 'Disabled', - MULTIPLE = 'Multiple' -} - -enum ACTION { - VIEW_RECORD = 'View record', - VIEW_DECLARATION = 'View declaration', - REVIEW_DECLARATION = 'Review declaration', - REVIEW_POTENTIAL_DUPLICATE = 'Review potential duplicate', - REVIEW_CORRECTION_REQUEST = 'Review correction request', - UPDATE_DECLARATION = 'Update declaration', - ARCHIVE_RECORD = 'Archive Record', - REINSTATE_RECORD = 'Reinstate Record', - PRINT_RECORD = 'Print certified copy', - ISSUE_CERTIFICATE = 'Issue certificate', - CORRECT_RECORD = 'Correct Record', - DELETE_DECLARATION = 'Delete Declaration', - UNASSIGN = 'Unassign' -} - -const actionStatus = ( - component: ReactWrapper<{}, {}>, - targetAction: string[] -): { status: ACTION_STATUS; node?: ReactWrapper<{}, {}> } => { - const target = component - .find('li') - .map((a) => a) - .filter((a) => targetAction.includes(a.text())) - - if (target.length === 0) return { status: ACTION_STATUS.HIDDEN } - if (target.length > 1) return { status: ACTION_STATUS.MULTIPLE } - - return target[0].prop('disabled') - ? { status: ACTION_STATUS.DISABLED } - : { status: ACTION_STATUS.ENABLED, node: target[0] } -} - -describe('View action', () => { - it('Draft', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.VIEW_DECLARATION]) - - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In progress', async () => { - const { store } = createStore() - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.VIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - expect(router.state.location.pathname).toContain( - defaultDeclaration.id + '/viewRecord' - ) - }) - - it('In review', async () => { - const { store } = createStore() - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.VIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - expect(router.state.location.pathname).toContain( - defaultDeclaration.id + '/viewRecord' - ) - }) - - it('Potential duplicate', async () => { - const { store } = createStore() - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.VIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - expect(router.state.location.pathname).toContain( - defaultDeclaration.id + '/viewRecord' - ) - }) - - it('Requires update', async () => { - const { store } = createStore() - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.VIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - expect(router.state.location.pathname).toContain( - defaultDeclaration.id + '/viewRecord' - ) - }) - - it('Validated', async () => { - const { store } = createStore() - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.VIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - expect(router.state.location.pathname).toContain( - defaultDeclaration.id + '/viewRecord' - ) - }) - - it('Archived', async () => { - const { store } = createStore() - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.VIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - expect(router.state.location.pathname).toContain( - defaultDeclaration.id + '/viewRecord' - ) - }) - - it('Registered', async () => { - const { store } = createStore() - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.VIEW_RECORD]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - expect(router.state.location.pathname).toContain( - defaultDeclaration.id + '/viewRecord' - ) - }) - - it('Registered + Printed in advance', async () => { - const { store } = createStore() - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.VIEW_RECORD]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - expect(router.state.location.pathname).toContain( - defaultDeclaration.id + '/viewRecord' - ) - }) - - it('Pending correction', async () => { - const { store } = createStore() - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.VIEW_RECORD]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - expect(router.state.location.pathname).toContain( - defaultDeclaration.id + '/viewRecord' - ) - }) -}) - -describe('Review action', () => { - it('Draft', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In progress', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In review - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ - ACTION.REVIEW_DECLARATION - ]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - await flushPromises() - - expect(router.state.location.pathname).toContain( - 'reviews/' + defaultDeclaration.id - ) - }) - - it('In review - Not downloaded - Has Scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('In review - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Potential duplicate', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Requires update', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Validated - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ - ACTION.REVIEW_DECLARATION - ]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - await flushPromises() - - expect(router.state.location.pathname).toContain( - 'reviews/' + defaultDeclaration.id - ) - }) - - it('Validated - Not downloaded - Has scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('Validated - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Archived', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered + Printed in advance', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Pending correction', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) -}) - -describe('Review potential duplicate action', () => { - it('Draft', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REVIEW_DUPLICATES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_POTENTIAL_DUPLICATE - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In progress', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REVIEW_DUPLICATES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_POTENTIAL_DUPLICATE - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In review', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REVIEW_DUPLICATES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_POTENTIAL_DUPLICATE - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Potential duplicate - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_POTENTIAL_DUPLICATE - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Potential duplicate - Not downloaded - Has Scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REVIEW_DUPLICATES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_POTENTIAL_DUPLICATE - ]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('Potential duplicate - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REVIEW_DUPLICATES], store) - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ - ACTION.REVIEW_POTENTIAL_DUPLICATE - ]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - await flushPromises() - - expect(router.state.location.pathname).toContain( - 'reviews/' + defaultDeclaration.id - ) - }) - - it('Requires update', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REVIEW_DUPLICATES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Validated - Does not have scope', async () => { - const { store } = createStore() - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REVIEW_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Archived', async () => { - const { store } = createStore() - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_POTENTIAL_DUPLICATE - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Validated', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REVIEW_DUPLICATES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_POTENTIAL_DUPLICATE - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Archived', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REVIEW_DUPLICATES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_POTENTIAL_DUPLICATE - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REVIEW_DUPLICATES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_POTENTIAL_DUPLICATE - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered + Printed in advance', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REVIEW_DUPLICATES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_POTENTIAL_DUPLICATE - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Pending correction', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REVIEW_DUPLICATES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_POTENTIAL_DUPLICATE - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) -}) - -describe('Review correction action', () => { - it('Draft', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_CORRECTION_REQUEST - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In progress', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_CORRECTION_REQUEST - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In review', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_CORRECTION_REQUEST - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Potential duplicate', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_CORRECTION_REQUEST - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Requires update', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_CORRECTION_REQUEST - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Validated', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_CORRECTION_REQUEST - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Archived', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_CORRECTION_REQUEST - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_CORRECTION_REQUEST - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered + Printed in advance', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_CORRECTION_REQUEST - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Pending correction - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ - ACTION.REVIEW_CORRECTION_REQUEST - ]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - await flushPromises() - - expect(router.state.location.pathname).toContain( - 'review-correction/' + defaultDeclaration.id - ) - }) - - it('Pending correction - Not downloaded - Has scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_CORRECTION_REQUEST - ]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('Pending correction - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ - ACTION.REVIEW_CORRECTION_REQUEST - ]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) -}) - -describe('Update action', () => { - it('Draft', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ - ACTION.UPDATE_DECLARATION - ]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - await flushPromises() - - expect(router.state.location.pathname).toContain( - 'drafts/' + defaultDeclaration.id - ) - }) - - it('In progress - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ - ACTION.UPDATE_DECLARATION - ]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - await flushPromises() - - expect(router.state.location.pathname).toContain( - 'reviews/' + defaultDeclaration.id - ) - }) - - it('In progress - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.UPDATE_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In progress - Not downloaded - Has scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.UPDATE_DECLARATION]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('In review', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.UPDATE_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Potential duplicate', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.UPDATE_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Requires update - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ - ACTION.UPDATE_DECLARATION - ]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - await flushPromises() - - expect(router.state.location.pathname).toContain( - 'reviews/' + defaultDeclaration.id - ) - }) - - it('Requires update - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.UPDATE_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Requires update - Not downloaded - Has scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.UPDATE_DECLARATION]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('Validated', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.UPDATE_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Archived', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.UPDATE_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.UPDATE_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered + Printed in advance', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.UPDATE_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Pending correction - Downloaded', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTER], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.UPDATE_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) -}) - -describe('Archive action', () => { - it('Draft', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_ARCHIVE], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In progress - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_ARCHIVE], store) - const toggleDisplayDialogMock = vi.fn() - const { component } = await createTestComponent( - , - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.ENABLED) - node?.simulate('click') - - expect(toggleDisplayDialogMock).toHaveBeenCalled() - }) - - it('In progress - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In progress - Not downloaded - Has scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_ARCHIVE], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('In review - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_ARCHIVE], store) - const toggleDisplayDialogMock = vi.fn() - const { component } = await createTestComponent( - , - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.ENABLED) - node?.simulate('click') - - expect(toggleDisplayDialogMock).toHaveBeenCalled() - }) - - it('In review - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In review - Not downloaded - Has scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_ARCHIVE], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('Requires update - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_ARCHIVE], store) - const toggleDisplayDialogMock = vi.fn() - const { component } = await createTestComponent( - , - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.ENABLED) - node?.simulate('click') - - expect(toggleDisplayDialogMock).toHaveBeenCalled() - }) - - it('Requires update - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Requires update - Not downloaded - Has scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_ARCHIVE], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('Validated - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_ARCHIVE], store) - const toggleDisplayDialogMock = vi.fn() - const { component } = await createTestComponent( - , - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.ENABLED) - node?.simulate('click') - - expect(toggleDisplayDialogMock).toHaveBeenCalled() - }) - - it('Validated - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Validated - Not downloaded - Has scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_ARCHIVE], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('Archived', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_ARCHIVE], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_ARCHIVE], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered + Printed in advance', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_ARCHIVE], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Pending correction', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_ARCHIVE], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ARCHIVE_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) -}) - -describe('Reinstate action', () => { - it('Draft', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_REINSTATE], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REINSTATE_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Archived - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_REINSTATE], store) - const toggleDisplayDialogMock = vi.fn() - const { component } = await createTestComponent( - , - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.REINSTATE_RECORD]) - expect(status).toBe(ACTION_STATUS.ENABLED) - node?.simulate('click') - expect(toggleDisplayDialogMock).toHaveBeenCalled() - }) - - it('Archived - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REINSTATE_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Archived - Not downloaded - Has scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_REINSTATE], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REINSTATE_RECORD]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('Registered', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_DECLARATION_REINSTATE], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.REINSTATE_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) -}) - -describe('Print action', () => { - it('Draft', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.PRINT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In progress', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.PRINT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In review', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.PRINT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Potential duplicate', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.PRINT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Requires update', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.PRINT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Validated', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.PRINT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Archived', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.PRINT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.PRINT_RECORD]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - await flushPromises() - expect(router.state.location.pathname).toContain( - 'cert/collector/' + defaultDeclaration.id - ) - }) - - it('Registered - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.PRINT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered - Not downloaded - Has scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.PRINT_RECORD]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('Registered + Printed in advance', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.PRINT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Pending correction', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.PRINT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) -}) - -describe('Issue action', () => { - it('Draft', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ISSUE_CERTIFICATE]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In progress', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ISSUE_CERTIFICATE]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In review', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ISSUE_CERTIFICATE]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Potential duplicate', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ISSUE_CERTIFICATE]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Requires update', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ISSUE_CERTIFICATE]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Validated', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ISSUE_CERTIFICATE]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Archived', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ISSUE_CERTIFICATE]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ISSUE_CERTIFICATE]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered + Printed in advance - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.ISSUE_CERTIFICATE]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - await flushPromises() - - expect(router.state.location.pathname).toContain( - 'issue/' + defaultDeclaration.id - ) - }) - - it('Registered + Printed in advance - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ISSUE_CERTIFICATE]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered + Printed in advance - Not downloaded - Has scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ISSUE_CERTIFICATE]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('Pending correction', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.ISSUE_CERTIFICATE]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) -}) - -describe('Correct action', () => { - it('Draft', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In progress', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In review', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Potential duplicate', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Requires update', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Validated', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Archived', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - await flushPromises() - expect(router.state.location.pathname).toContain( - 'correction/' + defaultDeclaration.id - ) - }) - - it('Registered - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered - Not downloaded - Has scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('Registered + Printed in advance - Does not have scope', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Registered + Printed in advance - Not downloaded - Has scope', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.DISABLED) - }) - - it('Registered + Printed in advance - Assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component, router } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - - await flushPromises() - expect(router.state.location.pathname).toContain( - 'correction/' + defaultDeclaration.id - ) - }) - - it('Pending correction', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_REGISTRATION_CORRECT], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.CORRECT_RECORD]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) -}) - -describe('Delete declaration action', () => { - it('Draft', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status, node } = actionStatus(component, [ - ACTION.DELETE_DECLARATION - ]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - expect(component.find('h1').hostNodes().text()).toEqual('Delete draft?') - }) - - it('In progress', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.DELETE_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('In review', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status } = actionStatus(component, [ACTION.DELETE_DECLARATION]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) -}) - -describe('Unassign action', () => { - const Assignment = 'Assigned to Kennedy Mweene at Ibombo District Office' - it('Has scope - assigned to someone else', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_UNASSIGN_OTHERS], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status: assignmentStatus } = actionStatus(component, [Assignment]) - expect(assignmentStatus).toBe(ACTION_STATUS.ENABLED) - - const { status, node } = actionStatus(component, [ACTION.UNASSIGN]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - expect(component.find('h2').hostNodes().text()).toEqual('Unassign record?') - }) - - it('Does not have scope - assigned to someone else', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status: assignmentStatus } = actionStatus(component, [Assignment]) - expect(assignmentStatus).toBe(ACTION_STATUS.ENABLED) - - const { status } = actionStatus(component, [ACTION.UNASSIGN]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) - - it('Assigned to self', async () => { - const { store } = createStore() - setScopes([], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status: assignmentStatus } = actionStatus(component, [Assignment]) - expect(assignmentStatus).toBe(ACTION_STATUS.HIDDEN) - - const { status, node } = actionStatus(component, [ACTION.UNASSIGN]) - expect(status).toBe(ACTION_STATUS.ENABLED) - - node?.simulate('click') - expect(component.find('h2').hostNodes().text()).toEqual('Unassign record?') - }) - - it('Not assigned', async () => { - const { store } = createStore() - setScopes([SCOPES.RECORD_UNASSIGN_OTHERS], store) - const { component } = await createTestComponent( - {}} - />, - { store } - ) - - const { status: assignmentStatus } = actionStatus(component, [Assignment]) - expect(assignmentStatus).toBe(ACTION_STATUS.HIDDEN) - - const { status } = actionStatus(component, [ACTION.UNASSIGN]) - expect(status).toBe(ACTION_STATUS.HIDDEN) - }) -}) diff --git a/packages/client/src/views/RecordAudit/RecordAudit.test.tsx b/packages/client/src/views/RecordAudit/RecordAudit.test.tsx deleted file mode 100644 index f274faa9b18..00000000000 --- a/packages/client/src/views/RecordAudit/RecordAudit.test.tsx +++ /dev/null @@ -1,431 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ - -import * as React from 'react' -import { - createTestComponent, - mockDeclarationData, - flushPromises, - mockDeathDeclarationData, - mockMarriageDeclarationData, - userDetails -} from '@client/tests/util' -import { RecordAudit } from './RecordAudit' -import { createStore } from '@client/store' -import { ReactWrapper } from 'enzyme' -import { - createDeclaration, - storeDeclaration, - IDeclaration -} from '@client/declarations' -import { EventType } from '@client/utils/gateway' -import { formatUrl } from '@client/navigation' -import { DECLARATION_RECORD_AUDIT } from '@client/navigation/routes' -import type { GQLBirthEventSearchSet } from '@client/utils/gateway-deprecated-do-not-use' -import { FETCH_DECLARATION_SHORT_INFO } from './queries' -import { waitForElement } from '@client/tests/wait-for-element' - -const declaration: IDeclaration = createDeclaration( - EventType.Birth, - mockDeclarationData -) -declaration.data.registration = { - ...declaration.data.registration, - informantType: 'MOTHER', - contactPoint: { - value: 'MOTHER', - nestedFields: { registrationPhone: '01557394986' } - } -} - -declaration.data.history = [ - // @ts-ignore - { - date: new Date().toString(), - regStatus: 'STARTED', - user: { - id: userDetails.userMgntUserID, - name: userDetails.name, - role: userDetails.role - }, - office: userDetails.primaryOffice, - comments: [], - input: [], - output: [] - } -] - -describe('Record audit summary for a draft birth declaration', () => { - let component: ReactWrapper<{}, {}> - - beforeEach(async () => { - const { store } = createStore() - store.dispatch(storeDeclaration(declaration)) - const { component: testComponent } = await createTestComponent( - , - { - store, - path: DECLARATION_RECORD_AUDIT, - initialEntries: [ - formatUrl(DECLARATION_RECORD_AUDIT, { - tab: 'inProgressTab', - declarationId: declaration.id - }) - ] - } - ) - component = testComponent - }) - - it('Record Audit page loads properly', async () => { - expect(component.exists('RecordAuditBody')).toBeTruthy() - }) - - it('Check values for saved declarations', async () => { - expect( - component.find({ 'data-testid': 'status-value' }).hostNodes().text() - ).toBe('Draft') - expect( - component.find({ 'data-testid': 'type-value' }).hostNodes().text() - ).toBe('Birth') - expect(component.exists({ 'data-testid': 'brn-value' })).toBeFalsy() - expect( - component.find({ 'data-testid': 'placeOfBirth-value' }).hostNodes() - ).toHaveLength(1) - }) -}) - -describe('Record audit summary for a draft death declaration', () => { - let component: ReactWrapper<{}, {}> - - beforeEach(async () => { - const { store } = createStore() - const deathDeclaration = createDeclaration( - EventType.Death, - mockDeathDeclarationData - ) - - store.dispatch(storeDeclaration(deathDeclaration)) - const { component: testComponent } = await createTestComponent( - , - { - store, - initialEntries: [ - formatUrl(DECLARATION_RECORD_AUDIT, { - tab: 'inProgressTab', - declarationId: deathDeclaration.id - }) - ], - path: DECLARATION_RECORD_AUDIT - } - ) - component = testComponent - }) - - it('Record Audit page loads properly', async () => { - expect(component.exists('RecordAuditBody')).toBeTruthy() - }) - - it('Check values for saved declarations', async () => { - expect( - component.find({ 'data-testid': 'status-value' }).hostNodes().text() - ).toBe('Draft') - expect( - component.find({ 'data-testid': 'type-value' }).hostNodes().text() - ).toBe('Death') - expect(component.exists({ 'data-testid': 'drn-value' })).toBeFalsy() - expect( - component.find({ 'data-testid': 'placeOfDeath-value' }).hostNodes() - ).toHaveLength(1) - }) -}) - -describe('Record audit summary for a draft marriage declaration', () => { - let component: ReactWrapper<{}, {}> - - beforeEach(async () => { - const { store } = createStore() - const marriageDeclaration = createDeclaration( - EventType.Marriage, - mockMarriageDeclarationData - ) - - store.dispatch(storeDeclaration(marriageDeclaration)) - const { component: testComponent } = await createTestComponent( - , - { - store, - path: DECLARATION_RECORD_AUDIT, - initialEntries: [ - formatUrl(DECLARATION_RECORD_AUDIT, { - tab: 'inProgressTab', - declarationId: marriageDeclaration.id - }) - ] - } - ) - component = testComponent - }) - - it('Record Audit page loads properly', async () => { - expect(component.exists('RecordAuditBody')).toBeTruthy() - }) - - it('Check values for saved declarations', async () => { - expect( - component.find({ 'data-testid': 'status-value' }).hostNodes().text() - ).toBe('Draft') - expect( - component.find({ 'data-testid': 'type-value' }).hostNodes().text() - ).toBe('Marriage') - expect(component.exists({ 'data-testid': 'drn-value' })).toBeFalsy() - expect( - component.find({ 'data-testid': 'placeOfMarriage-value' }).hostNodes() - ).toHaveLength(1) - }) -}) - -describe('Record audit summary for WorkQueue declarations', () => { - let component: ReactWrapper<{}, {}> - - beforeEach(async () => { - const { store } = createStore() - - store.getState().workqueueState.workqueue.data.inProgressTab.results = [ - { - id: 'db097901-feba-4f71-a1ae-d3d46289d2d5', - type: EventType.Birth, - registration: { - status: 'DRAFT', - contactNumber: '+8801622688231', - trackingId: 'BN99CGM', - registeredLocationId: '425b9cab-6ec3-47b3-bb8b-aee1b1afe4fc', - createdAt: '1597657903690' - }, - operationHistories: [ - { - operationType: 'DRAFT', - operatedOn: '2020-08-17T09:51:43.350Z', - operatorRole: 'FIELD_AGENT', - operatorName: [ - { - firstNames: 'Shakib', - familyName: 'Al Hasan', - use: 'en' - }, - { - firstNames: 'সাকিব', - familyName: 'হাসান', - use: 'bn' - } - ], - operatorOfficeName: 'Baniajan Union Parishad', - operatorOfficeAlias: ['বানিয়াজান ইউনিয়ন পরিষদ'] - } - ], - childName: [ - { - firstNames: 'Shakib', - familyName: 'Al Hasan', - use: 'en' - }, - { - firstNames: 'সাকিব', - familyName: 'হাসান', - use: 'bn' - } - ] - } as GQLBirthEventSearchSet - ] - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: DECLARATION_RECORD_AUDIT, - initialEntries: [ - formatUrl(DECLARATION_RECORD_AUDIT, { - tab: 'inProgressTab', - declarationId: 'db097901-feba-4f71-a1ae-d3d46289d2d5' - }) - ] - } - ) - component = testComponent - }) - - it('Record Audit page loads properly', async () => { - expect(component.exists('RecordAuditBody')).toBeTruthy() - }) - - it('Check values for WQ declarations', async () => { - expect( - component.find({ 'data-testid': 'status-value' }).hostNodes().text() - ).toBe('Draft') - expect( - component.find({ 'data-testid': 'type-value' }).hostNodes().text() - ).toBe('Birth') - expect(component.find('#content-name').hostNodes().text()).toBe( - 'Al Hasan Shakib' - ) - expect( - component - .find({ - 'data-testid': 'placeOfBirth-value', - 'data-testclass': 'locked' - }) - .hostNodes() - ).toHaveLength(1) - expect( - component - .find({ - 'data-testid': 'placeOfDeath-value', - 'data-testclass': 'locked' - }) - .hostNodes() - ).toHaveLength(0) - }) -}) - -describe('Record audit summary for GQLQuery', () => { - let component: ReactWrapper<{}, {}> - - beforeEach(async () => { - const { store } = createStore() - - const mocks = [ - { - request: { - query: FETCH_DECLARATION_SHORT_INFO, - variables: { - id: '956281c9-1f47-4c26-948a-970dd23c4094' - } - }, - result: { - data: { - fetchRegistration: { - id: '956281c9-1f47-4c26-948a-970dd23c4094', - registration: { - type: 'DEATH', - trackingId: 'DG6PECX', - status: [ - { - type: 'REGISTERED' - } - ] - }, - deceased: { - name: [ - { - use: 'bn', - firstNames: 'ক ম আব্দুল্লাহ আল আমিন ', - familyName: 'খান' - } - ] - } - } - } - } - } - ] - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: DECLARATION_RECORD_AUDIT, - initialEntries: [ - formatUrl(DECLARATION_RECORD_AUDIT, { - tab: 'search', - declarationId: '956281c9-1f47-4c26-948a-970dd23c4094' - }) - ], - graphqlMocks: mocks - } - ) - component = testComponent - - await flushPromises() - component.update() - await waitForElement(component, 'RecordAuditBody') - }) - - it('Record Audit page loads properly', async () => { - expect(component.exists('RecordAuditBody')).toBeTruthy() - }) - - it('Check values for GQL declarations', async () => { - expect( - component.find({ 'data-testid': 'status-value' }).hostNodes().text() - ).toBe('Registered') - expect( - component.find({ 'data-testid': 'type-value' }).hostNodes().text() - ).toBe('Death') - expect( - component - .find({ - 'data-testid': 'placeOfBirth-value', - 'data-testclass': 'locked' - }) - .hostNodes() - ).toHaveLength(0) - expect( - component - .find({ - 'data-testid': 'placeOfDeath-value', - 'data-testclass': 'locked' - }) - .hostNodes() - ).toHaveLength(1) - }) -}) - -describe('Record audit summary for unsuccesful GQLQuery', () => { - let component: ReactWrapper<{}, {}> - - beforeEach(async () => { - const { store } = createStore() - - const mocks = [ - { - request: { - query: FETCH_DECLARATION_SHORT_INFO, - variables: { - id: '956281c9-1f47-4c26-948a-970dd23c4094' - } - } - } - ] - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: DECLARATION_RECORD_AUDIT, - initialEntries: [ - formatUrl(DECLARATION_RECORD_AUDIT, { - tab: 'search', - declarationId: '956281c9-1f47-4c26-948a-970dd23c4094' - }) - ], - graphqlMocks: mocks - } - ) - component = testComponent - - await flushPromises() - component.update() - }) - - it('Redirect to home page', async () => { - expect(window.location.href).not.toContain('/record-audit') - }) -}) diff --git a/packages/client/src/views/RegisterForm/DeclarationForm.test.tsx b/packages/client/src/views/RegisterForm/DeclarationForm.test.tsx deleted file mode 100644 index fa516e2eab5..00000000000 --- a/packages/client/src/views/RegisterForm/DeclarationForm.test.tsx +++ /dev/null @@ -1,406 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { - createDeclaration, - IDeclaration, - IUserData, - storeDeclaration -} from '@client/declarations' -import { formatUrl } from '@client/navigation' -import { DRAFT_BIRTH_PARENT_FORM } from '@client/navigation/routes' -import { storage } from '@client/storage' -import { - createTestApp, - flushPromises, - getFileFromBase64String, - goToChildSection, - goToDocumentsSection, - goToFatherSection, - goToMotherSection, - goToSection, - selectOption, - setScopes, - REGISTRATION_AGENT_DEFAULT_SCOPES, - waitForReady, - setPageVisibility, - userDetails, - validImageB64String -} from '@client/tests/util' -import { ReactWrapper } from 'enzyme' -import { Store } from 'redux' -import { SCOPES } from '@opencrvs/commons/client' -import { EventType } from '@client/utils/gateway' -import { waitForElement } from '@client/tests/wait-for-element' -import { createMemoryRouter } from 'react-router-dom' -import { Mock, vi } from 'vitest' - -describe('when user starts a new declaration', () => { - describe('In case of insecured page show unlock screen', () => { - let draft: IDeclaration - let app: ReactWrapper - let store: Store - - beforeEach(async () => { - await flushPromises() - const userData: IUserData[] = [ - { - userID: userDetails.userMgntUserID, - userPIN: - '$2a$10$xQBLcbPgGQNu9p6zVchWuu6pmCrQIjcb6k2W1PIVUxVTE/PumWM82', - declarations: [] - } - ] - - const indexedDB = { - USER_DETAILS: JSON.stringify(userDetails), - USER_DATA: JSON.stringify(userData), - screenLock: 'true' - } - ;(storage.getItem as Mock).mockImplementation( - (param: keyof typeof indexedDB) => Promise.resolve(indexedDB[param]) - ) - - draft = createDeclaration(EventType.Birth) - - const testApp = await createTestApp( - { waitUntilOfflineCountryConfigLoaded: true }, - [ - formatUrl(DRAFT_BIRTH_PARENT_FORM, { - declarationId: draft.id.toString() - }) - ] - ) - app = testApp.app - store = testApp.store - store.dispatch(storeDeclaration(draft)) - - setScopes([SCOPES.RECORD_DECLARE_BIRTH], store) - - await store.dispatch(storeDeclaration(draft)) - }) - - it('renders unlock screen', async () => { - await waitForElement(app, '#unlockPage') - }) - }) - - describe('when secured', () => { - let app: ReactWrapper - - let store: Store - let router: ReturnType - - beforeEach(async () => { - await flushPromises() - const testApp = await createTestApp() - app = testApp.app - store = testApp.store - router = testApp.router - - setScopes(REGISTRATION_AGENT_DEFAULT_SCOPES, store) - await waitForReady(app) - }) - - describe('when user is in birth registration by parent informant view', () => { - let draft: IDeclaration - beforeEach(async () => { - await flushPromises() - const data = { - registration: { - informantType: { - value: '', - nestedFields: { otherInformantType: '' } - }, - contactPoint: { - value: '', - nestedFields: { registrationPhone: '' } - } - } - } - draft = createDeclaration(EventType.Birth, data) - - /* - * Needs to be done before storeDeclaration(draft) - * so offline declarations wouldn't override the dispatched ones - */ - store.dispatch(storeDeclaration(draft)) - - await router.navigate( - formatUrl(DRAFT_BIRTH_PARENT_FORM, { - declarationId: draft.id.toString() - }) - ) - - app.update() - await flushPromises() - - await goToChildSection(app) - }) - - describe('when user types in something and press continue', () => { - beforeEach(async () => { - await flushPromises() - - app - .find('#firstNamesEng') - .hostNodes() - .simulate('change', { - target: { id: 'firstNamesEng', value: 'hello' } - }) - - app.update() - await flushPromises() - - app.find('#next_section').hostNodes().simulate('click') - app.update() - - await flushPromises() - }) - it('redirect to home when pressed save and exit button', async () => { - app.find('#save-exit-btn').hostNodes().simulate('click') - await flushPromises() - app.update() - expect(window.location.href).toContain('/') - }) - it('check toggle menu toggle button handler', async () => { - app - .find('#eventToggleMenu-Dropdown-Content') - .hostNodes() - .simulate('click') - await flushPromises() - app.update() - expect( - app.find('#eventToggleMenu-Dropdown-Content').hostNodes().length - ).toEqual(1) - }) - it('check toggle menu item handler', async () => { - const menuLink = await waitForElement( - app, - '#eventToggleMenu-Dropdown-Content' - ) - menuLink.hostNodes().simulate('click') - await flushPromises() - app.update() - - await waitForElement(app, '#eventToggleMenu-Dropdown-Content') - app - .find('#eventToggleMenu-Dropdown-Content') - .hostNodes() - .simulate('click') - await flushPromises() - app.update() - - expect(window.location.href).toContain('/') - }) - }) - - describe('when user enters childBirthDate and clicks to documents page', () => { - beforeEach(async () => { - await flushPromises() - - Date.now = vi.fn(() => 1549607679507) // 08-02-2019 - await waitForElement(app, '#childBirthDate-dd') - app - .find('#childBirthDate-dd') - .hostNodes() - .simulate('change', { - target: { id: 'childBirthDate-dd', value: '19' } - }) - app - .find('#childBirthDate-mm') - .hostNodes() - .simulate('change', { - target: { id: 'childBirthDate-mm', value: '11' } - }) - app - .find('#childBirthDate-yyyy') - .hostNodes() - .simulate('change', { - target: { id: 'childBirthDate-yyyy', value: '2018' } - }) - - app.update() - }) - - describe('when user goes to documents page', () => { - beforeEach(async () => { - await flushPromises() - await goToDocumentsSection(app) - }) - - it('renders list of document upload field', async () => { - await flushPromises() - await waitForElement(app, '#form_section_id_documents-view-group') - const fileInputs = app - .find('#form_section_id_documents-view-group') - .find('section') - .children().length - - expect(fileInputs).toEqual(5) - }) - it('still renders list of document upload field even when page is hidden - allows use of camera', async () => { - setPageVisibility(false) - await flushPromises() - app.update() - await flushPromises() - - const fileInputs = app - .find('#form_section_id_documents-view-group') - .find('section') - .children().length - - expect(fileInputs).toEqual(5) - }) - it('No error while uploading valid file', async () => { - await flushPromises() - app.update() - await flushPromises() - selectOption(app, '#uploadDocForMother', 'Birth certificate') - app.update() - app - .find('input[name="uploadDocForMother"][type="file"]') - .simulate('change', { - target: { - files: [ - getFileFromBase64String( - validImageB64String, - 'index.png', - 'image/png' - ) - ] - } - }) - - await flushPromises() - app.update() - - expect(app.find('#upload-error').exists()).toBe(false) - }) - it('Error while uploading invalid file', async () => { - await flushPromises() - app.update() - - selectOption(app, '#uploadDocForMother', 'Birth certificate') - app.update() - app - .find('input[name="uploadDocForMother"][type="file"]') - .simulate('change', { - target: { - files: [ - getFileFromBase64String( - validImageB64String, - 'index.bmp', - 'image/bmp' - ) - ] - } - }) - await flushPromises() - app.update() - expect(app.find('#upload-error').hostNodes().first().text()).toBe( - 'File format not supported. Please attach a png, jpf or pdf (max 5mb)' - ) - }) - }) - }) - - describe('when user goes to preview page', () => { - beforeEach(async () => { - await flushPromises() - await goToSection(app, 5) - app - .find('#btn_change_child_familyNameEng') - .hostNodes() - .first() - .simulate('click') - }) - - it('renders preview page', async () => { - await flushPromises() - const button = await waitForElement(app, '#back-to-review-button') - - button.hostNodes().simulate('click') - - const changeNameButton = await waitForElement( - app, - '#btn_change_child_familyNameEng' - ) - expect(changeNameButton.hostNodes()).toHaveLength(1) - }) - - it('should go to input field when user press change button to edit information', async () => { - await flushPromises() - const backToReviewButton = await waitForElement( - app, - '#back-to-review-button' - ) - - backToReviewButton.hostNodes().simulate('click') - - const changeNameButton = await waitForElement( - app, - '#btn_change_child_familyNameEng' - ) - - changeNameButton.hostNodes().first().simulate('click') - - const familyNameEngInputField = await waitForElement( - app, - '#familyNameEng' - ) - - expect(familyNameEngInputField.hostNodes()).toHaveLength(1) - }) - }) - describe('when user clicks the "mother" page', () => { - beforeEach(async () => { - await flushPromises() - await goToMotherSection(app) - }) - - it('changes to the mother details section', async () => { - await flushPromises() - app.update() - expect(router.state.location.pathname).toContain('mother') - }) - - it('hides everything with pinpad if page loses focus', async () => { - await flushPromises() - app.update() - - setPageVisibility(false) - await waitForElement(app, '#unlockPage') - }) - }) - describe('when user clicks the "father" page', () => { - beforeEach(async () => { - await flushPromises() - await goToFatherSection(app) - }) - - it('changes to the father details section', () => { - expect(router.state.location.pathname).toContain('father') - }) - }) - describe('when user is in document page', () => { - beforeEach(async () => { - await flushPromises() - await goToDocumentsSection(app) - }) - it('image upload field is rendered', async () => { - await flushPromises() - app.update() - expect(app.find('#upload_document').hostNodes()).toHaveLength(5) - }) - }) - }) - }) -}) diff --git a/packages/client/src/views/RegisterForm/PreviewForm.test.tsx b/packages/client/src/views/RegisterForm/PreviewForm.test.tsx deleted file mode 100644 index 7d662aaad07..00000000000 --- a/packages/client/src/views/RegisterForm/PreviewForm.test.tsx +++ /dev/null @@ -1,318 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { - createTestApp, - getItem, - mockDeclarationData, - goToEndOfForm, - waitForReady, - flushPromises, - setScopes, - REGISTRAR_DEFAULT_SCOPES -} from '@client/tests/util' -import { - DRAFT_BIRTH_PARENT_FORM, - REVIEW_EVENT_PARENT_FORM_PAGE, - REGISTRAR_HOME -} from '@client/navigation/routes' -import { - storeDeclaration, - IDeclaration, - SUBMISSION_STATUS, - createReviewDeclaration -} from '@client/declarations' -import { ReactWrapper } from 'enzyme' -import { Store } from 'redux' -import { SCOPES } from '@opencrvs/commons/client' -import { EventType } from '@client/utils/gateway' -import { v4 as uuid } from 'uuid' -import * as ReactApollo from '@apollo/client/react' -import { waitForElement } from '@client/tests/wait-for-element' -import { - birthDraftData, - birthReviewDraftData, - deathReviewDraftData, - marriageReviewDraftData -} from '@client/tests/mock-drafts' -import { vi } from 'vitest' -import { formatUrl } from '@client/navigation' -import { createBrowserRouter } from 'react-router-dom' - -describe('when user is previewing the form data', () => { - let app: ReactWrapper - let router: ReturnType - let store: Store - - beforeEach(async () => { - const testApp = await createTestApp( - { waitUntilOfflineCountryConfigLoaded: true }, - [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - event: 'death', - pageId: 'review' - }) - ] - ) - app = testApp.app - - router = testApp.router - store = testApp.store - setScopes(REGISTRAR_DEFAULT_SCOPES, store) - await waitForReady(app) - }) - - describe('when user is in the death review section', () => { - let customDraft: IDeclaration - - const registerScopeToken = - 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWdpc3RlciIsImNlcnRpZnkiLCJkZW1vIl0sImlhdCI6MTU0MjY4ODc3MCwiZXhwIjoxNTQzMjkzNTcwLCJhdWQiOlsib3BlbmNydnM6YXV0aC11c2VyIiwib3BlbmNydnM6dXNlci1tZ250LXVzZXIiLCJvcGVuY3J2czpoZWFydGgtdXNlciIsIm9wZW5jcnZzOmdhdGV3YXktdXNlciIsIm9wZW5jcnZzOm5vdGlmaWNhdGlvbi11c2VyIiwib3BlbmNydnM6d29ya2Zsb3ctdXNlciJdLCJpc3MiOiJvcGVuY3J2czphdXRoLXNlcnZpY2UiLCJzdWIiOiI1YmVhYWY2MDg0ZmRjNDc5MTA3ZjI5OGMifQ.ElQd99Lu7WFX3L_0RecU_Q7-WZClztdNpepo7deNHqzro-Cog4WLN7RW3ZS5PuQtMaiOq1tCb-Fm3h7t4l4KDJgvC11OyT7jD6R2s2OleoRVm3Mcw5LPYuUVHt64lR_moex0x_bCqS72iZmjrjS-fNlnWK5zHfYAjF2PWKceMTGk6wnI9N49f6VwwkinJcwJi6ylsjVkylNbutQZO0qTc7HRP-cBfAzNcKD37FqTRNpVSvHdzQSNcs7oiv3kInDN5aNa2536XSd3H-RiKR9hm9eID9bSIJgFIGzkWRd5jnoYxT70G0t03_mTVnDnqPXDtyI-lmerx24Ost0rQLUNIg' - - beforeEach(async () => { - getItem.mockReturnValue(registerScopeToken) - - await flushPromises() - const data = deathReviewDraftData - - customDraft = { id: uuid(), data, review: true, event: EventType.Death } - - store.dispatch(storeDeclaration(customDraft)) - router.navigate( - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: customDraft.id.toString(), - event: 'death', - pageId: 'review' - }) - ) - - await waitForElement(app, '#readyDeclaration') - }) - - it('successfully submits the review form', async () => { - vi.doMock('@apollo/client/react', () => ({ default: ReactApollo })) - - app.update().find('#registerDeclarationBtn').hostNodes().simulate('click') - app.update() - app.update().find('#submit_confirm').hostNodes().simulate('click') - }) - - it('rejecting declaration redirects to reject confirmation screen', async () => { - vi.doMock('@apollo/client/react', () => ({ default: ReactApollo })) - - app.find('#rejectDeclarationBtn').hostNodes().simulate('click') - - app - .find('#rejectionCommentForHealthWorker') - .hostNodes() - .simulate('change', { - target: { - id: 'rejectionCommentForHealthWorker', - value: 'reject reason' - } - }) - - app.find('#submit_reject_form').hostNodes().simulate('click') - - expect(router.state.location.pathname).toEqual( - `${REGISTRAR_HOME}/my-drafts/1` - ) - }) - }) - - describe('when user is in the preview section', () => { - let customDraft: IDeclaration - - beforeEach(async () => { - const data = birthDraftData - - customDraft = { - id: uuid(), - data, - event: EventType.Birth, - submissionStatus: SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT] - } - setScopes( - [SCOPES.RECORD_DECLARE_BIRTH, SCOPES.RECORD_SUBMIT_FOR_REVIEW], - store - ) - await flushPromises() - store.dispatch(storeDeclaration(customDraft)) - router.navigate( - DRAFT_BIRTH_PARENT_FORM.replace( - ':declarationId', - customDraft.id.toString() - ) - ) - - await waitForElement(app, '#readyDeclaration') - }) - - describe('when user clicks the "submit" button', () => { - beforeEach(async () => { - await goToEndOfForm(app) - }) - - it('check whether submit button is enabled or not', () => { - expect( - app.find('#submit_for_review').hostNodes().prop('disabled') - ).toBe(false) - }) - describe('All sections visited', () => { - it('Should be able to click SEND FOR REVIEW Button', () => { - expect( - app.find('#submit_for_review').hostNodes().prop('disabled') - ).toBe(false) - }) - describe('button clicked', () => { - beforeEach(async () => { - app.find('#submit_for_review').hostNodes().simulate('click') - }) - - it('confirmation screen should show up', () => { - expect(app.find('#submit_confirm').hostNodes()).toHaveLength(1) - }) - it('should redirect to home page', () => { - app.find('#submit_confirm').hostNodes().simulate('click') - expect(router.state.location.pathname).toBe( - `${REGISTRAR_HOME}/my-drafts/1` - ) - }) - }) - }) - }) - }) - - describe('when user is in the birth review section', () => { - let customDraft: IDeclaration - - beforeEach(async () => { - setScopes(REGISTRAR_DEFAULT_SCOPES, store) - await waitForReady(app) - await flushPromises() - const data = birthReviewDraftData - - customDraft = { id: uuid(), data, review: true, event: EventType.Birth } - store.dispatch(storeDeclaration(customDraft)) - router.navigate( - REVIEW_EVENT_PARENT_FORM_PAGE.replace( - ':declarationId', - customDraft.id.toString() - ) - .replace(':event', 'birth') - .replace(':pageId', 'review') - ) - await waitForElement(app, '#readyDeclaration') - }) - - it('rejecting declaration redirects to home screen', async () => { - vi.doMock('@apollo/client/react', () => ({ default: ReactApollo })) - - app.find('#rejectDeclarationBtn').hostNodes().simulate('click') - - app - .find('#rejectionCommentForHealthWorker') - .hostNodes() - .simulate('change', { - target: { - id: 'rejectionCommentForHealthWorker', - value: 'reject reason' - } - }) - - app.find('#submit_reject_form').hostNodes().simulate('click') - - expect(router.state.location.pathname).toEqual( - `${REGISTRAR_HOME}/my-drafts/1` - ) - }) - }) - - describe('when user is in the marriage review section', () => { - let customDraft: IDeclaration - - const registerScopeToken = - 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWdpc3RlciIsImNlcnRpZnkiLCJkZW1vIl0sImlhdCI6MTU0MjY4ODc3MCwiZXhwIjoxNTQzMjkzNTcwLCJhdWQiOlsib3BlbmNydnM6YXV0aC11c2VyIiwib3BlbmNydnM6dXNlci1tZ250LXVzZXIiLCJvcGVuY3J2czpoZWFydGgtdXNlciIsIm9wZW5jcnZzOmdhdGV3YXktdXNlciIsIm9wZW5jcnZzOm5vdGlmaWNhdGlvbi11c2VyIiwib3BlbmNydnM6d29ya2Zsb3ctdXNlciJdLCJpc3MiOiJvcGVuY3J2czphdXRoLXNlcnZpY2UiLCJzdWIiOiI1YmVhYWY2MDg0ZmRjNDc5MTA3ZjI5OGMifQ.ElQd99Lu7WFX3L_0RecU_Q7-WZClztdNpepo7deNHqzro-Cog4WLN7RW3ZS5PuQtMaiOq1tCb-Fm3h7t4l4KDJgvC11OyT7jD6R2s2OleoRVm3Mcw5LPYuUVHt64lR_moex0x_bCqS72iZmjrjS-fNlnWK5zHfYAjF2PWKceMTGk6wnI9N49f6VwwkinJcwJi6ylsjVkylNbutQZO0qTc7HRP-cBfAzNcKD37FqTRNpVSvHdzQSNcs7oiv3kInDN5aNa2536XSd3H-RiKR9hm9eID9bSIJgFIGzkWRd5jnoYxT70G0t03_mTVnDnqPXDtyI-lmerx24Ost0rQLUNIg' - - beforeEach(async () => { - getItem.mockReturnValue(registerScopeToken) - await flushPromises() - const data = marriageReviewDraftData - - customDraft = { - id: uuid(), - data, - review: true, - event: EventType.Marriage - } - store.dispatch(storeDeclaration(customDraft)) - router.navigate( - REVIEW_EVENT_PARENT_FORM_PAGE.replace( - ':declarationId', - customDraft.id.toString() - ) - .replace(':event', 'marriage') - .replace(':pageId', 'review') - ) - await waitForElement(app, '#readyDeclaration') - }) - - it('rejecting declaration redirects to reject confirmation screen', async () => { - vi.doMock('@apollo/client/react', () => ({ default: ReactApollo })) - - app.find('#rejectDeclarationBtn').hostNodes().simulate('click') - - app - .find('#rejectionCommentForHealthWorker') - .hostNodes() - .simulate('change', { - target: { - id: 'rejectionCommentForHealthWorker', - value: 'reject reason' - } - }) - app.find('#submit_reject_form').hostNodes().simulate('click') - - expect(router.state.location.pathname).toEqual( - `${REGISTRAR_HOME}/my-drafts/1` - ) - }) - }) - - describe('when user has validate scope', () => { - beforeEach(async () => { - await flushPromises() - const data = { - _fhirIDMap: { - composition: '16' - }, - ...mockDeclarationData - } - - const customDraft = createReviewDeclaration(uuid(), data, EventType.Birth) - customDraft.submissionStatus = SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT] - - store.dispatch(storeDeclaration(customDraft)) - router.navigate( - REVIEW_EVENT_PARENT_FORM_PAGE.replace( - ':declarationId', - customDraft.id.toString() - ) - .replace(':event', 'birth') - .replace(':pageId', 'review') - ) - app.update() - }) - - it('shows send for review button', async () => { - await waitForElement(app, '#readyDeclaration') - expect(app.update().contains('#validateDeclarationBtn')).toBeFalsy() - }) - }) -}) diff --git a/packages/client/src/views/RegisterForm/RegisterForm.init.test.tsx b/packages/client/src/views/RegisterForm/RegisterForm.init.test.tsx deleted file mode 100644 index 0820db36801..00000000000 --- a/packages/client/src/views/RegisterForm/RegisterForm.init.test.tsx +++ /dev/null @@ -1,218 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import * as React from 'react' -import { - createTestComponent, - getRegisterFormFromStore, - createTestStore -} from '@client/tests/util' -import { RegisterForm } from '@client/views/RegisterForm/RegisterForm' -import { - createDeclaration, - IUserData, - getCurrentUserID, - getDeclarationsOfCurrentUser, - writeDeclarationByUser, - deleteDeclarationByUser, - IDeclaration -} from '@client/declarations' -import { DRAFT_BIRTH_PARENT_FORM_PAGE } from '@opencrvs/client/src/navigation/routes' -import { vi } from 'vitest' - -import { EventType, Status } from '@client/utils/gateway' -import { storage } from '@client/storage' -import { UserDetails } from '@client/utils/userUtils' -import { formatUrl } from '@client/navigation' -describe('when user logs in', () => { - // Some mock data - const draft1 = createDeclaration(EventType.Birth) - const draft2 = createDeclaration(EventType.Death) - const draft3 = createDeclaration(EventType.Birth) - - const currentUserData: IUserData = { - userID: 'shakib75', - declarations: [draft1, draft2] - } - - const anotherUserData: IUserData = { - userID: 'mortaza', - declarations: [draft3] - } - - const currentUserDetails: Partial = { - userMgntUserID: 'shakib75', - id: 'f244b79e-16e7-40b2-834f-c1c57bd7eae8', - creationDate: '2022-03-25T12:30:34.597+00:00', - practitionerId: '12345', - name: [ - { - use: 'en', - firstNames: 'Jonathan', - familyName: 'Campbell', - __typename: 'HumanName' - } - ], - mobile: '+260921111111', - role: { - id: 'NATIONAL_SYSTEM_ADMIN', - label: { - id: 'userRoles.nationalSystemAdmin', - defaultMessage: 'National System Admin', - description: 'National System Admin' - } - }, - status: 'active' as Status, - localRegistrar: { - name: [], - role: 'FIELD_AGENT' - } - } - - const indexedDB = { - USER_DATA: JSON.stringify([currentUserData, anotherUserData]), - USER_DETAILS: JSON.stringify(currentUserDetails) - } - - beforeEach(() => { - // Mocking storage reading - // @ts-ignore - storage.getItem = vi.fn((key: string) => { - switch (key) { - case 'USER_DATA': - case 'USER_DETAILS': - return indexedDB[key] - default: - return undefined - } - }) - - // Mocking storage writing - // @ts-ignore - storage.setItem = vi.fn((key: string, value: string) => { - switch (key) { - case 'USER_DATA': - case 'USER_DETAILS': - indexedDB[key] = value - break - default: - break - } - }) - }) - - it('should read userID correctly', async () => { - const uID = await getCurrentUserID() // reads from USER_DETAILS and returns the userMgntUserID, if exists - expect(uID).toEqual('shakib75') - }) - - it('should read only the drafts of the currently logged-in user', async () => { - const details = await getDeclarationsOfCurrentUser() - const currentUserDrafts = (JSON.parse(details) as IUserData).declarations - expect(currentUserDrafts.length).toBe(2) - expect(currentUserDrafts[0]).toEqual(draft1) - expect(currentUserDrafts[1]).toEqual(draft2) - expect( - currentUserDrafts.find((draft) => draft.id === draft3.id) - ).toBeFalsy() - }) - - describe('Declaration in index db', () => { - let draft: IDeclaration - - beforeAll(async () => { - draft = createDeclaration(EventType.Death) - vi.mock('lodash/debounce', () => vi.fn((fn) => fn)) - const { store } = await createTestStore() - await writeDeclarationByUser( - store.getState, - currentUserData.userID, - draft - ) - }) - - it("should save the draft inside the current user's array of drafts", async () => { - // Now, let's check if the new draft is added - const details = await getDeclarationsOfCurrentUser() - const currentUserDrafts = (JSON.parse(details) as IUserData).declarations - expect(currentUserDrafts.length).toBe(3) - expect(currentUserDrafts[0]).toBeTruthy() - }) - - it("should delete the draft from the current user's array of declarations", async () => { - const { store } = await createTestStore() - await deleteDeclarationByUser( - currentUserData.userID, - draft.id, - store.getState().declarationsState - ) - - // Now, let's check if the new draft is added - const details = await getDeclarationsOfCurrentUser() - const currentUserDrafts = (JSON.parse(details) as IUserData).declarations - expect(currentUserDrafts.length).toBe(2) - expect( - currentUserDrafts.find((cDraft) => cDraft.id === draft.id) - ).toBeFalsy() - }) - }) -}) - -describe('when there is no user-data saved', () => { - it('should return an empty array', async () => { - // Mocking storage reading - // @ts-ignore - storage.getItem = vi.fn((key: string): string => { - switch (key) { - case 'USER_DATA': - return '[]' - case 'USER_DETAILS': - return '{ "userMgntUserID": "tamimIq" }' - default: - return '' - } - }) - const str = await getDeclarationsOfCurrentUser() - const drafts = (JSON.parse(str) as IUserData).declarations - expect(drafts.length).toBe(0) - }) -}) - -describe('when user is in the register form before initial draft load', () => { - it('throws error when draft not found after initial drafts load', async () => { - const { store } = await createTestStore() - - const draft = createDeclaration(EventType.Birth) - const form = await getRegisterFormFromStore(store, EventType.Birth) - - try { - await createTestComponent( - , - { - store, - path: DRAFT_BIRTH_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(DRAFT_BIRTH_PARENT_FORM_PAGE, { - declarationId: '', - pageId: '', - groupId: '' - }) - ] - } - ) - } catch (error) { - expect(error).toBeInstanceOf(Error) - } - }) -}) diff --git a/packages/client/src/views/RegisterForm/RegisterForm.test.tsx b/packages/client/src/views/RegisterForm/RegisterForm.test.tsx deleted file mode 100644 index 62f22373e7a..00000000000 --- a/packages/client/src/views/RegisterForm/RegisterForm.test.tsx +++ /dev/null @@ -1,875 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import * as React from 'react' -import { - createTestComponent, - selectOption, - mockDeclarationData, - mockDeathDeclarationData, - mockMarriageDeclarationData, - getRegisterFormFromStore, - getReviewFormFromStore, - createTestStore, - flushPromises, - userDetails, - mockOfflineData, - setScopes, - TestComponentWithRouteMock -} from '@client/tests/util' -import { RegisterForm } from '@client/views/RegisterForm/RegisterForm' -import { ReactWrapper } from 'enzyme' -import { - createDeclaration, - createReviewDeclaration, - storeDeclaration, - setInitialDeclarations, - SUBMISSION_STATUS, - IDeclaration -} from '@client/declarations' -import { v4 as uuid } from 'uuid' -import { AppStore } from '@client/store' -import { - DRAFT_BIRTH_PARENT_FORM_PAGE, - REVIEW_EVENT_PARENT_FORM_PAGE, - DRAFT_DEATH_FORM_PAGE, - HOME, - DRAFT_BIRTH_PARENT_FORM_PAGE_GROUP, - DRAFT_MARRIAGE_FORM_PAGE -} from '@opencrvs/client/src/navigation/routes' -import { IFormData } from '@opencrvs/client/src/forms' -import { SCOPES } from '@opencrvs/commons/client' -import { EventType, RegStatus } from '@client/utils/gateway' -import { draftToGqlTransformer } from '@client/transformer' -import { IForm } from '@client/forms' -import { clone, cloneDeep } from 'lodash' -import { getRegisterForm } from '@client/forms/register/declaration-selectors' -import { waitForElement } from '@client/tests/wait-for-element' -import { vi } from 'vitest' -import { createClient } from '@client/utils/apolloClient' -import { ApolloClient } from '@apollo/client' -import { formatUrl } from '@client/navigation' -import { createMemoryRouter } from 'react-router-dom' - -describe('when user is in the register form for birth event', () => { - let component: TestComponentWithRouteMock - - let store: AppStore - let client: ApolloClient<{}> - - describe('when user is in the mother section', () => { - beforeEach(async () => { - const storeContext = await createTestStore() - store = storeContext.store - client = createClient(store) - - const draft = createDeclaration(EventType.Birth) - store.dispatch(storeDeclaration(draft)) - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(draft)) - - const form = await getRegisterFormFromStore(store, EventType.Birth) - const testComponent = await createTestComponent( - // @ts-ignore - , - { - store, - apolloClient: client, - path: DRAFT_BIRTH_PARENT_FORM_PAGE_GROUP, - initialEntries: [ - formatUrl(DRAFT_BIRTH_PARENT_FORM_PAGE_GROUP, { - declarationId: draft.id, - pageId: 'mother', - groupId: 'mother-view-group' - }) - ] - } - ) - component = testComponent - }) - it('takes field agent to declaration submitted page when save & exit button is clicked', async () => { - localStorage.getItem = vi.fn( - () => - 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJkZWNsYXJlIiwiZGVtbyJdLCJpYXQiOjE1NjMyNTYyNDIsImV4cCI6MTU2Mzg2MTA0MiwiYXVkIjpbIm9wZW5jcnZzOmF1dGgtdXNlciIsIm9wZW5jcnZzOnVzZXItbWdudC11c2VyIiwib3BlbmNydnM6aGVhcnRoLXVzZXIiLCJvcGVuY3J2czpnYXRld2F5LXVzZXIiLCJvcGVuY3J2czpub3RpZmljYXRpb24tdXNlciIsIm9wZW5jcnZzOndvcmtmbG93LXVzZXIiLCJvcGVuY3J2czpzZWFyY2gtdXNlciIsIm9wZW5jcnZzOm1ldHJpY3MtdXNlciIsIm9wZW5jcnZzOnJlc291cmNlcy11c2VyIl0sImlzcyI6Im9wZW5jcnZzOmF1dGgtc2VydmljZSIsInN1YiI6IjVkMWM1YTJhNTgxNjM0MDBlZjFkMDEyOSJ9.hZu0em2JA0sl-5uzck4mn4HfYdzxSmgoERA8SbWRPXEmriSYjs4PEPk9StXF_Ed5kd53VlNF9xf39DDGWqyyn76gpcMPbHJAL8nqLV82hot8fgU1WtEk865U8-9oAxaVmxAsjpHayiuD6zfKuR-ixrLFdoRKP13LdORktFCQe5e7To2w7vXArjUb6SDpSHST4Fbkhg8vzOcykweSGiNlmoEVtLzkpamS6fcTGRHkNpb_Wk_AQW9TAdw6NqG5lDEAO10auNgJpKxO8X-DQKhvEfY5TbpblR51L_U8pUXpDCAvGegMLnwmfAIoH1hMj--Wd2JhqgUvj0YrlDKI99fntA' - ) - component.component.update() - - component.component.find('#save-exit-btn').hostNodes().simulate('click') - component.component.update() - component.component - .find('#confirm_save_exit') - .hostNodes() - .simulate('click') - component.component.update() - await flushPromises() - expect(component.router.state.location.pathname).toEqual( - '/registration-home/my-drafts/1' - ) - }) - it('takes registrar to declaration submitted page when save button is clicked', async () => { - localStorage.getItem = vi.fn( - () => - 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWdpc3RlciIsInBlcmZvcm1hbmNlIiwiY2VydGlmeSIsImRlbW8iXSwiaWF0IjoxNTYzOTcyOTQ0LCJleHAiOjE1NjQ1Nzc3NDQsImF1ZCI6WyJvcGVuY3J2czphdXRoLXVzZXIiLCJvcGVuY3J2czp1c2VyLW1nbnQtdXNlciIsIm9wZW5jcnZzOmhlYXJ0aC11c2VyIiwib3BlbmNydnM6Z2F0ZXdheS11c2VyIiwib3BlbmNydnM6bm90aWZpY2F0aW9uLXVzZXIiLCJvcGVuY3J2czp3b3JrZmxvdy11c2VyIiwib3BlbmNydnM6c2VhcmNoLXVzZXIiLCJvcGVuY3J2czptZXRyaWNzLXVzZXIiLCJvcGVuY3J2czpyZXNvdXJjZXMtdXNlciJdLCJpc3MiOiJvcGVuY3J2czphdXRoLXNlcnZpY2UiLCJzdWIiOiI1ZDFjNWEyYTU4MTYzNDAwZWYxZDAxMmIifQ.VrH31goeitKvLHQchy5HQJkQWjhK-cWisxSgQUXChK4MZQis9Ufzn7dWK3s2s0dSpnFqk-0Yj5cVlq7JgQVcniO26WhnSyXHYQk7DG-TSA5FXGYoKMhjMZCh5qOZTRaVI6yvnEsLKTYeNvkXKJ2wb6M9U5OWjUh1KGPexd9mSjUsUwZ5BDTvI0WjnBTgQ_a0-KhxjjypT8Y_VXiiY-KWLxuOpVGalv3P3nbH8dAUzEuzKsrq6q0MJsaJkgDliaz2pZd10JxnJE1VYUob2SNHFnmJnz8Llwe1lH4xa8rluIA6YBmxdkrU2VkhCBPD6VxGYRHrD3LKRa3Cgm1X0qNQTw' - ) - component.component.find('#save-exit-btn').hostNodes().simulate('click') - component.component.update() - component.component - .find('#confirm_save_exit') - .hostNodes() - .simulate('click') - component.component.update() - await flushPromises() - expect( - component.router.state.location.pathname.includes( - '/registration-home/my-drafts/1' - ) - ).toBeTruthy() - }) - }) -}) - -describe('when user is in the register form for death event', () => { - let component: ReactWrapper<{}, {}> - - let form: IForm - let store: AppStore - let draft: ReturnType - - beforeEach(async () => { - const testStore = await createTestStore() - store = testStore.store - - draft = createDeclaration(EventType.Death) - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(draft)) - form = await getRegisterFormFromStore(store, EventType.Death) - }) - describe('when user is in optional cause of death section', () => { - beforeEach(async () => { - const clonedForm = cloneDeep(form) - clonedForm.sections[2].optional = true - // TODO: need to check if causeOfDeathNotice is needed or not - // clonedForm.sections[2].notice = messages.causeOfDeathNotice - clonedForm.sections[2].groups[0].ignoreSingleFieldView = true - const { component: testComponent } = await createTestComponent( - , - { - store, - path: DRAFT_DEATH_FORM_PAGE, - initialEntries: [ - formatUrl(DRAFT_DEATH_FORM_PAGE, { - declarationId: draft.id, - pageId: 'deathEvent', - groupId: 'death-event-details' - }) - ] - } - ) - component = testComponent - }) - - it('renders the deathEvent details page', () => { - expect( - component.find('#form_section_id_death-event-details').hostNodes() - ).toHaveLength(1) - }) - }) -}) - -describe('when user is in the register form for marriage event', () => { - let component: ReactWrapper<{}, {}> - - let form: IForm - let store: AppStore - - let draft: ReturnType - - beforeEach(async () => { - const testStore = await createTestStore() - store = testStore.store - - draft = createDeclaration(EventType.Marriage) - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(draft)) - form = await getRegisterFormFromStore(store, EventType.Marriage) - }) - - describe('when user is in marriage section', () => { - beforeEach(async () => { - const clonedForm = cloneDeep(form) - clonedForm.sections[2].optional = true - clonedForm.sections[2].groups[0].ignoreSingleFieldView = true - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: DRAFT_MARRIAGE_FORM_PAGE, - initialEntries: [ - formatUrl(DRAFT_MARRIAGE_FORM_PAGE, { - declarationId: draft.id, - pageId: 'marriageEvent', - groupId: 'marriage-event-details' - }) - ] - } - ) - component = testComponent - }) - - it('renders the marriageEvent details page', () => { - expect( - component.find('#form_section_id_marriage-event-details').hostNodes() - ).toHaveLength(1) - }) - }) -}) - -describe('when user is in the register form preview section and has the submit complete scope', () => { - let component: TestComponentWithRouteMock - let store: AppStore - - const mock = vi.fn() - - beforeEach(async () => { - mock.mockReset() - const storeContext = await createTestStore() - store = storeContext.store - - const draft = createDeclaration(EventType.Birth) - draft.data = { - child: { firstNamesEng: 'John', familyNameEng: 'Doe' }, - father: { - detailsExist: true - }, - mother: { - detailsExist: true - }, - documents: { - imageUploader: { title: 'dummy', description: 'dummy', data: '' } - }, - registration: { - commentsOrNotes: '', - contactPoint: { - nestedFields: { - registrationPhone: '01557394989' - }, - value: 'MOTHER' - } - } - } - setScopes([SCOPES.RECORD_SUBMIT_INCOMPLETE], store) - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(draft)) - - const form = await getRegisterFormFromStore(store, EventType.Birth) - const testComponent = await createTestComponent( - , - { - store, - path: DRAFT_BIRTH_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(DRAFT_BIRTH_PARENT_FORM_PAGE, { - declarationId: draft.id, - pageId: 'preview', - groupId: 'preview-view-group' - }) - ] - } - ) - component = testComponent - }) - - it('submit button will be enabled when even if form is not fully filled-up', () => { - expect( - component.component - .find('#submit_incomplete') - .hostNodes() - .prop('disabled') - ).toBe(false) - }) - - it('Displays submit confirm modal when submit button is clicked', () => { - component.component.find('#submit_incomplete').hostNodes().simulate('click') - - expect( - component.component.find('#submit_confirm').hostNodes() - ).toHaveLength(1) - }) - - describe('User in the Preview section for submitting the Form and has the submit for review scope', () => { - beforeEach(async () => { - // @ts-ignore - const nDeclaration = createReviewDeclaration( - uuid(), - mockDeclarationData, - EventType.Birth - ) - nDeclaration.submissionStatus = SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT] - setScopes([SCOPES.RECORD_SUBMIT_FOR_REVIEW], store) - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(nDeclaration)) - - const nform = getRegisterForm(store.getState())[EventType.Birth] - const nTestComponent = await createTestComponent( - , - { - store, - path: DRAFT_BIRTH_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(DRAFT_BIRTH_PARENT_FORM_PAGE, { - declarationId: nDeclaration.id, - pageId: 'preview', - groupId: 'preview-view-group' - }) - ] - } - ) - component = nTestComponent - }) - - it('should be able to submit the form', () => { - component.component - .find('#submit_for_review') - .hostNodes() - .simulate('click') - - component.component.update() - - const cancelBtn = component.component.find('#cancel-btn').hostNodes() - expect(cancelBtn.length).toEqual(1) - - cancelBtn.simulate('click') - component.component.update() - - expect( - component.component.find('#submit_confirm').hostNodes().length - ).toEqual(0) - expect( - component.component.find('#submit_for_review').hostNodes().length - ).toEqual(1) - - component.component - .find('#submit_for_review') - .hostNodes() - .simulate('click') - component.component.update() - - const confirmBtn = component.component.find('#submit_confirm').hostNodes() - expect(confirmBtn.length).toEqual(1) - - confirmBtn.simulate('click') - component.component.update() - expect(component.router.state.location.pathname).toBe(HOME) - }) - }) -}) - -describe('when user is in the register form review section', () => { - let component: ReactWrapper<{}, {}> - beforeEach(async () => { - const { store } = await createTestStore() - // @ts-ignore - const declaration = createReviewDeclaration( - uuid(), - mockDeclarationData, - EventType.Birth - ) - setScopes([SCOPES.RECORD_REGISTER, SCOPES.RECORD_SUBMIT_FOR_UPDATES], store) - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(declaration)) - - const form = await getReviewFormFromStore(store, EventType.Birth) - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: declaration.id, - pageId: 'review', - groupId: 'review-view-group' - }) - ] - } - ) - component = testComponent - }) - - it('clicking the reject button launches the reject form action page', async () => { - component.find('#rejectDeclarationBtn').hostNodes().simulate('click') - - await waitForElement(component, '#reject-registration-form-container') - expect( - component.find('#reject-registration-form-container').hostNodes() - ).toHaveLength(1) - }) -}) - -describe('when user is in the register form from review edit', () => { - let component: ReactWrapper<{}, {}> - let router: ReturnType - beforeEach(async () => { - const { store } = await createTestStore() - // @ts-ignore - const declaration = createReviewDeclaration( - uuid(), - mockDeclarationData, - EventType.Birth - ) - setScopes([SCOPES.RECORD_REGISTER], store) - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(declaration)) - - const form = await getReviewFormFromStore(store, EventType.Birth) - - const { component: testComponent, router: testRouter } = - await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - '/', - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: declaration.id, - pageId: 'mother', - groupId: 'mother-view-group' - }) - ] - } - ) - component = testComponent - router = testRouter - }) - - it('should redirect to review page when back button is clicked', async () => { - const backButton = await waitForElement(component, '#back-to-review-button') - backButton.hostNodes().simulate('click') - component.update() - await flushPromises() - expect(router.state.location.pathname).toContain('/review') - }) -}) - -describe('when user is in the register form from sent for review edit', () => { - let component: ReactWrapper<{}, {}> - let testAppStore: AppStore - beforeEach(async () => { - Date.now = vi.fn(() => 1582525224324) - const { store } = await createTestStore() - // @ts-ignore - const declaration = createReviewDeclaration( - uuid(), - mockDeclarationData, - EventType.Birth, - RegStatus.Declared - ) - - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(declaration)) - - const form = await getReviewFormFromStore(store, EventType.Birth) - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: declaration.id, - pageId: 'mother', - groupId: 'mother-view-group' - }) - ] - } - ) - component = testComponent - testAppStore = store - }) - - it('clicking on save draft opens modal', async () => { - const saveDraftButton = await waitForElement(component, '#save-exit-btn') - saveDraftButton.hostNodes().simulate('click') - component.update() - const saveDraftConfirmationModal = await waitForElement( - component, - '#save_declaration_confirmation' - ) - expect(saveDraftConfirmationModal.hostNodes()).toHaveLength(1) - }) - - it('clicking save confirm saves the draft', async () => { - const DRAFT_MODIFY_TIME = 1582525379383 - Date.now = vi.fn(() => DRAFT_MODIFY_TIME) - selectOption(component, '#educationalAttainment', 'Tertiary') - - // Do some modifications - component.find('input#iD').simulate('change', { - target: { id: 'iD', value: '1234567898' } - }) - const saveDraftButton = await waitForElement(component, '#save-exit-btn') - saveDraftButton.hostNodes().simulate('click') - component.update() - const saveDraftConfirmationModal = await waitForElement( - component, - '#save_declaration_confirmation' - ) - - saveDraftConfirmationModal - .find('#confirm_save_exit') - .hostNodes() - .simulate('click') - component.update() - - await flushPromises() - const modifyTime = - testAppStore.getState().declarationsState.declarations[0].modifiedOn - - expect(modifyTime).toBe(DRAFT_MODIFY_TIME) - }) -}) - -describe('When user is in Preview section death event', () => { - let store: AppStore - let component: TestComponentWithRouteMock - let deathDraft: IDeclaration - let deathForm: IForm - - beforeEach(async () => { - const testStore = await createTestStore() - store = testStore.store - - const draft = createDeclaration(EventType.Death) - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(draft)) - vi.clearAllMocks() - // @ts-ignore - deathDraft = createReviewDeclaration( - uuid(), - // @ts-ignore - mockDeathDeclarationData, - EventType.Death - ) - setScopes([SCOPES.RECORD_SUBMIT_INCOMPLETE], store) - deathDraft.submissionStatus = SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT] - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(deathDraft)) - - deathForm = await getRegisterFormFromStore(store, EventType.Death) - const nTestComponent = await createTestComponent( - , - { - store, - path: DRAFT_BIRTH_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(DRAFT_BIRTH_PARENT_FORM_PAGE, { - declarationId: deathDraft.id, - pageId: 'preview', - groupId: 'preview-view-group' - }) - ] - } - ) - component = nTestComponent - }) - - it('Check if death location type is parsed properly', () => { - expect( - draftToGqlTransformer( - deathForm, - mockDeathDeclarationData as IFormData, - deathDraft.id, - userDetails, - mockOfflineData, - undefined - ).eventLocation.type - ).toBe('OTHER') - }) - - it('Check if death location partOf is parsed properly', () => { - expect( - draftToGqlTransformer( - deathForm, - mockDeathDeclarationData as IFormData, - deathDraft.id, - userDetails, - mockOfflineData, - undefined - ).eventLocation.address.country - ).toEqual('FAR') - }) - - it('Should be able to submit the form', () => { - component.component.find('#submit_incomplete').hostNodes().simulate('click') - - const confirmBtn = component.component.find('#submit_confirm').hostNodes() - expect(confirmBtn.length).toEqual(1) - - confirmBtn.simulate('click') - component.component.update() - - expect(component.router.state.location.pathname).toBe(HOME) - }) - - it('Check if death location as hospital is parsed properly', () => { - const hospitalLocatioMockDeathDeclarationData = clone( - mockDeathDeclarationData - ) - hospitalLocatioMockDeathDeclarationData.deathEvent.placeOfDeath = - 'HEALTH_FACILITY' - hospitalLocatioMockDeathDeclarationData.deathEvent.deathLocation = - '5e3736a0-090e-43b4-9012-f1cef399e123' - expect( - draftToGqlTransformer( - deathForm, - hospitalLocatioMockDeathDeclarationData as IFormData, - '123', - userDetails, - mockOfflineData, - undefined - ).eventLocation.address - ).toBe(undefined) - }) - - it('Check if death location as hospital _fhirID is parsed properly', () => { - const hospitalLocatioMockDeathDeclarationData = clone( - mockDeathDeclarationData - ) - hospitalLocatioMockDeathDeclarationData.deathEvent.placeOfDeath = - 'HEALTH_FACILITY' - hospitalLocatioMockDeathDeclarationData.deathEvent.deathLocation = - '5e3736a0-090e-43b4-9012-f1cef399e123' - - expect( - draftToGqlTransformer( - deathForm, - hospitalLocatioMockDeathDeclarationData as IFormData, - '123', - userDetails, - mockOfflineData, - undefined - ).eventLocation._fhirID - ).toBe('5e3736a0-090e-43b4-9012-f1cef399e123') - }) - - it('Check if death location is deceased parmanent address', () => { - const mockDeathDeclaration = clone(mockDeathDeclarationData) - mockDeathDeclaration.deathEvent.placeOfDeath = 'PRIMARY_ADDRESS' - expect( - draftToGqlTransformer( - deathForm, - mockDeathDeclaration as IFormData, - '123', - userDetails, - mockOfflineData, - undefined - ).eventLocation.type - ).toBe('PRIMARY_ADDRESS') - }) -}) - -describe('When user is in Preview section death event in offline mode', () => { - let component: TestComponentWithRouteMock - let deathDraft - let deathForm: IForm - let store: AppStore - - beforeEach(async () => { - const testStore = await createTestStore() - - store = testStore.store - - const draft = createDeclaration(EventType.Death) - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(draft)) - - Object.defineProperty(window.navigator, 'onLine', { - value: false, - writable: true - }) - vi.clearAllMocks() - // @ts-ignore - deathDraft = createReviewDeclaration( - uuid(), - // @ts-ignore - mockDeathDeclarationData, - EventType.Death - ) - - setScopes([SCOPES.RECORD_SUBMIT_INCOMPLETE], store) - deathDraft.submissionStatus = SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT] - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(deathDraft)) - - deathForm = await getRegisterFormFromStore(store, EventType.Death) - const nTestComponent = await createTestComponent( - , - { - store, - path: DRAFT_BIRTH_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(DRAFT_BIRTH_PARENT_FORM_PAGE, { - declarationId: deathDraft.id, - pageId: 'preview', - groupId: 'preview-view-group' - }) - ] - } - ) - component = nTestComponent - }) - - it('Should be able to submit the form', async () => { - component.component.find('#submit_incomplete').hostNodes().simulate('click') - - const confirmBtn = component.component.find('#submit_confirm').hostNodes() - expect(confirmBtn.length).toEqual(1) - - confirmBtn.simulate('click') - component.component.update() - - expect(component.router.state.location.pathname).toBe(HOME) - }) -}) - -describe('When user is in Preview section marriage event', () => { - let store: AppStore - - let component: TestComponentWithRouteMock - let marriageDraft - let marriageForm: IForm - - beforeEach(async () => { - const testStore = await createTestStore() - store = testStore.store - - const draft = createDeclaration(EventType.Death) - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(draft)) - vi.clearAllMocks() - // @ts-ignore - marriageDraft = createReviewDeclaration( - uuid(), - // @ts-ignore - mockDeathDeclarationData, - EventType.Marriage - ) - - setScopes([SCOPES.RECORD_SUBMIT_INCOMPLETE], store) - marriageDraft.submissionStatus = SUBMISSION_STATUS[SUBMISSION_STATUS.DRAFT] - store.dispatch(setInitialDeclarations()) - store.dispatch(storeDeclaration(marriageDraft)) - - marriageForm = await getRegisterFormFromStore(store, EventType.Marriage) - - const nTestComponent = await createTestComponent( - // @ts-ignore - , - { - store, - path: DRAFT_MARRIAGE_FORM_PAGE, - initialEntries: [ - formatUrl(DRAFT_MARRIAGE_FORM_PAGE, { - declarationId: marriageDraft.id, - pageId: 'preview', - groupId: 'preview-view-group' - }) - ] - } - ) - component = nTestComponent - }) - - it('Check if marriage location partOf is parsed properly', () => { - expect( - draftToGqlTransformer( - marriageForm, - mockMarriageDeclarationData as unknown as IFormData, - '123', - userDetails, - mockOfflineData, - undefined - ).eventLocation.address.country - ).toEqual('FAR') - }) - - it('Check if data has witnessOne', () => { - expect( - draftToGqlTransformer( - marriageForm, - mockMarriageDeclarationData as unknown as IFormData, - '123', - userDetails, - mockOfflineData, - undefined - ).witnessOne._fhirID - ).toEqual('36972633-1c80-4fb4-a636-17f7dc9c2e14') - }) - - it('Should be able to submit the form', () => { - component.component.find('#submit_incomplete').hostNodes().simulate('click') - - const confirmBtn = component.component.find('#submit_confirm').hostNodes() - expect(confirmBtn.length).toEqual(1) - - confirmBtn.simulate('click') - component.component.update() - - expect(component.router.state.location.pathname).toBe(HOME) - }) -}) diff --git a/packages/client/src/views/RegisterForm/ReviewForm.test.tsx b/packages/client/src/views/RegisterForm/ReviewForm.test.tsx deleted file mode 100644 index 5a640c495ff..00000000000 --- a/packages/client/src/views/RegisterForm/ReviewForm.test.tsx +++ /dev/null @@ -1,854 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { - createReviewDeclaration, - DOWNLOAD_STATUS, - getStorageDeclarationsSuccess, - IDeclaration, - storeDeclaration -} from '@opencrvs/client/src/declarations' -import { IForm, IFormSectionData } from '@opencrvs/client/src/forms' -import { SCOPES } from '@opencrvs/commons/client' -import { EventType, RegStatus } from '@client/utils/gateway' -import { REVIEW_EVENT_PARENT_FORM_PAGE } from '@opencrvs/client/src/navigation/routes' -import { checkAuth } from '@opencrvs/client/src/profile/profileActions' -import { RegisterForm } from '@opencrvs/client/src/views/RegisterForm/RegisterForm' -import * as React from 'react' -import { queries } from '@client/profile/queries' -import { AppStore } from '@client/store' -import { - createTestComponent, - mockUserResponseWithName, - getReviewFormFromStore, - createTestStore, - mockDeathDeclarationData, - setScopes, - REGISTRAR_DEFAULT_SCOPES, - flushPromises -} from '@client/tests/util' -import { v4 as uuid } from 'uuid' -import { ReviewForm } from '@client/views/RegisterForm/ReviewForm' -import { WORKQUEUE_TABS } from '@client/components/interface/WorkQueueTabs' -import { birthDraftData } from '@client/tests/mock-drafts' -import { vi, Mock } from 'vitest' -import { formatUrl } from '@client/navigation' - -const declareScope = - 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJpYXQiOjE1MzMxOTUyMjgsImV4cCI6MTU0MzE5NTIyNywiYXVkIjpbImdhdGV3YXkiXSwic3ViIjoiMSJ9.G4KzkaIsW8fTkkF-O8DI0qESKeBI332UFlTXRis3vJ6daisu06W5cZsgYhmxhx_n0Q27cBYt2OSOnjgR72KGA5IAAfMbAJifCul8ib57R4VJN8I90RWqtvA0qGjV-sPndnQdmXzCJx-RTumzvr_vKPgNDmHzLFNYpQxcmQHA-N8li-QHMTzBHU4s9y8_5JOCkudeoTMOd_1021EDAQbrhonji5V1EOSY2woV5nMHhmq166I1L0K_29ngmCqQZYi1t6QBonsIowlXJvKmjOH5vXHdCCJIFnmwHmII4BK-ivcXeiVOEM_ibfxMWkAeTRHDshOiErBFeEvqd6VWzKvbKAH0UY-Rvnbh4FbprmO4u4_6Yd2y2HnbweSo-v76dVNcvUS0GFLFdVBt0xTay-mIeDy8CKyzNDOWhmNUvtVi9mhbXYfzzEkwvi9cWwT1M8ZrsWsvsqqQbkRCyBmey_ysvVb5akuabenpPsTAjiR8-XU2mdceTKqJTwbMU5gz-8fgulbTB_9TNJXqQlH7tyYXMWHUY3uiVHWg2xgjRiGaXGTiDgZd01smYsxhVnPAddQOhqZYCrAgVcT1GBFVvhO7CC-rhtNlLl21YThNNZNpJHsCgg31WA9gMQ_2qAJmw2135fAyylO8q7ozRUvx46EezZiPzhCkPMeELzLhQMEIqjo' - -const registerScopeToken = - 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWdpc3RlciIsImNlcnRpZnkiLCJkZW1vIl0sImlhdCI6MTU0MjY4ODc3MCwiZXhwIjoxNTQzMjkzNTcwLCJhdWQiOlsib3BlbmNydnM6YXV0aC11c2VyIiwib3BlbmNydnM6dXNlci1tZ250LXVzZXIiLCJvcGVuY3J2czpoZWFydGgtdXNlciIsIm9wZW5jcnZzOmdhdGV3YXktdXNlciIsIm9wZW5jcnZzOm5vdGlmaWNhdGlvbi11c2VyIiwib3BlbmNydnM6d29ya2Zsb3ctdXNlciJdLCJpc3MiOiJvcGVuY3J2czphdXRoLXNlcnZpY2UiLCJzdWIiOiI1YmVhYWY2MDg0ZmRjNDc5MTA3ZjI5OGMifQ.ElQd99Lu7WFX3L_0RecU_Q7-WZClztdNpepo7deNHqzro-Cog4WLN7RW3ZS5PuQtMaiOq1tCb-Fm3h7t4l4KDJgvC11OyT7jD6R2s2OleoRVm3Mcw5LPYuUVHt64lR_moex0x_bCqS72iZmjrjS-fNlnWK5zHfYAjF2PWKceMTGk6wnI9N49f6VwwkinJcwJi6ylsjVkylNbutQZO0qTc7HRP-cBfAzNcKD37FqTRNpVSvHdzQSNcs7oiv3kInDN5aNa2536XSd3H-RiKR9hm9eID9bSIJgFIGzkWRd5jnoYxT70G0t03_mTVnDnqPXDtyI-lmerx24Ost0rQLUNIg' -const getItem = window.localStorage.getItem as Mock - -const mockDeclarationData = { - child: { - firstNames: 'গায়ত্রী', - familyName: 'স্পিভক', - firstNamesEng: 'Mike', - familyNameEng: 'Test', - childBirthDate: '1977-09-20', - gender: 'male', - weightAtBirth: '3.5', - attendantAtBirth: 'MIDWIFE', - birthType: 'SINGLE', - multipleBirth: 1, - placeOfBirth: 'HEALTH_FACILITY', - birthLocation: '627fc0cc-e0e2-4c09-804d-38a9fa1807ee' - }, - mother: { - firstNames: 'স্পিভক', - familyName: 'গায়ত্রী', - firstNamesEng: 'Liz', - familyNameEng: 'Test', - iD: '6546511876932', - iDType: 'NATIONAL_ID', - motherBirthDate: '1949-05-31', - dateOfMarriage: '1972-09-19', - maritalStatus: 'MARRIED', - educationalAttainment: 'SECOND_STAGE_TERTIARY_ISCED_6', - nationality: 'BGD' - }, - father: { - detailsExist: true, - firstNames: 'গায়ত্রী', - familyName: 'স্পিভক', - firstNamesEng: 'Jeff', - familyNameEng: 'Test', - iD: '123456789', - iDType: 'PASSPORT', - fatherBirthDate: '1950-05-19', - dateOfMarriage: '1972-09-19', - maritalStatus: 'MARRIED', - educationalAttainment: 'SECOND_STAGE_TERTIARY_ISCED_6', - nationality: 'BGD', - primaryAddressSameAsOtherPrimary: true - }, - documents: { - uploadDocForMother: [ - { - optionValues: ['MOTHER', 'BIRTH_CERTIFICATE'], - type: 'image/jpeg', - title: 'MOTHER', - description: 'BIRTH_CERTIFICATE', - data: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQECWAJYAAD' - } - ] - }, - registration: { - whoseContactDetails: { - value: 'MOTHER', - nestedFields: { registrationPhone: '01557394986' } - }, - informantType: { - value: 'MOTHER', - nestedFields: { otherInformantType: '' } - }, - registrationNumber: '201908122365BDSS0SE1', - regStatus: { - type: 'REGISTERED', - officeName: 'MokhtarPur', - officeAlias: 'মখতারপুর', - officeAddressLevel3: 'Gazipur', - officeAddressLevel4: 'Dhaka' - }, - contactPoint: { - nestedFields: { - registrationPhone: '+8801711111111' - } - }, - certificates: [{}] - } -} -const birthDeclaration: IDeclaration = { - id: '31a78be1-5ab3-42c7-8f64-7678cb294508', - data: { - ...mockDeclarationData, - corrector: { - relationship: { - value: 'MOTHER', - nestedFields: { - otherRelationship: '' - } - }, - hasShowedVerifiedDocument: false - }, - review: {}, - supportingDocuments: { - uploadDocForLegalProof: '', - supportDocumentRequiredForCorrection: true - }, - reason: { - type: { - value: 'CLERICAL_ERROR', - nestedFields: { - reasonForChange: '' - } - }, - additionalComment: 'Comment' - } - }, - originalData: mockDeclarationData, - review: true, - event: EventType.Birth, - registrationStatus: RegStatus.Registered, - downloadStatus: DOWNLOAD_STATUS.DOWNLOADED, - modifiedOn: 1644407705186, - visitedGroupIds: [ - { - sectionId: 'mother', - groupId: 'mother-view-group' - }, - { - sectionId: 'father', - groupId: 'father-view-group' - } - ], - timeLoggedMS: 990618 -} - -const deathDeclaration: IDeclaration = { - id: '85bccf72-6117-4cab-827d-47728becb0c1', - data: { - ...mockDeathDeclarationData, - informant: { - firstNamesEng: 'John', - familyNameEng: 'Millar' - }, - spouse: { - hasDetails: { - value: 'Yes', - nestedFields: { - spouseFirstNamesEng: 'spouse', - spouseFamilyNameEng: 'name' - } - } - }, - documents: { - uploadDocForDeceased: [], - uploadDocForInformant: [], - uploadDocForDeceasedDeath: [] - }, - _fhirIDMap: { - composition: '85bccf72-6117-4cab-827d-47728becb0c1' - }, - corrector: { - relationship: { - value: 'INFORMANT', - nestedFields: { - otherRelationship: '' - } - }, - hasShowedVerifiedDocument: false - }, - review: {}, - supportingDocuments: { - uploadDocForLegalProof: '', - supportDocumentRequiredForCorrection: true - }, - reason: { - type: { - value: 'MATERIAL_ERROR', - nestedFields: { - reasonForChange: '' - } - }, - additionalComment: '' - } - }, - originalData: mockDeathDeclarationData, - review: true, - event: EventType.Death, - registrationStatus: RegStatus.Registered, - downloadStatus: DOWNLOAD_STATUS.DOWNLOADED, - modifiedOn: 1644490181166, - visitedGroupIds: [ - { - sectionId: 'informant', - groupId: 'informant-view-group' - }, - { - sectionId: 'registration', - groupId: 'point-of-contact' - }, - { - sectionId: 'deceased', - groupId: 'deceased-view-group' - }, - { - sectionId: 'spouse', - groupId: 'spouse-view-group' - }, - { - sectionId: 'deathEvent', - groupId: 'deathEvent-deathLocation' - } - ], - timeLoggedMS: 4446 -} - -const mockFetchUserDetails = vi.fn() -mockFetchUserDetails.mockReturnValue(mockUserResponseWithName) -queries.fetchUserDetails = mockFetchUserDetails -describe('ReviewForm tests', () => { - const scope = [SCOPES.RECORD_REGISTER] - - let form: IForm - let store: AppStore - - beforeEach(async () => { - const testStore = await createTestStore() - store = testStore.store - - setScopes(REGISTRAR_DEFAULT_SCOPES, store) - - form = await getReviewFormFromStore(store, EventType.Birth) - getItem.mockReturnValue(registerScopeToken) - }) - - it('Shared contact phone number should be set properly', async () => { - // NOTE: check auth dispatches actions that eventually will retrieve user data - // from offline storage. Those would override the data we set here. - await new Promise((resolve) => setTimeout(resolve, 100)) - store.dispatch(storeDeclaration(birthDeclaration)) - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: birthDeclaration.id, - pageId: 'review', - event: birthDeclaration.event.toLowerCase() - }) - ] - } - ) - - const data = testComponent - .find(RegisterForm) - .prop('declaration') as IDeclaration - expect( - ( - (data.data.registration.contactPoint as IFormSectionData) - .nestedFields as IFormSectionData - ).registrationPhone - ).toBe('+8801711111111') - }) - - it('when registration has attachment', async () => { - // NOTE: check auth dispatches actions that eventually will retrieve user data - // from offline storage. Those would override the data we set here. - await new Promise((resolve) => setTimeout(resolve, 100)) - store.dispatch(storeDeclaration(birthDeclaration)) - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: birthDeclaration.id, - pageId: 'review', - event: birthDeclaration.event.toLowerCase() - }) - ] - } - ) - - store.dispatch(storeDeclaration(birthDeclaration)) - testComponent.update() - - const data = testComponent - .find(RegisterForm) - .prop('declaration') as IDeclaration - - expect(data.data.documents.uploadDocForMother).toEqual([ - { - optionValues: ['MOTHER', 'BIRTH_CERTIFICATE'], - type: 'image/jpeg', - title: 'MOTHER', - description: 'BIRTH_CERTIFICATE', - data: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQECWAJYAAD' - } - ]) - }) - it('check registration', async () => { - // NOTE: check auth dispatches actions that eventually will retrieve user data - // from offline storage. Those would override the data we set here. - await flushPromises() - store.dispatch(storeDeclaration(birthDeclaration)) - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: birthDeclaration.id, - pageId: 'review', - event: birthDeclaration.event.toLowerCase() - }) - ] - } - ) - - testComponent.update() - - const data = testComponent - .find(RegisterForm) - .prop('declaration') as IDeclaration - expect(data.data.registration).toEqual({ - whoseContactDetails: { - value: 'MOTHER', - nestedFields: { registrationPhone: '01557394986' } - }, - informantType: { - value: 'MOTHER', - nestedFields: { otherInformantType: '' } - }, - registrationNumber: '201908122365BDSS0SE1', - regStatus: { - type: 'REGISTERED', - officeName: 'MokhtarPur', - officeAlias: 'মখতারপুর', - officeAddressLevel3: 'Gazipur', - officeAddressLevel4: 'Dhaka' - }, - contactPoint: { - nestedFields: { - registrationPhone: '+8801711111111' - } - }, - certificates: [{}] - }) - }) - - it('redirect to home when exit button is clicked', async () => { - const declaration = createReviewDeclaration( - uuid(), - birthDraftData, - EventType.Birth, - RegStatus.InProgress - ) - - // NOTE: check auth dispatches actions that eventually will retrieve user data - // from offline storage. Those would override the data we set here. - await flushPromises() - - store.dispatch( - getStorageDeclarationsSuccess( - JSON.stringify({ - userID: 'currentUser', // mock - drafts: [declaration], - declarations: [] - }) - ) - ) - store.dispatch(storeDeclaration(declaration)) - - const { component: testComponent, router: testRouter } = - await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: declaration.id, - pageId: 'review', - event: declaration.event.toLowerCase() - }) - ] - } - ) - - testComponent.update() - testComponent.find('#exit-btn').hostNodes().simulate('click') - testComponent.update() - expect(testRouter.state.location.pathname).toContain('/progress') - }) - - it('redirect to review tab when exit button is clicked', async () => { - // NOTE: check auth dispatches actions that eventually will retrieve user data - // from offline storage. Those would override the data we set here. - await flushPromises() - - const declaration = createReviewDeclaration( - uuid(), - birthDraftData, - EventType.Birth, - RegStatus.Declared - ) - - store.dispatch( - getStorageDeclarationsSuccess( - JSON.stringify({ - userID: 'currentUser', // mock - drafts: [declaration], - declarations: [] - }) - ) - ) - store.dispatch(storeDeclaration(declaration)) - - const { component: testComponent, router: testRouter } = - await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: declaration.id, - pageId: 'review', - event: declaration.event.toLowerCase() - }) - ] - } - ) - - testComponent.find('#exit-btn').hostNodes().simulate('click') - expect(testRouter.state.location.pathname).toContain( - WORKQUEUE_TABS.readyForReview - ) - }) - - it('redirect to review tab when exit button is clicked', async () => { - // NOTE: check auth dispatches actions that eventually will retrieve user data - // from offline storage. Those would override the data we set here. - await flushPromises() - - const declaration = createReviewDeclaration( - uuid(), - birthDraftData, - EventType.Birth, - RegStatus.Validated - ) - - store.dispatch( - getStorageDeclarationsSuccess( - JSON.stringify({ - userID: 'currentUser', // mock - drafts: [declaration], - declarations: [] - }) - ) - ) - store.dispatch(storeDeclaration(declaration)) - - const { component: testComponent, router: testRouter } = - await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: declaration.id, - pageId: 'review', - event: declaration.event.toLowerCase() - }) - ] - } - ) - - testComponent.find('#exit-btn').hostNodes().simulate('click') - - expect(testRouter.state.location.pathname).toContain( - WORKQUEUE_TABS.readyForReview - ) - }) - - it('redirect to update tab when exit button is clicked', async () => { - // NOTE: check auth dispatches actions that eventually will retrieve user data - // from offline storage. Those would override the data we set here. - await flushPromises() - - const declaration = createReviewDeclaration( - uuid(), - birthDraftData, - EventType.Birth, - RegStatus.Rejected - ) - - store.dispatch( - getStorageDeclarationsSuccess( - JSON.stringify({ - userID: 'currentUser', // mock - drafts: [declaration], - declarations: [] - }) - ) - ) - store.dispatch(storeDeclaration(declaration)) - - const { component: testComponent, router: testRouter } = - await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: declaration.id, - pageId: 'review', - event: declaration.event.toLowerCase() - }) - ] - } - ) - - testComponent.find('#exit-btn').hostNodes().simulate('click') - expect(testRouter.state.location.pathname).toContain( - WORKQUEUE_TABS.requiresUpdate - ) - }) - - it('redirect to progress tab when exit button is clicked', async () => { - // NOTE: check auth dispatches actions that eventually will retrieve user data - // from offline storage. Those would override the data we set here. - await flushPromises() - - const declaration = createReviewDeclaration( - uuid(), - birthDraftData, - EventType.Birth - ) - - store.dispatch( - getStorageDeclarationsSuccess( - JSON.stringify({ - userID: 'currentUser', // mock - drafts: [declaration], - declarations: [] - }) - ) - ) - store.dispatch(storeDeclaration(declaration)) - - const { component: testComponent, router: testRouter } = - await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: declaration.id, - pageId: 'review', - event: declaration.event.toLowerCase() - }) - ] - } - ) - - testComponent.find('#exit-btn').hostNodes().simulate('click') - - expect(testRouter.state.location.pathname).toContain('/progress') - }) - - it('it checked if review form is already in store and avoid loading from backend', async () => { - // NOTE: check auth dispatches actions that eventually will retrieve user data - // from offline storage. Those would override the data we set here. - await flushPromises() - - const declaration = createReviewDeclaration( - uuid(), - birthDraftData, - EventType.Birth - ) - store.dispatch( - getStorageDeclarationsSuccess( - JSON.stringify({ - userID: 'currentUser', // mock - drafts: [declaration], - declarations: [] - }) - ) - ) - - store.dispatch(storeDeclaration(declaration)) - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: declaration.id, - pageId: 'review', - event: declaration.event.toLowerCase() - }) - ] - } - ) - - const data = testComponent - .find(RegisterForm) - .prop('declaration') as IDeclaration - - expect(data.data).toEqual(birthDraftData) - }) - - describe('Death review flow', () => { - it('it returns death registration', async () => { - // NOTE: check auth dispatches actions that eventually will retrieve user data - // from offline storage. Those would override the data we set here. - await flushPromises() - - store.dispatch(storeDeclaration(deathDeclaration)) - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: deathDeclaration.id, - pageId: 'review', - event: deathDeclaration.event.toLowerCase() - }) - ] - } - ) - - testComponent.update() - const data = testComponent - .find(RegisterForm) - .prop('declaration') as IDeclaration - - expect(data.data.deceased).toEqual( - expect.objectContaining({ - iDType: 'NATIONAL_ID', - iD: '1230000000000', - firstNames: 'মকবুল', - familyName: 'ইসলাম', - firstNamesEng: 'Mokbul', - familyNameEng: 'Islam', - nationality: 'BGD', - gender: 'male', - maritalStatus: 'MARRIED', - birthDate: '1987-02-16' - }) - ) - }) - it('populates proper death event section', async () => { - // NOTE: check auth dispatches actions that eventually will retrieve user data - // from offline storage. Those would override the data we set here. - await flushPromises() - - store.dispatch(storeDeclaration(deathDeclaration)) - const form = await getReviewFormFromStore(store, EventType.Death) - const { component: testComponent } = await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: deathDeclaration.id, - pageId: 'review', - event: deathDeclaration.event.toLowerCase() - }) - ] - } - ) - - testComponent.update() - const data = testComponent - .find(RegisterForm) - .prop('declaration') as IDeclaration - - expect(data.data.deathEvent).toEqual( - expect.objectContaining({ - deathDate: '1987-02-16', - manner: 'ACCIDENT', - placeOfDeath: 'OTHER', - deathLocation: '' - }) - ) - }) - }) - describe('ReviewForm tests for register scope', () => { - beforeEach(async () => { - getItem.mockReturnValue(declareScope) - store.dispatch(checkAuth()) - }) - - it('shows error message for user with declare scope', async () => { - // NOTE: check auth dispatches actions that eventually will retrieve user data - // from offline storage. Those would override the data we set here. - await flushPromises() - - store.dispatch(storeDeclaration(birthDeclaration)) - const { component: testComponent } = await createTestComponent( - , - { - store, - path: REVIEW_EVENT_PARENT_FORM_PAGE, - initialEntries: [ - formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, { - declarationId: birthDeclaration.id, - pageId: 'review', - event: birthDeclaration.event.toLowerCase() - }) - ] - } - ) - - expect( - testComponent - .find('#review-unauthorized-error-text') - .children() - .hostNodes() - .text() - ).toBe('We are unable to display this page to you') - }) - }) -}) diff --git a/packages/client/src/views/RegisterForm/review/EditConfirmation.test.tsx b/packages/client/src/views/RegisterForm/review/EditConfirmation.test.tsx deleted file mode 100644 index b0cce6d7a9e..00000000000 --- a/packages/client/src/views/RegisterForm/review/EditConfirmation.test.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import * as React from 'react' -import { EditConfirmation } from '@client/views/RegisterForm/review/EditConfirmation' -import { ReactWrapper } from 'enzyme' -import { createStore } from '@client/store' -import { createTestComponent } from '@client/tests/util' -import { vi } from 'vitest' - -const { store } = createStore() -const mockHandleEdit = vi.fn() -const mockHandleClose = vi.fn() - -describe('when user is in the review page', () => { - let editComponent: ReactWrapper<{}, {}> - beforeEach(async () => { - const { component: testComponent } = await createTestComponent( - , - { store } - ) - editComponent = testComponent - }) - - it('renders edit confirmation dialog', () => { - expect(editComponent.find('#edit_confirm').hostNodes()).toHaveLength(1) - }) - - it('mock edit button click', () => { - editComponent.find('#edit_confirm').hostNodes().simulate('click') - - expect(mockHandleEdit).toHaveBeenCalled() - }) - - it('mock preview back button click', () => { - editComponent.find('#preview_back').hostNodes().simulate('click') - - expect(mockHandleClose).toHaveBeenCalled() - }) -}) diff --git a/packages/client/src/views/RegisterForm/review/ReviewSection.test.tsx b/packages/client/src/views/RegisterForm/review/ReviewSection.test.tsx deleted file mode 100644 index f8a315c208f..00000000000 --- a/packages/client/src/views/RegisterForm/review/ReviewSection.test.tsx +++ /dev/null @@ -1,958 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { - createDeclaration, - createReviewDeclaration, - storeDeclaration -} from '@client/declarations' -import { - DATE, - DOCUMENT_UPLOADER_WITH_OPTION, - IForm, - // DeathSection, - LOCATION_SEARCH_INPUT, - TEXT, - // MarriageSection - // BirthSection, - ViewType -} from '@client/forms' -import { formMessages } from '@client/i18n/messages' -import { formatUrl } from '@client/navigation' -import { REVIEW_EVENT_PARENT_FORM_PAGE } from '@client/navigation/routes' -import { offlineDataReady } from '@client/offline/actions' -import { createStore } from '@client/store' -import { - createTestComponent, - flushPromises, - getRegisterFormFromStore, - mockOfflineData, - mockOfflineDataDispatch, - resizeWindow, - setScopes, - TestComponentWithRouteMock, - userDetails -} from '@client/tests/util' -import { waitForElement } from '@client/tests/wait-for-element' -import { isMobileDevice } from '@client/utils/commonUtils' -import { SCOPES } from '@opencrvs/commons/client' -import { - EventType as DeclarationEvent, - EventType, - RegStatus -} from '@client/utils/gateway' -import { - renderSelectDynamicLabel, - ReviewSection -} from '@client/views/RegisterForm/review/ReviewSection' -import { ReactWrapper } from 'enzyme' -import * as React from 'react' -import { createIntl } from 'react-intl' -import { v4 as uuid } from 'uuid' -import { Mock, SpyInstance, vi } from 'vitest' - -const { store } = createStore() -const mockHandler = vi.fn() - -const draft = createDeclaration(EventType.Birth) -draft.data = { - child: { firstNamesEng: 'John', familyNameEng: 'Doe' }, - father: { - detailsExist: true - }, - mother: { - detailsExist: true - }, - documents: { - imageUploader: { title: 'dummy', description: 'dummy', data: '' } - }, - registration: { - commentsOrNotes: '' - } -} - -const declaredBirthDeclaration = createReviewDeclaration( - uuid(), - draft.data, - EventType.Birth -) -const rejectedDraftBirth = createReviewDeclaration( - uuid(), - draft.data, - EventType.Birth, - RegStatus.Rejected -) -const rejectedDraftDeath = createReviewDeclaration( - uuid(), - draft.data, - EventType.Death, - RegStatus.Rejected -) -const rejectedDraftMarriage = createReviewDeclaration( - uuid(), - draft.data, - DeclarationEvent.Marriage, - RegStatus.Rejected -) - -describe('when in device of large viewport', () => { - let userAgentMock: SpyInstance - let form: Awaited> - - beforeEach(async () => { - store.dispatch(offlineDataReady(mockOfflineDataDispatch)) - await flushPromises() - form = await getRegisterFormFromStore(store, EventType.Birth) - userAgentMock = vi.spyOn(window.navigator, 'userAgent', 'get') - Object.assign(window, { outerWidth: 1034 }) - - userAgentMock.mockReturnValue('Desktop') - ;(isMobileDevice as Mock).mockRestore() - }) - - const intl = createIntl({ locale: 'en' }) - - describe('when user is in the review page', () => { - let reviewSectionComponent: TestComponentWithRouteMock['component'] - let reviewSectionRouter: TestComponentWithRouteMock['router'] - beforeEach(async () => { - const { component: testComponent, router } = await createTestComponent( - , - { - store, - initialEntries: [formatUrl(REVIEW_EVENT_PARENT_FORM_PAGE, {})] - } - ) - reviewSectionComponent = testComponent - reviewSectionRouter = router - await waitForElement(reviewSectionComponent, '#review_header') - }) - - it('Goes to child document while scroll to child section', async () => { - window.dispatchEvent(new Event('scroll')) - await waitForElement(reviewSectionComponent, '#child-accordion') - }) - - it('shows zero document error if no document is uploaded', async () => { - window.dispatchEvent(new Event('scroll')) - await waitForElement(reviewSectionComponent, '#zero_document') - }) - - describe('when user clicks on change link', () => { - beforeEach(() => { - reviewSectionComponent - .find('#btn_change_child_familyNameEng') - .hostNodes() - .first() - .simulate('click') - reviewSectionComponent.update() - }) - - it('edit dialog should not show up', () => { - expect( - reviewSectionComponent.find('#edit_confirm').hostNodes() - ).toHaveLength(0) - }) - - it('clicking on edit takes user back to form', async () => { - reviewSectionComponent - .find('#btn_change_child_familyNameEng') - .hostNodes() - .first() - .simulate('click') - reviewSectionComponent.update() - await flushPromises() - expect(reviewSectionRouter.state.location.pathname).toContain('reviews') - }) - }) - - it('renders header component', () => { - expect( - reviewSectionComponent.find('#review_header').hostNodes() - ).toHaveLength(1) - expect( - reviewSectionComponent.find('#review_header_title').hostNodes().text() - ).toBe('Government of the peoples republic of Bangladesh') - expect( - reviewSectionComponent.find('#review_header_subject').hostNodes().text() - ).toBe('Birth Declaration for Doe John') - }) - - it('typing additional comments input triggers onchange review form', async () => { - store.dispatch(storeDeclaration(draft)) - reviewSectionComponent - .find('#additional_comments') - .hostNodes() - .simulate('change', { - target: { - id: 'additional_comments', - value: 'some comments' - } - }) - expect(mockHandler).toBeCalled() - }) - }) - describe('return the correct label on dynamic fields', () => { - it('Should return the Bengali label', () => { - expect( - renderSelectDynamicLabel( - '8cbc862a-b817-4c29-a490-4a8767ff023c', - { resource: 'locations', dependency: 'countryPrimary' }, - { - countryPrimary: 'BGD', - statePrimary: '8cbc862a-b817-4c29-a490-4a8767ff023c' - }, - intl, - mockOfflineData, - 'bn' - ) - ).toBe('চট্টগ্রাম') - }) - it('Should return the English label', () => { - expect( - renderSelectDynamicLabel( - '8cbc862a-b817-4c29-a490-4a8767ff023c', - { resource: 'locations', dependency: 'countryPrimary' }, - { - countryPrimary: 'BGD', - statePrimary: '8cbc862a-b817-4c29-a490-4a8767ff023c' - }, - intl, - mockOfflineData, - 'en' - ) - ).toBe('Chittagong') - }) - }) - - describe('when user is in the review page for rejected birth declaration', () => { - let reviewSectionComponent: ReactWrapper<{}, {}> - beforeEach(async () => { - setScopes([SCOPES.RECORD_REGISTER], store) - - const { component: testComponent } = await createTestComponent( - , - { store } - ) - reviewSectionComponent = testComponent - }) - - it('Should not click the Reject Declaration', async () => { - const rejectButton = reviewSectionComponent.find( - '#rejectDeclarationBtn' - ).length - expect(rejectButton).toEqual(0) - }) - }) - - describe('when user is in the review page for rejected death declaration', () => { - let reviewSectionComponent: ReactWrapper<{}, {}> - beforeEach(async () => { - const { component: testComponent } = await createTestComponent( - , - { store } - ) - reviewSectionComponent = testComponent - }) - - it('Should not click the Reject Declaration', async () => { - const rejectButton = reviewSectionComponent.find( - '#rejectDeclarationBtn' - ).length - expect(rejectButton).toEqual(0) - }) - }) - - describe('when user is in the review page for rejected marriage declaration', () => { - let reviewSectionComponent: ReactWrapper<{}, {}> - beforeEach(async () => { - const { component: testComponent } = await createTestComponent( - , - { store } - ) - reviewSectionComponent = testComponent - }) - - it('Should not click the Reject Declaration', async () => { - const rejectButton = reviewSectionComponent.find( - '#rejectDeclarationBtn' - ).length - expect(rejectButton).toEqual(0) - }) - }) - - describe('when user is in the review page to validate birth declaration', () => { - let reviewSectionComponent: TestComponentWithRouteMock['component'] - let reviewSectionRouter: TestComponentWithRouteMock['router'] - - beforeEach(async () => { - setScopes( - [SCOPES.RECORD_SUBMIT_FOR_APPROVAL, SCOPES.RECORD_SUBMIT_FOR_UPDATES], - store - ) - - const { component: testComponent, router } = await createTestComponent( - , - { store } - ) - reviewSectionComponent = testComponent - reviewSectionRouter = router - }) - - it('Should click the validator Declaration Button', async () => { - const validatorButton = reviewSectionComponent.contains( - '#validatorDeclarationBtn' - ) - expect(validatorButton).toBeFalsy() - }) - - it('Should click the Reject Declaration Button', async () => { - const rejectButton = reviewSectionComponent.contains( - '#rejectDeclarationBtn' - ) - expect(rejectButton).toBeFalsy() - }) - - describe('when user clicks on change link', () => { - beforeEach(() => { - reviewSectionComponent - .find('#btn_change_child_familyNameEng') - .hostNodes() - .first() - .simulate('click') - reviewSectionComponent.update() - }) - - it('edit dialog should show up', () => { - expect( - reviewSectionComponent.find('#edit_confirm').hostNodes() - ).toHaveLength(1) - }) - - it('clicking on edit takes user back to form', async () => { - reviewSectionComponent - .find('#edit_confirm') - .hostNodes() - .simulate('click') - reviewSectionComponent.update() - await flushPromises() - expect(reviewSectionRouter.state.location.pathname).toContain('reviews') - }) - }) - }) - - describe('when form has location input field', () => { - let reviewSectionComponent: ReactWrapper<{}, {}> - - beforeAll(() => { - vi.resetAllMocks() - }) - - beforeEach(async () => { - setScopes([SCOPES.RECORD_REGISTER], store) - const form = { - sections: [ - { - id: 'registration', - viewType: 'hidden', - name: { - defaultMessage: 'Registration', - description: 'Form section name for Registration', - id: 'form.section.declaration.name' - }, - groups: [] - }, - { - id: 'child', - viewType: 'form' as ViewType, - title: formMessages.childTitle, - name: formMessages.childTitle, - groups: [ - { - id: 'child-view-group', - fields: [ - { - name: 'birthLocation', - type: LOCATION_SEARCH_INPUT, - searchableResource: ['facilities'], - searchableType: ['HEALTH_FACILITY'], - locationList: [], - required: true, - validator: [], - label: formMessages.birthLocation - } - ] - } - ] - }, - { - id: 'documents', - name: formMessages.documentsName, - title: formMessages.documentsTitle, - viewType: 'form' as ViewType, - groups: [ - { - id: 'documents-view-group', - fields: [ - { - name: 'uploadDocForMother', - extraValue: 'MOTHER', - type: DOCUMENT_UPLOADER_WITH_OPTION, - label: formMessages.uploadDocForMother, - required: true, - validator: [], - options: [ - { - label: formMessages.docTypeBirthCert, - value: 'BIRTH_CERTIFICATE' - } - ] - } - ] - } - ] - }, - { - id: 'preview', - viewType: 'preview', - name: { - defaultMessage: 'Preview', - description: 'Form section name for Preview', - id: 'register.form.section.preview.name' - }, - title: { - defaultMessage: 'Preview', - description: 'Form section title for Preview', - id: 'register.form.section.preview.title' - }, - groups: [ - { - id: 'preview-view-group', - fields: [] - } - ] - } - ] - } satisfies IForm - - const data = { - child: { - birthLocation: '0d8474da-0361-4d32-979e-af91f012340a' - }, - documents: {} - } - - const simpleDraft = createReviewDeclaration(uuid(), data, EventType.Birth) - - const { component: testComponent } = await createTestComponent( - , - { store } - ) - reviewSectionComponent = testComponent - }) - - it('renders selected location label', () => { - expect(reviewSectionComponent.find('#Hospital')).toBeTruthy() - }) - }) - - describe('when form has a postfix with no value', () => { - let reviewSectionComponent: ReactWrapper<{}, {}> - - beforeAll(() => { - vi.resetAllMocks() - }) - - beforeEach(async () => { - setScopes([SCOPES.RECORD_REGISTER], store) - const form = { - sections: [ - { - id: 'registration', - viewType: 'hidden', - name: { - defaultMessage: 'Registration' - }, - groups: [] - }, - { - id: 'child', - viewType: 'form' as ViewType, - title: formMessages.childTitle, - name: formMessages.childTitle, - groups: [ - { - id: 'child-view-group', - fields: [ - { - name: 'weight', - type: 'NUMBER', - required: false, - validator: [], - postfix: 'kg', - label: { - defaultMessage: 'Weight', - id: 'weight' - } - }, - { - name: 'height', - type: 'NUMBER', - required: false, - validator: [], - postfix: 'inches', - label: { - defaultMessage: 'Height', - id: 'height' - } - } - ] - } - ] - }, - { - id: 'documents', - name: formMessages.documentsName, - title: formMessages.documentsTitle, - viewType: 'form' as ViewType, - groups: [ - { - id: 'documents-view-group', - fields: [] - } - ] - }, - { - id: 'preview', - viewType: 'preview', - name: { - defaultMessage: 'Preview' - }, - title: { - defaultMessage: 'Preview' - }, - groups: [ - { - id: 'preview-view-group', - fields: [] - } - ] - } - ] - } satisfies IForm - - const data = { - child: { - weight: 67 - }, - documents: {} - } - - const simpleDraft = createReviewDeclaration(uuid(), data, EventType.Birth) - - const { component: testComponent } = await createTestComponent( - , - { store } - ) - reviewSectionComponent = testComponent - }) - - it('should not render postfix', () => { - const text = reviewSectionComponent.text() - - expect(text).toContain('kg') - expect(text).not.toContain('inches') - }) - }) - - describe('when form has empty field in a group', () => { - let reviewSectionComponent: ReactWrapper<{}, {}> - - beforeAll(() => { - vi.resetAllMocks() - }) - - beforeEach(async () => { - setScopes([SCOPES.RECORD_REGISTER], store) - const form = { - sections: [ - { - id: 'registration', - viewType: 'hidden', - name: { - defaultMessage: 'Registration', - description: 'Form section name for Registration', - id: 'form.section.declaration.name' - }, - groups: [] - }, - { - id: 'child', - viewType: 'form' as ViewType, - title: formMessages.childTitle, - name: formMessages.childTitle, - groups: [ - { - id: 'child-view-group', - fields: [ - { - name: 'addressLine1Placeofbirth', - type: TEXT, - required: false, - validator: [], - previewGroup: 'address', - label: { - defaultMessage: 'TestAddress 1', - id: 'test1' - } - }, - { - name: 'addressLine2Placeofbirth', - type: TEXT, - required: false, - validator: [], - previewGroup: 'address', - label: { - defaultMessage: 'TestAddress 2', - id: 'test2' - } - }, - { - name: 'addressLine3Placeofbirth', - type: TEXT, - required: false, - validator: [], - previewGroup: 'address', - label: { - defaultMessage: 'TestAddress 3', - id: 'test3' - } - } - ] - } - ] - }, - { - id: 'documents', - name: formMessages.documentsName, - title: formMessages.documentsTitle, - viewType: 'form' as ViewType, - groups: [ - { - id: 'documents-view-group', - fields: [ - { - name: 'uploadDocForMother', - extraValue: 'MOTHER', - type: DOCUMENT_UPLOADER_WITH_OPTION, - label: formMessages.uploadDocForMother, - required: true, - validator: [], - options: [ - { - label: formMessages.docTypeBirthCert, - value: 'BIRTH_CERTIFICATE' - } - ] - } - ] - } - ] - }, - { - id: 'preview', - viewType: 'preview', - name: { - defaultMessage: 'Preview', - description: 'Form section name for Preview', - id: 'register.form.section.preview.name' - }, - title: { - defaultMessage: 'Preview', - description: 'Form section title for Preview', - id: 'register.form.section.preview.title' - }, - groups: [ - { - id: 'preview-view-group', - fields: [] - } - ] - } - ] - } satisfies IForm - - const data = { - child: { - addressLine1Placeofbirth: 'District 9', - addressLine2Placeofbirth: '', - addressLine3Placeofbirth: 'Suburb 7' - }, - documents: {} - } - - const simpleDraft = createReviewDeclaration(uuid(), data, EventType.Birth) - - const { component: testComponent } = await createTestComponent( - , - { store } - ) - reviewSectionComponent = testComponent - }) - - it('renders only fields with values', () => { - const addressList = reviewSectionComponent.find({ - 'data-testid': 'row-value-TestAddress' - }) - - const innerHtml = addressList - .children() - .map((n) => n.html()) - .join('') - - const addressLines = innerHtml.split('
') - - expect(addressLines.length).toBe(2) - }) - }) -}) - -describe('when in device of small viewport', () => { - let reviewSectionComponent: ReactWrapper<{}, {}> - let userAgentMock: SpyInstance - const { store } = createStore() - - beforeAll(() => { - resizeWindow(600, 960) - vi.resetAllMocks() - }) - - beforeEach(async () => { - userAgentMock = vi.spyOn(window.navigator, 'userAgent', 'get') - userAgentMock.mockReturnValue('Android') - setScopes([SCOPES.RECORD_REGISTER], store) - const form = { - sections: [ - { - id: 'registration', - viewType: 'hidden', - name: { - defaultMessage: 'Registration', - description: 'Form section name for Registration', - id: 'form.section.declaration.name' - }, - groups: [] - }, - { - id: 'mother', - name: formMessages.motherTitle, - title: formMessages.motherTitle, - viewType: 'form' as ViewType, - groups: [ - { - id: 'mother-view-group', - fields: [ - { - name: 'motherBirthDate', - type: DATE, - label: formMessages.dateOfBirth, - required: true, - validator: [], - initialValue: '' - } - ] - } - ] - }, - { - id: 'documents', - name: formMessages.documentsName, - title: formMessages.documentsTitle, - viewType: 'form' as ViewType, - groups: [ - { - id: 'documents-view-group', - fields: [ - { - name: 'uploadDocForMother', - extraValue: 'MOTHER', - type: DOCUMENT_UPLOADER_WITH_OPTION, - label: formMessages.uploadDocForMother, - required: true, - validator: [], - options: [ - { - label: formMessages.docTypeBirthCert, - value: 'BIRTH_CERTIFICATE' - } - ] - } - ] - } - ] - }, - { - id: 'preview', - viewType: 'preview', - name: { - defaultMessage: 'Preview', - description: 'Form section name for Preview', - id: 'register.form.section.preview.name' - }, - title: { - defaultMessage: 'Preview', - description: 'Form section title for Preview', - id: 'register.form.section.preview.title' - }, - groups: [ - { - id: 'preview-view-group', - fields: [] - } - ] - } - ] - } satisfies IForm - ;(isMobileDevice as Mock).mockRestore() - - const data = { - documents: { - uploadDocForMother: [ - { - optionValues: ['MOTHER', 'NATIONAL_ID'], - type: 'image/png', - data: 'data:image/png;base64,abcd' - } - ], - uploadDocForChildDOB: [ - { - optionValues: ['CHILD', 'NOTIFICATION_OF_BIRTH'], - type: 'image/png', - data: 'data:image/png;base64,abcd' - } - ] - } - } - - const simpleDraft = createReviewDeclaration( - uuid(), - data, - DeclarationEvent.Birth - ) - - const { component: testComponent } = await createTestComponent( - , - { store } - ) - - reviewSectionComponent = testComponent - }) - - it('renders preview list of documents', () => { - expect( - reviewSectionComponent - .find('#preview-list-all_attachment_list') - .hostNodes() - ).toHaveLength(1) - }) - - describe('clicking on preview list item...', () => { - beforeEach(() => { - reviewSectionComponent - .find('#document_NOTIFICATION_OF_BIRTH_link') - .hostNodes() - .simulate('click') - - reviewSectionComponent.update() - }) - - it('opens document preview page', () => { - expect( - reviewSectionComponent.find('#preview_image_field').hostNodes() - ).toHaveLength(1) - }) - - it('clicking on back button closes image preview', () => { - reviewSectionComponent - .find('#preview_close') - .hostNodes() - .simulate('click') - - expect( - reviewSectionComponent.find('#preview_image_field').hostNodes() - ).toHaveLength(0) - }) - }) -}) diff --git a/packages/client/src/views/RegisterForm/review/ReviewSection.tsx b/packages/client/src/views/RegisterForm/review/ReviewSection.tsx index 634083193a2..e32af8bc59e 100644 --- a/packages/client/src/views/RegisterForm/review/ReviewSection.tsx +++ b/packages/client/src/views/RegisterForm/review/ReviewSection.tsx @@ -315,7 +315,7 @@ function renderSelectOrRadioLabel( return option?.label ? intl.formatMessage(option.label) : value } -export function renderSelectDynamicLabel( +function renderSelectDynamicLabel( value: IFormFieldValue, options: IDynamicOptions, draftData: IFormSectionData, diff --git a/packages/client/src/views/SearchResult/AdvancedSearch.test.tsx b/packages/client/src/views/SearchResult/AdvancedSearch.test.tsx deleted file mode 100644 index a8478917615..00000000000 --- a/packages/client/src/views/SearchResult/AdvancedSearch.test.tsx +++ /dev/null @@ -1,164 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { ADVANCED_SEARCH_RESULT } from '@client/navigation/routes' -import { setAdvancedSearchParam } from '@client/search/advancedSearch/actions' -import { createStore } from '@client/store' -import { - createTestComponent, - mockUserResponse, - setScopes -} from '@client/tests/util' -import { ReactWrapper } from 'enzyme' -import * as React from 'react' -import { createMemoryRouter } from 'react-router-dom' -import { AdvancedSearchConfig } from './AdvancedSearch' -import { SCOPES } from '@opencrvs/commons/client' -import { setUserDetails } from '@client/profile/profileActions' - -let testComponent: ReactWrapper -beforeEach(async () => { - const { store } = createStore() - setScopes([SCOPES.SEARCH_BIRTH, SCOPES.SEARCH_DEATH], store) - testComponent = ( - await createTestComponent(, { store }) - )?.component - testComponent.update() -}) - -describe('should render both birth and death tabs', () => { - it('should shows birth, and death tab button', async () => { - expect(testComponent.find('#tab_birth').hostNodes().text()).toBe('Birth') - expect(testComponent.find('#tab_death').hostNodes().text()).toBe('Death') - }) -}) - -describe('when advancedSearchPage renders with no active params in store', () => { - it('renders searchbutton as enabled', async () => { - expect( - testComponent.find('#search').hostNodes().props().disabled - ).toBeFalsy() - }) - it('renders all accordions as closed', async () => { - expect( - testComponent.find('#BirthRegistrationDetails-content').hostNodes().length - ).toBe(0) - }) -}) - -describe('when search button is clicked with insufficient parameters', () => { - let testComponent: ReactWrapper - - beforeEach(async () => { - const { store } = createStore() - setScopes([SCOPES.SEARCH_BIRTH, SCOPES.SEARCH_DEATH], store) - store.dispatch( - setAdvancedSearchParam({ - event: 'birth' - }) - ) - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - store.dispatch(setUserDetails(mockUserResponse as any)) - ;({ component: testComponent } = await createTestComponent( - , - { - store - } - )) - testComponent.update() - }) - - it('renders error message when searchbutton is clicked under birth tab', async () => { - testComponent.find('#search').hostNodes().simulate('click') - - await new Promise((resolve) => setTimeout(resolve, 0)) - testComponent.update() - - const errorText = testComponent.find('#error-text').hostNodes() - expect(errorText.exists()).toBe(true) - }) -}) - -describe('when search button is clicked with insufficient parameters', () => { - let testComponent: ReactWrapper - - beforeEach(async () => { - const { store } = createStore() - setScopes([SCOPES.SEARCH_BIRTH, SCOPES.SEARCH_DEATH], store) - store.dispatch( - setAdvancedSearchParam({ - event: 'death' - }) - ) - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - store.dispatch(setUserDetails(mockUserResponse as any)) - ;({ component: testComponent } = await createTestComponent( - , - { - store - } - )) - testComponent.update() - }) - - it('renders error message when searchbutton is clicked under death tab', async () => { - testComponent.find('#search').hostNodes().simulate('click') - - await new Promise((resolve) => setTimeout(resolve, 0)) - testComponent.update() - - const errorText = testComponent.find('#error-text').hostNodes() - expect(errorText.exists()).toBe(true) - }) -}) - -describe('when advancedSearchPage renders with 2 or more active params in store', () => { - let testComponent: ReactWrapper - let router: ReturnType - beforeEach(async () => { - const { store } = createStore() - setScopes([SCOPES.SEARCH_BIRTH, SCOPES.SEARCH_DEATH], store) - store.dispatch( - setAdvancedSearchParam({ - event: 'birth', - declarationLocationId: '0d8474da-0361-4d32-979e-af91f012340a', - registrationStatuses: ['IN_PROGRESS'] - }) - ) - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - store.dispatch(setUserDetails(mockUserResponse as any)) - ;({ component: testComponent, router } = await createTestComponent( - , - { - store - } - )) - testComponent.update() - }) - - it('renders searchbutton as enabled', async () => { - expect( - testComponent.find('#search').hostNodes().props().disabled - ).toBeFalsy() - }) - - it('renders accordions with active params as opened', async () => { - expect( - testComponent.find('#BirthRegistrationDetails-content').hostNodes().length - ).toBe(1) - }) - - it('goes to advancedSearch Result page if search button is clicked', async () => { - testComponent.find('#search').hostNodes().simulate('click') - expect(router.state.location.pathname).toContain( - `${ADVANCED_SEARCH_RESULT}` - ) - }) -}) diff --git a/packages/client/src/views/SearchResult/SearchResult.test.tsx b/packages/client/src/views/SearchResult/SearchResult.test.tsx deleted file mode 100644 index 755509673fe..00000000000 --- a/packages/client/src/views/SearchResult/SearchResult.test.tsx +++ /dev/null @@ -1,876 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { Spinner } from '@opencrvs/components/lib/Spinner' -import { Workqueue } from '@opencrvs/components/lib/Workqueue' - -import { merge } from 'lodash' -import * as React from 'react' -import { queries } from '@client/profile/queries' -import { SEARCH_EVENTS } from '@client/search/queries' -import { createStore } from '@client/store' -import { - createTestComponent, - mockUserResponse, - REGISTRAR_DEFAULT_SCOPES, - setScopes -} from '@client/tests/util' -import { SearchResult } from '@client/views/SearchResult/SearchResult' - -import { waitForElement } from '@client/tests/wait-for-element' -import { EventType } from '@client/utils/gateway' -import { storeDeclaration } from '@client/declarations' -import { vi } from 'vitest' - -const mockFetchUserDetails = vi.fn() - -const nameObj = { - data: { - getUser: { - name: [ - { - use: 'en', - firstNames: 'Mohammad', - familyName: 'Ashraful', - __typename: 'HumanName' - }, - { use: 'bn', firstNames: '', familyName: '', __typename: 'HumanName' } - ], - role: { - id: 'DISTRICT_REGISTRAR', - label: { - defaultMessage: 'District Registrar', - description: 'Name for user role Field Agent', - id: 'userRole.fieldAgent' - } - } - } - } -} - -merge(mockUserResponse, nameObj) -mockFetchUserDetails.mockReturnValue(mockUserResponse) -queries.fetchUserDetails = mockFetchUserDetails - -describe('SearchResult tests', () => { - let store: ReturnType['store'] - - beforeEach(async () => { - ;({ store } = createStore()) - setScopes(REGISTRAR_DEFAULT_SCOPES, store) - }) - - it('sets loading state while waiting for data', async () => { - const { component: testComponent } = await createTestComponent( - , - { store, initialEntries: ['/?searchText=DW0UTHR&searchType=TRACKING_ID'] } - ) - - // @ts-ignore - expect(testComponent.containsMatchingElement(Spinner)).toBe(true) - }) - - it('renders all items returned from graphql query', async () => { - const graphqlMock = [ - { - request: { - operationName: null, - query: SEARCH_EVENTS, - variables: { - advancedSearchParameters: { - trackingId: '', - nationalId: '', - registrationNumber: '', - contactNumber: '+8801622688232', - name: '', - contactEmail: '' - }, - sort: 'DESC' - } - }, - result: { - data: { - searchEvents: { - totalItems: 4, - results: [ - { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e95', - type: EventType.Death, - __typename: 'X', - registration: { - __typename: 'X', - status: 'DECLARED', - trackingId: 'DW0UTHR', - registrationNumber: null, - duplicates: ['308c35b4-04f8-4664-83f5-9790e790cd33'], - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1' - }, - dateOfDeath: '2007-01-01', - deceasedName: [ - { - __typename: 'X', - firstNames: 'Iliyas', - familyName: 'Khan' - }, - { - __typename: 'X', - firstNames: 'ইলিয়াস', - familyName: 'খান' - } - ], - - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - dateOfBirth: '', - childName: [] - }, - { - id: 'c7e83060-4db9-4057-8b14-71841243b05f', - type: EventType.Death, - __typename: 'X', - registration: { - __typename: 'X', - status: 'REJECTED', - trackingId: 'DXMJPYA', - registrationNumber: null, - duplicates: null, - registeredLocationId: - '308c35b4-04f8-4664-83f5-9790e790cde1', - reason: - 'duplicate,misspelling,missing_supporting_doc,other', - comment: 'Rejected' - }, - dateOfDeath: '2010-01-01', - deceasedName: [ - { - __typename: 'X', - firstNames: 'Zahir', - familyName: 'Raihan' - }, - { - __typename: 'X', - firstNames: 'জহির', - familyName: 'রায়হান' - } - ], - - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - dateOfBirth: '', - childName: [] - }, - { - id: '150dd4ca-6822-4f94-ad92-b9be037dec2f', - type: EventType.Birth, - __typename: 'X', - registration: { - __typename: 'X', - status: 'REGISTERED', - trackingId: 'BQRZWDR', - registrationNumber: '2019333494BQRZWDR2', - duplicates: null, - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1' - }, - dateOfBirth: '2010-01-01', - childName: [ - { - __typename: 'X', - firstNames: 'Fokrul', - familyName: 'Islam' - }, - { - __typename: 'X', - firstNames: 'ফকরুল', - familyName: 'ইসলাম' - } - ], - - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - dateOfDeath: '', - deceasedName: [] - }, - { - id: '150dd4ca-6822-4f94-ad92-brbe037dec2f', - type: EventType.Birth, - __typename: 'X', - registration: { - __typename: 'X', - status: 'VALIDATED', - trackingId: 'BQRZWDR', - registrationNumber: '2019333494BQRZWDR2', - duplicates: null, - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1' - }, - dateOfBirth: '2010-01-01', - childName: [ - { - __typename: 'X', - firstNames: 'Fokrul', - familyName: 'Islam' - }, - { - __typename: 'X', - firstNames: 'ফকরুল', - familyName: 'ইসলাম' - } - ], - - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - dateOfDeath: '', - deceasedName: [] - }, - { - id: '150dd4ca-6822-4f94-ad92-b9beee7dec2f', - type: EventType.Birth, - __typename: 'X', - registration: { - __typename: 'X', - status: 'WAITING_VALIDATION', - trackingId: 'BQRZWDR', - registrationNumber: '2019333494BQRZWDR2', - duplicates: null, - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1' - }, - dateOfBirth: '2010-01-01', - childName: [ - { - __typename: 'X', - firstNames: 'Fokrul', - familyName: 'Islam' - }, - { - __typename: 'X', - firstNames: 'ফকরুল', - familyName: 'ইসলাম' - } - ], - - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - dateOfDeath: '', - deceasedName: [] - }, - { - id: 'fd60a75e-314e-4231-aab7-e6b71fb1106a', - type: EventType.Birth, - __typename: 'X', - registration: { - __typename: 'X', - status: 'CERTIFIED', - trackingId: 'B3DBJMP', - registrationNumber: '2019333494B3DBJMP5', - duplicates: null, - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1' - }, - dateOfBirth: '2008-01-01', - childName: [ - { - __typename: 'X', - firstNames: 'Rafiq', - familyName: 'Islam' - }, - { - __typename: 'X', - firstNames: 'রফিক', - familyName: 'ইসলাম' - } - ], - - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - dateOfDeath: '', - deceasedName: [] - } - ], - __typename: 'EventSearchResultSet' - } - } - } - } - ] - - const { component: testComponent } = await createTestComponent( - , - { - store, - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - graphqlMocks: graphqlMock as any, - initialEntries: ['/?searchText=01622688232&searchType=PHONE_NUMBER'] - } - ) - - // wait for mocked data to load mockedProvider - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - testComponent.update() - const data = testComponent.find(Workqueue).prop('content') - expect(data.length).toEqual(6) - }) - - it('renders error text when an error occurs', async () => { - const graphqlMock = [ - { - request: { - query: SEARCH_EVENTS, - variables: { - advancedSearchParameters: { - trackingId: '', - nationalId: '', - registrationNumber: '', - contactNumber: '+8801622688232', - contactEmail: '' - }, - sort: 'DESC' - } - }, - error: new Error('boom') - } - ] - - const { component: testComponent } = await createTestComponent( - , - { - store, - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - graphqlMocks: graphqlMock as any, - initialEntries: ['/?searchText=+8801622688232&searchType=PHONE_NUMBER'] - } - ) - - // wait for mocked data to load mockedProvider - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - - testComponent.update() - expect( - testComponent.find('#search-result-error-text').hostNodes().text() - ).toBe('An error occurred while searching') - }) - it('renders empty search page with a header in small devices', async () => { - const testSearchResultComponent = await createTestComponent( - , - { store, initialEntries: ['/search?location='] } - ) - - Object.defineProperty(window, 'innerWidth', { - writable: true, - configurable: true, - value: 200 - }) - - const searchTextInput = testSearchResultComponent.component - .find('#searchText') - .hostNodes() - - expect(searchTextInput).toHaveLength(1) - - searchTextInput.simulate('change', { target: { value: 'DW0UTHR' } }) - - testSearchResultComponent.component - .find('#searchIconButton') - .hostNodes() - .simulate('click') - - expect(testSearchResultComponent.router.state.location.search).toBe( - '?searchText=DW0UTHR&searchType=TRACKING_ID' - ) - }) - - it('renders download button and modals in search page', async () => { - const graphqlMock = [ - { - request: { - operationName: null, - query: SEARCH_EVENTS, - variables: { - advancedSearchParameters: { - trackingId: 'DW0UTHR', - nationalId: '', - registrationNumber: '', - contactNumber: '', - name: '', - contactEmail: '' - }, - sort: 'DESC' - } - }, - result: { - data: { - searchEvents: { - totalItems: 1, - results: [ - { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e95', - type: EventType.Death, - __typename: 'X', - registration: { - __typename: 'X', - status: 'DECLARED', - trackingId: 'DW0UTHR', - registrationNumber: null, - duplicates: [], - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1' - }, - dateOfDeath: '2007-01-01', - deceasedName: [ - { - __typename: 'X', - firstNames: 'Iliyas', - familyName: 'Khan' - }, - { - __typename: 'X', - firstNames: 'ইলিয়াস', - familyName: 'খান' - } - ], - - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - dateOfBirth: '', - childName: [] - } - ], - __typename: 'EventSearchResultSet' - } - } - } - } - ] - - const { component: testComponent } = await createTestComponent( - , - { - store, - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - graphqlMocks: graphqlMock as any, - initialEntries: ['/?searchText=DW0UTHR&searchType=TRACKING_ID'] - } - ) - - // wait for mocked data to load mockedProvider - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - - testComponent.update() - testComponent.find('#ListItemAction-0-icon').hostNodes().simulate('click') - - expect(testComponent.find('#assignment').hostNodes()).toHaveLength(1) - }) - - it('renders print button in search page', async () => { - const declaration = { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e92', - data: {}, - event: EventType.Birth, - downloadStatus: 'DOWNLOADED', - submissionStatus: 'REGISTERED' - } - - // @ts-ignore - store.dispatch(storeDeclaration(declaration)) - const graphqlMock = [ - { - request: { - operationName: null, - query: SEARCH_EVENTS, - variables: { - advancedSearchParameters: { - trackingId: 'DW0UTHR', - nationalId: '', - registrationNumber: '', - contactNumber: '', - name: '', - contactEmail: '' - }, - sort: 'DESC' - } - }, - result: { - data: { - searchEvents: { - totalItems: 1, - results: [ - { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e92', - type: EventType.Death, - __typename: 'X', - registration: { - __typename: 'X', - status: 'REGISTERED', - trackingId: 'DW0UTHR', - registrationNumber: null, - duplicates: [], - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1' - }, - dateOfDeath: '2007-01-01', - deceasedName: [ - { - __typename: 'X', - firstNames: 'Iliyas', - familyName: 'Khan' - }, - { - __typename: 'X', - firstNames: 'ইলিয়াস', - familyName: 'খান' - } - ], - - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - dateOfBirth: '', - childName: [] - } - ], - __typename: 'EventSearchResultSet' - } - } - } - } - ] - - Object.defineProperty(window, 'innerWidth', { - writable: true, - configurable: true, - value: 1100 - }) - const { component: testComponent } = await createTestComponent( - , - { - store, - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - graphqlMocks: graphqlMock as any, - initialEntries: ['/?searchText=DW0UTHR&searchType=TRACKING_ID'] - } - ) - - // wait for mocked data to load mockedProvider - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - - testComponent.update() - const printButton = await waitForElement( - testComponent, - '#ListItemAction-0-Print' - ) - printButton.hostNodes().simulate('click') - expect(printButton.hostNodes()).toHaveLength(1) - }) - - it('renders review button in search page while declaration is validated', async () => { - const declaration = { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e91', - data: {}, - event: EventType.Birth, - downloadStatus: 'DOWNLOADED', - submissionStatus: 'VALIDATED' - } - - // @ts-ignore - store.dispatch(storeDeclaration(declaration)) - const graphqlMock = [ - { - request: { - operationName: null, - query: SEARCH_EVENTS, - variables: { - advancedSearchParameters: { - trackingId: 'DW0UTHR', - nationalId: '', - registrationNumber: '', - contactNumber: '', - name: '', - contactEmail: '' - }, - sort: 'DESC' - } - }, - result: { - data: { - searchEvents: { - totalItems: 1, - results: [ - { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e91', - type: EventType.Death, - __typename: 'X', - registration: { - __typename: 'X', - status: 'VALIDATED', - trackingId: 'DW0UTHR', - registrationNumber: null, - duplicates: [], - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1' - }, - dateOfDeath: '2007-01-01', - deceasedName: [ - { - __typename: 'X', - firstNames: 'Iliyas', - familyName: 'Khan' - }, - { - __typename: 'X', - firstNames: 'ইলিয়াস', - familyName: 'খান' - } - ], - - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - dateOfBirth: '', - childName: [] - } - ], - __typename: 'EventSearchResultSet' - } - } - } - } - ] - - const { component: testComponent } = await createTestComponent( - , - { - store, - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - graphqlMocks: graphqlMock as any, - initialEntries: ['/?searchText=DW0UTHR&searchType=TRACKING_ID'] - } - ) - - // wait for mocked data to load mockedProvider - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - - testComponent.update() - - const reviewButton = await waitForElement( - testComponent, - '#ListItemAction-0-Review' - ) - - expect(reviewButton.hostNodes()).toHaveLength(1) - }) -}) - -describe('SearchResult downloadButton tests', () => { - let store: ReturnType['store'] - - beforeEach(async () => { - ;({ store } = createStore()) - setScopes(REGISTRAR_DEFAULT_SCOPES, store) - }) - it('renders review button in search page', async () => { - const declaration = { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e91', - data: {}, - event: EventType.Birth, - downloadStatus: 'DOWNLOADED', - submissionStatus: 'DECLARED' - } - - // @ts-ignore - store.dispatch(storeDeclaration(declaration)) - const graphqlMock = [ - { - request: { - operationName: null, - query: SEARCH_EVENTS, - variables: { - advancedSearchParameters: { - trackingId: 'DW0UTHR', - nationalId: '', - registrationNumber: '', - contactNumber: '', - name: '', - contactEmail: '' - }, - sort: 'DESC' - } - }, - result: { - data: { - searchEvents: { - totalItems: 1, - results: [ - { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e91', - type: EventType.Death, - __typename: 'X', - registration: { - __typename: 'X', - status: 'DECLARED', - trackingId: 'DW0UTHR', - registrationNumber: null, - duplicates: [], - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1' - }, - dateOfDeath: '2007-01-01', - deceasedName: [ - { - __typename: 'X', - firstNames: 'Iliyas', - familyName: 'Khan' - }, - { - __typename: 'X', - firstNames: 'ইলিয়াস', - familyName: 'খান' - } - ], - - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - dateOfBirth: '', - childName: [] - } - ], - __typename: 'EventSearchResultSet' - } - } - } - } - ] - - const { component: testComponent } = await createTestComponent( - , - { - store, - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - graphqlMocks: graphqlMock as any, - initialEntries: ['/?searchText=DW0UTHR&searchType=TRACKING_ID'] - } - ) - - // wait for mocked data to load mockedProvider - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - - testComponent.update() - - const reviewButton = await waitForElement( - testComponent, - '#ListItemAction-0-Review' - ) - - expect(reviewButton.hostNodes()).toHaveLength(1) - }) - it('renders update button in search page', async () => { - const declaration = { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e92', - data: {}, - event: EventType.Birth, - downloadStatus: 'DOWNLOADED', - submissionStatus: 'REJECTED' - } - - // @ts-ignore - store.dispatch(storeDeclaration(declaration)) - const graphqlMock = [ - { - request: { - operationName: null, - query: SEARCH_EVENTS, - variables: { - advancedSearchParameters: { - trackingId: 'DW0UTHR', - nationalId: '', - registrationNumber: '', - contactNumber: '', - name: '', - contactEmail: '' - }, - sort: 'DESC' - } - }, - result: { - data: { - searchEvents: { - totalItems: 1, - results: [ - { - id: 'bc09200d-0160-43b4-9e2b-5b9e90424e92', - type: EventType.Death, - __typename: 'X', - registration: { - __typename: 'X', - status: 'REJECTED', - trackingId: 'DW0UTHR', - registrationNumber: null, - duplicates: [], - registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1' - }, - dateOfDeath: '2007-01-01', - deceasedName: [ - { - __typename: 'X', - firstNames: 'Iliyas', - familyName: 'Khan' - }, - { - __typename: 'X', - firstNames: 'ইলিয়াস', - familyName: 'খান' - } - ], - - // TODO: When fragmentMatching work is completed, remove unnecessary result objects - // PR: https://github.com/opencrvs/opencrvs-core/pull/836/commits/6302fa8f015fe313cbce6197980f1300bf4eba32 - dateOfBirth: '', - childName: [] - } - ], - __typename: 'EventSearchResultSet' - } - } - } - } - ] - - const { component: testComponent } = await createTestComponent( - , - { - store, - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - graphqlMocks: graphqlMock as any, - initialEntries: ['/?searchText=DW0UTHR&searchType=TRACKING_ID'] - } - ) - - // wait for mocked data to load mockedProvider - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - - testComponent.update() - - const updateButton = await waitForElement( - testComponent, - '#ListItemAction-0-Update' - ) - - expect(updateButton.hostNodes()).toHaveLength(1) - }) -}) diff --git a/packages/client/src/views/Settings/Settings.test.tsx b/packages/client/src/views/Settings/Settings.test.tsx index 8223d0465a0..5cf83b48f51 100644 --- a/packages/client/src/views/Settings/Settings.test.tsx +++ b/packages/client/src/views/Settings/Settings.test.tsx @@ -13,9 +13,9 @@ import { createTestComponent, userDetails, flushPromises, - getFileFromBase64String, - validImageB64String + getFileFromBase64String } from '@client/tests/util' +import { validImageB64String } from '@client/tests/mock-offline-data' import { createStore } from '@client/store' import { SettingsPage } from '@client/views/Settings/SettingsPage' import { getStorageUserDetailsSuccess } from '@opencrvs/client/src/profile/profileActions' diff --git a/packages/client/src/views/SysAdmin/Performance/CompletenessRates.test.tsx b/packages/client/src/views/SysAdmin/Performance/CompletenessRates.test.tsx deleted file mode 100644 index 3434096626d..00000000000 --- a/packages/client/src/views/SysAdmin/Performance/CompletenessRates.test.tsx +++ /dev/null @@ -1,293 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ - -import * as React from 'react' -import { ReactWrapper } from 'enzyme' - -import { - createTestComponent, - createTestStore, - TestComponentWithRouteMock -} from '@client/tests/util' -import { AppStore } from '@client/store' -import { CompletenessRates } from '@client/views/SysAdmin/Performance/CompletenessRates' -import { EVENT_COMPLETENESS_RATES } from '@client/navigation/routes' -import { - FETCH_MONTH_WISE_EVENT_ESTIMATIONS, - IS_LEAF_LEVEL_LOCATION -} from '@client/views/SysAdmin/Performance/queries' -import { waitForElement } from '@client/tests/wait-for-element' -import { stringify, parse } from 'qs' -import { GraphQLError } from 'graphql' -import { vi } from 'vitest' -import { formatUrl } from '@client/navigation' - -const LOCATION_DHAKA_DIVISION = { - displayLabel: 'Dhaka Division', - id: '6e1f3bce-7bcb-4bf6-8e35-0d9facdf158b', - searchableText: 'Dhaka' -} -const timeStart = new Date(2019, 11, 6) -const timeEnd = new Date(2019, 11, 13) - -describe('Registraion Rates tests', () => { - const graphqlMocks = [ - { - request: { - query: IS_LEAF_LEVEL_LOCATION, - variables: { parentId: '6e1f3bce-7bcb-4bf6-8e35-0d9facdf158b' } - }, - result: { - data: { - isLeafLevelLocation: true - } - } - }, - { - request: { - query: FETCH_MONTH_WISE_EVENT_ESTIMATIONS, - variables: { - event: 'BIRTH', - locationId: '6e1f3bce-7bcb-4bf6-8e35-0d9facdf158b', - timeStart: timeStart.toISOString(), - timeEnd: timeEnd.toISOString() - } - }, - result: { - data: { - fetchMonthWiseEventMetrics: { - details: [ - { - actualTotalRegistration: 20, - actualTargetDayRegistration: 9, - estimatedRegistration: 45, - estimatedTargetDayPercentage: 4.5, - month: 'April', - year: '2020', - startOfMonth: '2020-03-30T18:00:00.000Z' - }, - { - actualTotalRegistration: 10, - actualTargetDayRegistration: 0, - estimatedRegistration: 45, - estimatedTargetDayPercentage: 0, - month: 'March', - year: '2020', - startOfMonth: '2020-02-29T18:00:00.000Z' - } - ], - total: { - actualTotalRegistration: 30, - actualTargetDayRegistration: 9, - estimatedRegistration: 45, - estimatedTargetDayPercentage: 2.25 - } - } - } - } - } - ] - let component: ReactWrapper<{}, {}> - let router: TestComponentWithRouteMock['router'] - let store: AppStore - - beforeAll(async () => { - Date.now = vi.fn(() => 1487076708000) - const { store: testStore } = await createTestStore() - store = testStore - }) - - beforeEach(async () => { - const { component: testComponent, router: testRouter } = - await createTestComponent(, { - store, - path: EVENT_COMPLETENESS_RATES, - initialEntries: [ - formatUrl(EVENT_COMPLETENESS_RATES, { - eventType: 'birth' - }) + - '?' + - stringify({ - time: 'withinTarget', - locationId: LOCATION_DHAKA_DIVISION.id, - timeEnd: new Date(1487076708000).toISOString(), - timeStart: new Date(1456868800000).toISOString() - }) - ], - graphqlMocks: graphqlMocks - }) - - component = testComponent - router = testRouter - - // wait for mocked data to load mockedProvider - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - - component.update() - }) - - it('renders the component', async () => { - Object.defineProperty(window, 'innerWidth', { - writable: true, - configurable: true, - value: 200 - }) - await waitForElement(component, '#reg-rates') - }) - - it('clicking on any other preset range changes date ranges in url', async () => { - const dateRangePickerElement = await waitForElement( - component, - '#date-range-picker-action' - ) - expect(dateRangePickerElement.hostNodes().text()).toBe('Last 12 months') - const previousQueryParams = router.state.location.search - dateRangePickerElement.hostNodes().simulate('click') - const last30DaysPresetButtonElement = await waitForElement( - component, - '#last30Days' - ) - last30DaysPresetButtonElement.hostNodes().at(0).simulate('click') - const confirmButtonElement = await waitForElement( - component, - '#date-range-confirm-action' - ) - confirmButtonElement.hostNodes().simulate('click') - expect(router.state.location.search).not.toBe(previousQueryParams) - }) - - it('click on close button or outside modal closes location picker modal', async () => { - const locationPickerElement = await waitForElement( - component, - '#location-range-picker-action' - ) - locationPickerElement.hostNodes().simulate('click') - - expect(component.find('#picker-modal').hostNodes()).toHaveLength(1) - - component.find('#close-btn').hostNodes().simulate('click') - - expect(component.find('#picker-modal').hostNodes()).toHaveLength(0) - - locationPickerElement.hostNodes().simulate('click') - expect(component.find('#picker-modal').hostNodes()).toHaveLength(1) - component.find('#cancelable-area').hostNodes().simulate('click') - expect(component.find('#picker-modal').hostNodes()).toHaveLength(0) - }) - - it('changing location id from location picker updates the query params', async () => { - const locationIdBeforeChange = parse(router.state.location.search, { - ignoreQueryPrefix: true - }).locationId - - const locationPickerElement = await waitForElement( - component, - '#location-range-picker-action' - ) - - locationPickerElement.hostNodes().simulate('click') - - const locationSearchInput = await waitForElement( - component, - '#locationSearchInput' - ) - locationSearchInput.hostNodes().simulate('change', { - target: { value: 'Baniajan', id: 'locationSearchInput' } - }) - - const searchResultOption = await waitForElement( - component, - '#locationOptionbfe8306c-0910-48fe-8bf5-0db906cf3155' - ) - searchResultOption.hostNodes().simulate('click') - - const newLocationId = parse(router.state.location.search, { - ignoreQueryPrefix: true - }).locationId - expect(newLocationId).not.toBe(locationIdBeforeChange) - expect(newLocationId).toBe('bfe8306c-0910-48fe-8bf5-0db906cf3155') - }) -}) - -describe('Registraion Rates error state tests', () => { - const graphqlMocks = [ - { - request: { - query: IS_LEAF_LEVEL_LOCATION, - variables: { parentId: '6e1f3bce-7bcb-4bf6-8e35-0d9facdf158b' } - }, - result: { - data: { - isLeafLevelLocation: true - } - } - }, - { - request: { - query: FETCH_MONTH_WISE_EVENT_ESTIMATIONS, - variables: { - event: 'BIRTH', - locationId: '6e1f3bce-7bcb-4bf6-8e35-0d9facdf158b', - timeStart: timeStart.toISOString(), - timeEnd: timeEnd.toISOString() - } - }, - result: { - errors: [new GraphQLError('Error!')] - } - } - ] - let component: ReactWrapper<{}, {}> - let store: AppStore - - beforeEach(async () => { - Date.now = vi.fn(() => 1487076708000) - ;({ store } = await createTestStore()) - - const { component: testComponent } = await createTestComponent( - , - { - store, - path: EVENT_COMPLETENESS_RATES, - initialEntries: [ - formatUrl(EVENT_COMPLETENESS_RATES, { - eventType: 'birth' - }) + - '?' + - stringify({ - time: 'withinTarget', - locationId: LOCATION_DHAKA_DIVISION.id, - timeEnd: new Date(1487076708000).toISOString(), - timeStart: new Date(1455454308000).toISOString() - }) - ], - graphqlMocks: graphqlMocks - } - ) - component = testComponent - // wait for mocked data to load mockedProvider - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - - component.update() - }) - - it('renders the error toast notification and component loader', async () => { - await waitForElement(component, '#error-toast') - expect(component.find('#error-toast').hostNodes()).toHaveLength(1) - expect( - component.find('#reg-rates-line-chart-loader').hostNodes() - ).toHaveLength(1) - }) -}) diff --git a/packages/client/src/views/SysAdmin/Performance/CompletenessRates.tsx b/packages/client/src/views/SysAdmin/Performance/CompletenessRates.tsx index c82e466c289..3e949609c29 100644 --- a/packages/client/src/views/SysAdmin/Performance/CompletenessRates.tsx +++ b/packages/client/src/views/SysAdmin/Performance/CompletenessRates.tsx @@ -162,7 +162,9 @@ function Filter({ type === 'ADMIN_STRUCTURE'} + locationFilter={({ locationType }) => + locationType === 'ADMIN_STRUCTURE' + } onChangeLocation={(newLocationId) => { navigate( generateCompletenessRatesUrl({ diff --git a/packages/client/src/views/SysAdmin/Performance/WorkflowStatus.tsx b/packages/client/src/views/SysAdmin/Performance/WorkflowStatus.tsx index 1ff6e02329f..1edd3cbf52a 100644 --- a/packages/client/src/views/SysAdmin/Performance/WorkflowStatus.tsx +++ b/packages/client/src/views/SysAdmin/Performance/WorkflowStatus.tsx @@ -709,17 +709,6 @@ function WorkflowStatusComponent(props: WorkflowStatusProps) { }) ) }} - locationFilter={ - window.config.DECLARATION_AUDIT_LOCATIONS - ? ({ jurisdictionType }) => - Boolean( - jurisdictionType && - window.config.DECLARATION_AUDIT_LOCATIONS.split( - ',' - ).includes(jurisdictionType) - ) - : undefined - } /> { diff --git a/packages/client/src/views/SysAdmin/Team/user/UserList.test.tsx b/packages/client/src/views/SysAdmin/Team/user/UserList.test.tsx index 6c5a050a58e..39cd95b5263 100644 --- a/packages/client/src/views/SysAdmin/Team/user/UserList.test.tsx +++ b/packages/client/src/views/SysAdmin/Team/user/UserList.test.tsx @@ -34,6 +34,7 @@ import { SearchUsersQuery, Status } from '@client/utils/gateway' import { NetworkStatus } from '@apollo/client' import { TEAM_USER_LIST } from '@client/navigation/routes' import { createMemoryRouter } from 'react-router-dom' +import * as useLocationsModule from '@client/v2-events/hooks/useLocations' const searchUserResultsMock = ( officeId: string, @@ -45,7 +46,8 @@ const searchUserResultsMock = ( variables: { primaryOfficeId: officeId, count: 10, - skip: 0 + skip: 0, + status: 'active' } }, result: { @@ -115,8 +117,8 @@ describe('for user with create my jurisdiction scope', () => { }) it('should show add user button if office is under jurisdiction', async () => { - const userOfficeId = 'da672661-eb0a-437b-aa7a-a6d9a1711dd1' - const selectedOfficeId = '0d8474da-0361-4d32-979e-af91f012340a' // This office is under the user's office in hierarchy + const userOfficeId = '62a0ccb4-880d-4f30-8882-f256007dfff9' + const selectedOfficeId = '028d2c85-ca31-426d-b5d1-2cef545a4902' // This office is under the user's office in hierarchy const { component } = await createTestComponent(, { store, @@ -178,7 +180,7 @@ describe('for user with create scope', () => { it('should show add user button if office is under jurisdiction', async () => { const userOfficeId = 'da672661-eb0a-437b-aa7a-a6d9a1711dd1' - const selectedOfficeId = '0d8474da-0361-4d32-979e-af91f012340a' // This office is under the user's office in hierarchy + const selectedOfficeId = '028d2c85-ca31-426d-b5d1-2cef545a4902' // This office is under the user's office in hierarchy const { component } = await createTestComponent(, { store, path: TEAM_USER_LIST, @@ -239,8 +241,8 @@ describe('for user with update my jurisdiction scope', () => { }) it('should show edit user button if office is under jurisdiction', async () => { - const userOfficeId = 'da672661-eb0a-437b-aa7a-a6d9a1711dd1' - const selectedOfficeId = '0d8474da-0361-4d32-979e-af91f012340a' // This office is under the user's office in hierarchy + const userOfficeId = '62a0ccb4-880d-4f30-8882-f256007dfff9' + const selectedOfficeId = '028d2c85-ca31-426d-b5d1-2cef545a4902' // This office is under the user's office in hierarchy const { component } = await createTestComponent(, { store, path: TEAM_USER_LIST, @@ -271,8 +273,8 @@ describe('for user with update my jurisdiction scope', () => { }) it('should not show edit user button if the other user has update all scope even if under jurisdiction', async () => { - const userOfficeId = 'da672661-eb0a-437b-aa7a-a6d9a1711dd1' - const selectedOfficeId = '0d8474da-0361-4d32-979e-af91f012340a' // This office is under the user's office in hierarchy + const userOfficeId = '62a0ccb4-880d-4f30-8882-f256007dfff9' + const selectedOfficeId = '028d2c85-ca31-426d-b5d1-2cef545a4902' // This office is under the user's office in hierarchy const { component } = await createTestComponent(, { store, path: TEAM_USER_LIST, @@ -341,7 +343,7 @@ describe('for user with update scope', () => { it('should show edit user button if office is under jurisdiction', async () => { const userOfficeId = 'da672661-eb0a-437b-aa7a-a6d9a1711dd1' - const selectedOfficeId = '0d8474da-0361-4d32-979e-af91f012340a' // This office is under the user's office in hierarchy + const selectedOfficeId = '028d2c85-ca31-426d-b5d1-2cef545a4902' // This office is under the user's office in hierarchy const { component } = await createTestComponent(, { store, path: TEAM_USER_LIST, @@ -373,7 +375,7 @@ describe('for user with update scope', () => { it('should show edit user button even if the other user has update all scope', async () => { const userOfficeId = 'da672661-eb0a-437b-aa7a-a6d9a1711dd1' - const selectedOfficeId = '0d8474da-0361-4d32-979e-af91f012340a' // This office is under the user's office in hierarchy + const selectedOfficeId = '028d2c85-ca31-426d-b5d1-2cef545a4902' // This office is under the user's office in hierarchy const { component } = await createTestComponent(, { store, path: TEAM_USER_LIST, @@ -506,9 +508,10 @@ describe('User list tests', () => { request: { query: SEARCH_USERS, variables: { - primaryOfficeId: '0d8474da-0361-4d32-979e-af91f012340a', + primaryOfficeId: '028d2c85-ca31-426d-b5d1-2cef545a4902', count: 10, - skip: 0 + skip: 0, + status: 'active' } }, result: { @@ -528,11 +531,21 @@ describe('User list tests', () => { TEAM_USER_LIST + '?' + stringify({ - locationId: '0d8474da-0361-4d32-979e-af91f012340a' + locationId: '028d2c85-ca31-426d-b5d1-2cef545a4902' }) ], graphqlMocks: userListMock }) + + store.dispatch( + actions.setUserDetails({ + loading: false, + data: fetchUserMock('028d2c85-ca31-426d-b5d1-2cef545a4902'), + networkStatus: NetworkStatus.ready + }) + ) + component.update() + component.update() const addUser = await waitForElement(component, '#add-user') addUser.hostNodes().simulate('click') @@ -547,9 +560,10 @@ describe('User list tests', () => { request: { query: SEARCH_USERS, variables: { - primaryOfficeId: '0d8474da-0361-4d32-979e-af91f012340a', + primaryOfficeId: '028d2c85-ca31-426d-b5d1-2cef545a4902', count: 10, - skip: 0 + skip: 0, + status: 'active' } }, result: { @@ -569,7 +583,7 @@ describe('User list tests', () => { TEAM_USER_LIST + '?' + stringify({ - locationId: '0d8474da-0361-4d32-979e-af91f012340a' + locationId: '028d2c85-ca31-426d-b5d1-2cef545a4902' }) ], graphqlMocks: userListMock @@ -592,9 +606,10 @@ describe('User list tests', () => { request: { query: SEARCH_USERS, variables: { - primaryOfficeId: '0d8474da-0361-4d32-979e-af91f012340a', + primaryOfficeId: '028d2c85-ca31-426d-b5d1-2cef545a4902', count: 10, - skip: 0 + skip: 0, + status: 'active' } }, result: { @@ -614,7 +629,7 @@ describe('User list tests', () => { TEAM_USER_LIST + '?' + stringify({ - locationId: '0d8474da-0361-4d32-979e-af91f012340a' + locationId: '028d2c85-ca31-426d-b5d1-2cef545a4902' }) ], graphqlMocks: userListMock @@ -641,9 +656,10 @@ describe('User list tests', () => { request: { query: SEARCH_USERS, variables: { - primaryOfficeId: '0d8474da-0361-4d32-979e-af91f012340a', + primaryOfficeId: '028d2c85-ca31-426d-b5d1-2cef545a4902', count: 10, - skip: 0 + skip: 0, + status: 'active' } }, result: { @@ -661,7 +677,7 @@ describe('User list tests', () => { } ], primaryOffice: { - id: '0d8474da-0361-4d32-979e-af91f012340a' + id: '028d2c85-ca31-426d-b5d1-2cef545a4902' }, role: { id: 'REGISTRATION_AGENT', @@ -684,7 +700,7 @@ describe('User list tests', () => { } ], primaryOffice: { - id: '0d8474da-0361-4d32-979e-af91f012340a' + id: '028d2c85-ca31-426d-b5d1-2cef545a4902' }, role: { id: 'LOCAL_REGISTRAR', @@ -707,7 +723,7 @@ describe('User list tests', () => { } ], primaryOffice: { - id: '0d8474da-0361-4d32-979e-af91f012340a' + id: '028d2c85-ca31-426d-b5d1-2cef545a4902' }, role: { id: 'DISTRICT_REGISTRAR', @@ -730,7 +746,7 @@ describe('User list tests', () => { } ], primaryOffice: { - id: '0d8474da-0361-4d32-979e-af91f012340a' + id: '028d2c85-ca31-426d-b5d1-2cef545a4902' }, role: { id: 'STATE_REGISTRAR', @@ -753,7 +769,7 @@ describe('User list tests', () => { } ], primaryOffice: { - id: '0d8474da-0361-4d32-979e-af91f012340a' + id: '028d2c85-ca31-426d-b5d1-2cef545a4902' }, role: { id: 'FIELD_AGENT', @@ -786,7 +802,7 @@ describe('User list tests', () => { TEAM_USER_LIST + '?' + stringify({ - locationId: '0d8474da-0361-4d32-979e-af91f012340a' + locationId: '028d2c85-ca31-426d-b5d1-2cef545a4902' }) ], graphqlMocks: userListMock diff --git a/packages/client/src/views/SysAdmin/Team/user/UserList.tsx b/packages/client/src/views/SysAdmin/Team/user/UserList.tsx index cbfbd8421c8..810e178f88b 100644 --- a/packages/client/src/views/SysAdmin/Team/user/UserList.tsx +++ b/packages/client/src/views/SysAdmin/Team/user/UserList.tsx @@ -17,7 +17,7 @@ import { import { messages } from '@client/i18n/messages/views/sysAdmin' import { messages as headerMessages } from '@client/i18n/messages/views/header' import { formatUrl } from '@client/navigation' -import { ILocation, IOfflineData } from '@client/offline/reducer' +import { IOfflineData } from '@client/offline/reducer' import { getOfflineData } from '@client/offline/selectors' import { IStoreState } from '@client/store' import styled, { withTheme } from 'styled-components' @@ -25,7 +25,7 @@ import { SEARCH_USERS } from '@client/user/queries' import { LANG_EN } from '@client/utils/constants' import { createNamesMap, getLocalisedName } from '@client/utils/data-formatting' import { SysAdminContentWrapper } from '@client/views/SysAdmin/SysAdminContentWrapper' -import { getAddressName, UserStatus } from '@client/views/SysAdmin/Team/utils' +import { getAddressNameV2, UserStatus } from '@client/views/SysAdmin/Team/utils' import { LinkButton } from '@opencrvs/components/lib/buttons' import { Button } from '@opencrvs/components/lib/Button' import { Pill } from '@opencrvs/components/lib/Pill' @@ -69,6 +69,8 @@ import { usePermissions } from '@client/hooks/useAuthorization' import * as routes from '@client/navigation/routes' import { UserSection } from '@client/forms' import { stringify } from 'querystring' +import { useLocations } from '@client/v2-events/hooks/useLocations' +import { Location, UUID } from '@opencrvs/commons/client' const DEFAULT_FIELD_AGENT_LIST_SIZE = 10 const DEFAULT_PAGE_NUMBER = 1 @@ -166,7 +168,6 @@ type IOnlineStatusProps = { type BaseProps = { theme: ITheme - offlineOffices: ILocation[] userDetails: UserDetails | null offlineCountryConfig: IOfflineData } @@ -213,6 +214,13 @@ function UserListComponent(props: IProps) { const location = useLocation() const navigate = useNavigate() + const { getLocations } = useLocations() + const locations = getLocations.useSuspenseQuery() + + const offlineOffices = [...locations.values()].filter( + ({ locationType }) => locationType === 'CRVS_OFFICE' + ) + const [showResendInviteSuccess, setShowResendInviteSuccess] = useState(false) const [showUsernameReminderSuccess, setShowUsernameReminderSuccess] = useState(false) @@ -225,8 +233,7 @@ function UserListComponent(props: IProps) { const { canReadUser, canEditUser, canAddOfficeUsers, canAccessOffice } = usePermissions() - const { intl, userDetails, offlineOffices, isOnline, offlineCountryConfig } = - props + const { intl, userDetails, isOnline, offlineCountryConfig } = props const { locationId } = parse(location.search, { ignoreQueryPrefix: true @@ -248,19 +255,18 @@ function UserListComponent(props: IProps) { const [currentPageNumber, setCurrentPageNumber] = useState(DEFAULT_PAGE_NUMBER) const recordCount = DEFAULT_FIELD_AGENT_LIST_SIZE * currentPageNumber - const searchedLocation: ILocation | undefined = offlineOffices.find( - ({ id }) => locationId === id - ) + + const parsedId = UUID.safeParse(locationId) + + const searchedLocation: Location | undefined = parsedId.success + ? locations.get(parsedId.data) + : undefined + const deliveryMethod = window.config.USER_NOTIFICATION_DELIVERY_METHOD const isMultipleOfficeUnderJurisdiction = offlineOffices.filter(canAccessOffice).length > 1 - const getParentLocation = ({ partOf }: ILocation) => { - const parentLocationId = partOf.split('/')[1] - return offlineCountryConfig.locations[parentLocationId] - } - const toggleUserActivationModal = useCallback( function toggleUserActivationModal(user?: User) { if (user !== undefined) { @@ -321,7 +327,11 @@ function UserListComponent(props: IProps) { const res = await userMutations.resendInvite(userId, [ { query: SEARCH_USERS, - variables: { primaryOfficeId: locationId, count: recordCount } + variables: { + primaryOfficeId: locationId, + count: recordCount, + status: 'active' + } } ]) if (res && res.data && res.data.resendInvite) { @@ -340,7 +350,11 @@ function UserListComponent(props: IProps) { const res = await userMutations.usernameReminderSend(userId, [ { query: SEARCH_USERS, - variables: { primaryOfficeId: locationId, count: recordCount } + variables: { + primaryOfficeId: locationId, + count: recordCount, + status: 'active' + } } ]) if (res && res.data && res.data.usernameReminder) { @@ -359,7 +373,11 @@ function UserListComponent(props: IProps) { const res = await userMutations.sendResetPasswordInvite(userId, [ { query: SEARCH_USERS, - variables: { primaryOfficeId: locationId, count: recordCount } + variables: { + primaryOfficeId: locationId, + count: recordCount, + status: 'active' + } } ]) if (res && res.data && res.data.resetPasswordInvite) { @@ -601,7 +619,7 @@ function UserListComponent(props: IProps) { setCurrentPageNumber(DEFAULT_PAGE_NUMBER) }} locationFilter={(location) => - location.type === 'CRVS_OFFICE' && canAccessOffice(location) + location.locationType === 'CRVS_OFFICE' && canAccessOffice(location) } /> ) @@ -673,7 +691,8 @@ function UserListComponent(props: IProps) { query: SEARCH_USERS, variables: { primaryOfficeId: locationId, - count: recordCount + count: recordCount, + status: 'active' } } ]} @@ -809,7 +828,8 @@ function UserListComponent(props: IProps) { variables={{ primaryOfficeId: locationId, count: DEFAULT_FIELD_AGENT_LIST_SIZE, - skip: (currentPageNumber - 1) * DEFAULT_FIELD_AGENT_LIST_SIZE + skip: (currentPageNumber - 1) * DEFAULT_FIELD_AGENT_LIST_SIZE, + status: 'active' }} fetchPolicy={'cache-and-network'} > @@ -818,9 +838,7 @@ function UserListComponent(props: IProps) { ) : data ? ( <> - + {searchedLocation && ( - {getAddressName( - offlineCountryConfig, - getParentLocation(searchedLocation) + {getAddressNameV2( + locations, + searchedLocation.parentId + ? locations.get(searchedLocation.parentId) + : undefined )} )} @@ -952,7 +968,6 @@ function UserListComponent(props: IProps) { } export const UserList = connect((state: IStoreState) => ({ - offlineOffices: Object.values(getOfflineData(state).offices), userDetails: getUserDetails(state), offlineCountryConfig: getOfflineData(state) }))(withTheme(injectIntl(withOnlineStatus(UserListComponent)))) diff --git a/packages/client/src/views/SysAdmin/Team/user/userCreation/SignatureForm.test.tsx b/packages/client/src/views/SysAdmin/Team/user/userCreation/SignatureForm.test.tsx index 51f428442e3..d6618b7158e 100644 --- a/packages/client/src/views/SysAdmin/Team/user/userCreation/SignatureForm.test.tsx +++ b/packages/client/src/views/SysAdmin/Team/user/userCreation/SignatureForm.test.tsx @@ -15,7 +15,6 @@ import { flushPromises, getFileFromBase64String, mockOfflineData, - validImageB64String, mockOfflineDataDispatch, mockFetchRoleGraphqlOperation, mockDataWithRegistarRoleSelected, @@ -23,6 +22,7 @@ import { mockUserGraphqlOperation, setScopes } from '@client/tests/util' +import { validImageB64String } from '@client/tests/mock-offline-data' import { waitForElement } from '@client/tests/wait-for-element' import { modifyUserFormData } from '@client/user/userReducer' import { CreateNewUser } from '@client/views/SysAdmin/Team/user/userCreation/CreateNewUser' diff --git a/packages/client/src/views/SysAdmin/Team/user/userCreation/UserReviewForm.tsx b/packages/client/src/views/SysAdmin/Team/user/userCreation/UserReviewForm.tsx index 731ec25cff4..6369ed713df 100644 --- a/packages/client/src/views/SysAdmin/Team/user/userCreation/UserReviewForm.tsx +++ b/packages/client/src/views/SysAdmin/Team/user/userCreation/UserReviewForm.tsx @@ -78,6 +78,9 @@ import { generateUserReviewFormUrl } from '@client/navigation' import { getListOfLocations } from '@client/utils/validate' +import { useLocations } from '@client/v2-events/hooks/useLocations' +import { UUID } from '@opencrvs/commons/client' +import { getOfficesUnderJurisdiction } from '@client/utils/locationUtils' interface IUserReviewFormProps { userId?: string @@ -302,20 +305,28 @@ const UserReviewFormComponent = ({ }: IFullProps & IDispatchProps & IStateProps) => { const navigate = useNavigate() - const { canAccessOffice } = usePermissions() + const { getLocations } = useLocations() + const locations = getLocations.useSuspenseQuery() let title: string | undefined let actionComponent: JSX.Element const locationId = formData['registrationOffice'] as string - const locationDetails = - offlineCountryConfiguration['locations'][locationId] || - offlineCountryConfiguration['facilities'][locationId] || - offlineCountryConfiguration['offices'][locationId] + + const parsedLocationId = UUID.safeParse(locationId) const userRole = userRoles.find(({ id }) => id === formData.role) - const offlineOffices = Object.values(offlineCountryConfiguration['offices']) + + const parsedPrimaryOfficeId = UUID.safeParse(userDetails?.primaryOffice.id) + let jurisdictionId = null + if (parsedPrimaryOfficeId.success) { + jurisdictionId = locations.get(parsedPrimaryOfficeId.data)?.parentId ?? null + } + const isMultipleOfficeUnderJurisdiction = - offlineOffices.filter(canAccessOffice).length > 1 + getOfficesUnderJurisdiction({ + locations, + jurisdictionId + }).length > 1 const handleSubmit = () => { const variables = draftToGqlTransformer( @@ -380,11 +391,11 @@ const UserReviewFormComponent = ({ title={title} goBack={() => navigate(-1)} goHome={() => { - if (locationDetails.id) { + if (parsedLocationId.success && locations.get(parsedLocationId.data)) { navigate({ pathname: routes.TEAM_USER_LIST, search: stringify({ - locationId: locationDetails.id + locationId }) }) } else if (userDetails?.primaryOffice?.id) { diff --git a/packages/client/src/views/SysAdmin/Team/utils.ts b/packages/client/src/views/SysAdmin/Team/utils.ts index 28961448a47..84c74d8164d 100644 --- a/packages/client/src/views/SysAdmin/Team/utils.ts +++ b/packages/client/src/views/SysAdmin/Team/utils.ts @@ -11,6 +11,7 @@ import { messages } from '@client/i18n/messages/views/userSetup' import { ILocation, IOfflineData } from '@client/offline/reducer' import { MessageDescriptor } from 'react-intl' +import { Location, UUID } from '@opencrvs/commons/client' export enum UserStatus { ACTIVE, @@ -70,6 +71,16 @@ export const getAddressName = ( return `${name}, ${getAddressName(offlineCountryConfig, parentLocation)}` } +export const getAddressNameV2 = ( + locations: Map, + location?: Location +): string => { + if (!location) return '' + const { name, parentId } = location + if (!parentId) return name + return `${name}, ${getAddressNameV2(locations, locations.get(parentId))}` +} + export function getUserAuditDescription( status: string ): MessageDescriptor | undefined { diff --git a/packages/commons/src/events/locations.ts b/packages/commons/src/events/locations.ts index ae605ff7ff3..d4a39e61f89 100644 --- a/packages/commons/src/events/locations.ts +++ b/packages/commons/src/events/locations.ts @@ -23,6 +23,7 @@ export type LocationType = z.infer export const Location = z.object({ id: UUID, name: z.string(), + externalId: z.string().nullable(), parentId: UUID.nullable(), validUntil: z.iso.datetime().nullable(), locationType: LocationType.nullable() diff --git a/packages/config/src/config/hearthClient.ts b/packages/config/src/config/hearthClient.ts index 2ef0a9dee27..12767aed2d2 100644 --- a/packages/config/src/config/hearthClient.ts +++ b/packages/config/src/config/hearthClient.ts @@ -42,5 +42,3 @@ export const start = async (): Promise => { export const stop = async (): Promise => { await client.close() } - -export default client diff --git a/packages/config/src/config/routes.ts b/packages/config/src/config/routes.ts index b1b2cc286dc..cde99ad696d 100644 --- a/packages/config/src/config/routes.ts +++ b/packages/config/src/config/routes.ts @@ -15,20 +15,6 @@ import getSystems from '@config/handlers/system/systemHandler' import getForms from '@config/handlers/forms/formsHandler' import getDashboardQueries from '@config/handlers/dashboardQueries/dashboardQueries' import { ServerRoute } from '@hapi/hapi' -import * as Joi from 'joi' -import { resolveChildren } from '@config/handlers/locations/children' -import { - fetchLocationsHandler, - locationQuerySchema, - requestParamsSchema, - createLocationHandler, - updateLocationHandler, - updateSchema, - requestSchema as createLocationReqSchema -} from '@config/handlers/locations/handler' -import { fetchLocationHandler } from '@config/handlers/locations/location' -import { locationHierarchyHandler } from '@config/handlers/locations/hierarchy' -import { SCOPES } from '@opencrvs/commons/authentication' export default function getRoutes(): ServerRoute[] { return [ @@ -94,94 +80,6 @@ export default function getRoutes(): ServerRoute[] { auth: false, description: 'Fetch dashboard queries from country config' } - }, - { - method: 'GET', - path: '/locations', - handler: fetchLocationsHandler, - options: { - tags: ['api'], - auth: false, - description: 'Get all locations', - validate: { - query: locationQuerySchema - } - } - }, - { - method: 'POST', - path: '/locations', - handler: createLocationHandler, - options: { - tags: ['api'], - auth: { - scope: [SCOPES.CONFIG_UPDATE_ALL, SCOPES.USER_DATA_SEEDING] - }, - description: 'Create a location', - validate: { - payload: createLocationReqSchema - } - } - }, - { - method: 'GET', - path: '/locations/{locationId}', - handler: fetchLocationHandler, - options: { - tags: ['api'], - auth: false, - description: 'Get a single location', - validate: { - params: requestParamsSchema - } - } - }, - { - method: 'PUT', - path: '/locations/{locationId}', - handler: updateLocationHandler, - options: { - tags: ['api'], - auth: { - scope: [SCOPES.CONFIG_UPDATE_ALL] - }, - description: 'Update a location or facility', - validate: { - payload: updateSchema, - params: requestParamsSchema - } - } - }, - { - method: 'GET', - path: '/locations/{locationId}/hierarchy', - handler: locationHierarchyHandler, - options: { - tags: ['api'], - auth: false, - description: "Get location's hierarchy", - validate: { - params: Joi.object({ - locationId: Joi.string().uuid() - }) - } - } - }, - { - method: 'GET', - path: '/locations/{locationId}/children', - handler: resolveChildren, - options: { - auth: false, - tags: ['api'], - description: - 'Retrieve all the children (multi-level) of a particular location', - validate: { - params: Joi.object({ - locationId: Joi.string().uuid() - }) - } - } } ] } diff --git a/packages/config/src/handlers/locations/children.test.ts b/packages/config/src/handlers/locations/children.test.ts deleted file mode 100644 index c37eeab9d8c..00000000000 --- a/packages/config/src/handlers/locations/children.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { UUID } from '@opencrvs/commons' -import * as fixtures from '@opencrvs/commons/fixtures' -import { resolveChildren } from './children' -import { fetchFromHearth } from '@config/services/hearth' -import { resolveLocationChildren } from './locationTreeSolver' - -jest.mock('@config/services/hearth', () => ({ - fetchFromHearth: jest.fn() -})) - -jest.mock('./locationTreeSolver', () => ({ - resolveLocationChildren: jest.fn() -})) - -const fetchFromHearthMock = fetchFromHearth as jest.Mock -const resolveLocationChildrenMock = resolveLocationChildren as jest.Mock - -// Cast handler to make it callable -const handler = resolveChildren as unknown as (req: Request) => Promise - -describe('resolveChildren', () => { - describe('given a location of type office', () => { - test('does not fetch location hierarchy', async () => { - const office = fixtures.savedLocation({ - id: 'uuid1' as UUID, - type: { coding: [{ code: 'CRVS_OFFICE' }] } - }) - - fetchFromHearthMock.mockResolvedValue(office) - - const req = { params: { locationId: office.id } } as any - - const res = await handler(req) - - expect(resolveLocationChildrenMock).not.toHaveBeenCalled() - expect(res).toEqual([office]) - }) - }) - - describe('given a location with children', () => { - test('should return location and children', async () => { - const parent = fixtures.savedLocation({ - id: 'uuid1' as UUID, - type: { coding: [{ code: 'ADMIN_STRUCTURE' }] } - }) - - fetchFromHearthMock.mockResolvedValue(parent) - - const child1 = fixtures.savedLocation({ id: 'uuid2' as UUID }) - const child2 = fixtures.savedLocation({ id: 'uuid3' as UUID }) - - resolveLocationChildrenMock.mockResolvedValue([child1, child2]) - - const req = { params: { locationId: parent.id } } as any - - const res = await handler(req) - - expect(res).toEqual([parent, child1, child2]) - }) - }) -}) diff --git a/packages/config/src/handlers/locations/children.ts b/packages/config/src/handlers/locations/children.ts deleted file mode 100644 index fb7eed3b4cc..00000000000 --- a/packages/config/src/handlers/locations/children.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { UUID } from '@opencrvs/commons' - -import { ServerRoute } from '@hapi/hapi' -import { fetchFromHearth } from '@config/services/hearth' -import { SavedLocation, isOffice } from '@opencrvs/commons/types' -import { resolveLocationChildren } from './locationTreeSolver' - -export const resolveChildren: ServerRoute['handler'] = async (req) => { - const { locationId } = req.params as { locationId: UUID } - const { type } = req.query || { type: undefined } - - const location = await fetchFromHearth( - `Location/${locationId}` - ) - if (isOffice(location)) { - return [location] - } - - const children = await resolveLocationChildren(locationId, type) - - return [location, ...children] -} diff --git a/packages/config/src/handlers/locations/handler.ts b/packages/config/src/handlers/locations/handler.ts deleted file mode 100644 index cc6a0d830d3..00000000000 --- a/packages/config/src/handlers/locations/handler.ts +++ /dev/null @@ -1,367 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import * as Hapi from '@hapi/hapi' -import { badRequest, conflict } from '@hapi/boom' -import * as Joi from 'joi' -import { - composeFhirLocation, - generateStatisticalExtensions, - getLocationsByIdentifier, - updateStatisticalExtensions -} from './utils' -import { v4 as uuid } from 'uuid' -import { - Bundle, - BundleEntry, - Saved, - URLReference, - Location as FhirLocation -} from '@opencrvs/commons/types' -import { fetchFromHearth, sendToFhir } from '@config/services/hearth' -import { UUID } from '@opencrvs/commons' - -enum Code { - CRVS_OFFICE = 'CRVS_OFFICE', - ADMIN_STRUCTURE = 'ADMIN_STRUCTURE', - HEALTH_FACILITY = 'HEALTH_FACILITY' -} - -export enum JurisdictionType { - STATE = 'STATE', - DISTRICT = 'DISTRICT', - LOCATION_LEVEL_1 = 'LOCATION_LEVEL_1', - LOCATION_LEVEL_2 = 'LOCATION_LEVEL_2', - LOCATION_LEVEL_3 = 'LOCATION_LEVEL_3', - LOCATION_LEVEL_4 = 'LOCATION_LEVEL_4', - LOCATION_LEVEL_5 = 'LOCATION_LEVEL_5' -} - -enum Status { - ACTIVE = 'active', - INACTIVE = 'inactive' -} - -export enum ExtensionUrl { - MALE_POPULATION = 'http://opencrvs.org/specs/id/statistics-male-populations', - FEMALE_POPULATION = 'http://opencrvs.org/specs/id/statistics-female-populations', - TOTAL_POPULATION = 'http://opencrvs.org/specs/id/statistics-total-populations', - CRUDE_BIRTH_RATE = 'http://opencrvs.org/specs/id/statistics-crude-birth-rates' -} - -export type Statistics = Array> - -export type LocationStatistic = { - year: number - male_population: number - female_population: number - population: number - crude_birth_rate: number -} - -export type Location = { - statisticalID: string - name: string - partOf: string - code: string - alias?: string - jurisdictionType: string - statistics?: LocationStatistic[] -} - -export type Facility = { - statisticalID: string - name: string - partOf: string - code: string - alias?: string - jurisdictionType?: string -} - -type UpdateLocation = { - name?: string - alias?: string - status?: string - statistics?: LocationStatistic -} - -const locationStatisticSchema = Joi.object({ - year: Joi.number().required(), - male_population: Joi.number().required(), - female_population: Joi.number().required(), - population: Joi.number().required(), - crude_birth_rate: Joi.number().required() -}).label('YearStatistics') - -function instanceOfJurisdiction(object: any): object is Location { - return 'statistics' in object -} - -const locationRequestSchema = Joi.object({ - statisticalID: Joi.string().required(), - name: Joi.string().required(), - alias: Joi.string().optional(), - partOf: Joi.string().required(), - code: Joi.string() - .valid(Code.ADMIN_STRUCTURE, Code.CRVS_OFFICE, Code.HEALTH_FACILITY) - .required(), - jurisdictionType: Joi.string() - .valid( - JurisdictionType.DISTRICT, - JurisdictionType.STATE, - JurisdictionType.LOCATION_LEVEL_1, - JurisdictionType.LOCATION_LEVEL_2, - JurisdictionType.LOCATION_LEVEL_3, - JurisdictionType.LOCATION_LEVEL_4, - JurisdictionType.LOCATION_LEVEL_5 - ) - .optional(), - statistics: Joi.array() - .items(locationStatisticSchema) - .optional() - .label('Statistics') -}).label('LocationPayload') - -export const requestSchema = Joi.alternatives().try( - locationRequestSchema, - Joi.array().items(locationRequestSchema) -) - -export const locationQuerySchema = Joi.object({ - type: Joi.string().valid( - Code.ADMIN_STRUCTURE, - Code.CRVS_OFFICE, - Code.HEALTH_FACILITY - ), - identifier: Joi.string().regex(/^[a-zA-Z0-9_]+$/), - name: Joi.string().regex(/^[a-zA-Z0-9_,.\s]+$/), - status: Joi.string().valid(Status.ACTIVE, Status.INACTIVE), - _count: Joi.number() -}).or('type', 'identifier') - -export const updateSchema = Joi.object({ - name: Joi.string().optional(), - alias: Joi.string().optional(), - status: Joi.string().valid(Status.ACTIVE, Status.INACTIVE).optional(), - statistics: locationStatisticSchema.optional() -}).label('UpdateLocationPayload') - -export const requestParamsSchema = Joi.object({ - locationId: Joi.string().uuid() -}) - -const LOCATION_CHUNK_SIZE = 400 - -export async function fetchLocationsHandler( - request: Hapi.Request, - h: Hapi.ResponseToolkit -) { - const searchParam = request.url.search - const response = await fetchFromHearth>( - `Location${searchParam}` - ) - - response.link = response.link?.map((link) => ({ - ...link, - url: link.url - .replace(link.url.split('/Location')[0], `${request.url.origin}`) - .replace('Location', 'location') - })) - - response.entry = response.entry?.map((entry) => ({ - ...entry, - fullUrl: entry.fullUrl - ?.replace(entry.fullUrl.split('/Location')[0], `${request.url.origin}`) - .replace('Location', 'location') as URLReference - })) - - return response -} - -function createChunks(array: T[], limit: number): T[][] { - const result = [] - for (let i = 0; i < array.length; i += limit) { - result.push(array.slice(i, i + limit)) - } - return result -} - -function createLocationSegments(locations: Location[]): Location[][] { - const segments = [] - for (const jurisdictionType of Object.keys(JurisdictionType)) { - const jurisdictionLocations = locations.filter( - (loc) => loc.jurisdictionType === jurisdictionType - ) - if (jurisdictionLocations.length) { - segments.push(...createChunks(jurisdictionLocations, LOCATION_CHUNK_SIZE)) - } - } - const facilitiesOrOffices = locations.filter((loc) => !loc.jurisdictionType) - if (facilitiesOrOffices.length) { - segments.push(...createChunks(facilitiesOrOffices, LOCATION_CHUNK_SIZE)) - } - return segments -} - -async function batchLocationsHandler( - locations: Location[] -): Promise { - let statisticalToFhirIDMapOfParentLocations: Map = new Map() - const locationSegments = createLocationSegments(locations) - const cumulativeResponse = { - resourceType: 'Bundle', - type: 'document', - entry: [] as fhir3.BundleEntry[] - } satisfies fhir3.Bundle - for (const each of locationSegments) { - const locationsBundle = { - resourceType: 'Bundle', - type: 'document', - entry: each - .map((location) => ({ - ...location, - // partOf is either Location/{fhirID} of another location or 'Location/0' - partOf: - statisticalToFhirIDMapOfParentLocations?.get( - location.partOf.split('/')[1] - ) ?? location.partOf - })) - .map( - (location): BundleEntry => ({ - fullUrl: `urn:uuid:${uuid() as UUID}`, - resource: { - ...composeFhirLocation(location), - ...(location.statistics && { - extension: generateStatisticalExtensions(location.statistics) - }) - } - }) - ) - } - const res = await fetchFromHearth( - '', - 'POST', - JSON.stringify(locationsBundle) - ) - statisticalToFhirIDMapOfParentLocations = new Map( - Array.from(statisticalToFhirIDMapOfParentLocations.entries()).concat( - each.map((loc, i) => [ - loc.statisticalID, - 'Location/' + res?.entry?.[i]?.response?.location?.split('/')?.[3] - ]) - ) - ) - cumulativeResponse.entry = cumulativeResponse.entry.concat(res.entry) - } - return cumulativeResponse -} - -export async function createLocationHandler( - request: Hapi.Request, - h: Hapi.ResponseToolkit -) { - if (Array.isArray(request.payload)) { - return batchLocationsHandler(request.payload as Location[]) - } - const payload = request.payload as Location | Facility - const newLocation = composeFhirLocation(payload) - const partOfLocation = payload.partOf.split('/')[1] - - const locations = [ - ...(await Promise.all([ - getLocationsByIdentifier( - `${Code.ADMIN_STRUCTURE}_${String(payload.statisticalID)}` - ), - getLocationsByIdentifier( - `${Code.CRVS_OFFICE}_${String(payload.statisticalID)}` - ), - getLocationsByIdentifier( - `${Code.HEALTH_FACILITY}_${String(payload.statisticalID)}` - ) - ]).then((results) => results.flat())) - ] - - if (locations.length !== 0) { - throw conflict(`statisticalID ${payload.statisticalID} already exists`) - } - - if (partOfLocation !== '0' && Boolean(partOfLocation)) { - const response = await fetchFromHearth(`Location?_id=${partOfLocation}`) - - if (response.total === 0) { - throw badRequest( - `${partOfLocation} is not a valid location for partOfLocation` - ) - } - } - - if (instanceOfJurisdiction(payload) && payload.statistics) { - const statisticalExtensions = generateStatisticalExtensions( - payload.statistics - ) - newLocation.extension = statisticalExtensions - } - - const response = await sendToFhir( - JSON.stringify(newLocation), - '/Location', - 'POST', - request.headers.authorization - ).catch((err) => { - throw Error('Cannot create location to FHIR') - }) - - return h.response({ - id: response.headers.get('location')?.split('/')[3], - status: response.statusText - }) -} - -export async function updateLocationHandler( - request: Hapi.Request, - h: Hapi.ResponseToolkit -) { - const locationId = request.params.locationId - const location = request.payload as UpdateLocation - const existingLocation = await fetchFromHearth(`Location?_id=${locationId}`) - const newLocation = existingLocation.entry[0].resource - - if (existingLocation.total === 0) { - throw badRequest(`${locationId} is not a valid location`) - } - - if (location.name) { - newLocation.name = location.name - } - if (location.alias) { - newLocation.alias = [location.alias] - } - if (location.status) { - newLocation.status = location.status - } - if (location.statistics) { - const statisticalExtensions = updateStatisticalExtensions( - location.statistics, - newLocation.extension - ) - newLocation.extension = statisticalExtensions - } - - const response = await sendToFhir( - JSON.stringify(newLocation), - `/Location/${locationId}`, - 'PUT', - request.headers.authorization - ).catch((err) => { - throw Error('Cannot update location to FHIR') - }) - - return h.response(response.statusText) -} diff --git a/packages/config/src/handlers/locations/hierarchy.test.ts b/packages/config/src/handlers/locations/hierarchy.test.ts deleted file mode 100644 index 081cf501bcf..00000000000 --- a/packages/config/src/handlers/locations/hierarchy.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { resolveLocationParents } from './locationTreeSolver' -import * as fixtures from '@opencrvs/commons/fixtures' -import { UUID } from '@opencrvs/commons' -import { fetchFromHearth } from '@config/services/hearth' -import { SavedLocation } from '@opencrvs/commons/types' - -jest.mock('@config/services/hearth', () => ({ - fetchFromHearth: jest.fn() -})) - -const fetchFromHearthMock = fetchFromHearth as jest.Mock - -const mockHearthLocations = (locations: SavedLocation[]) => { - fetchFromHearthMock.mockImplementation((id: string) => { - const location = locations.find((l) => `Location/${l.id}` === id) - return Promise.resolve(location) - }) -} - -describe('resolveLocationParents', () => { - it('should return child location only if the location has no parent', async () => { - const location = fixtures.savedLocation({ - id: 'uuid1' as UUID, - partOf: undefined - }) - - mockHearthLocations([location]) - - const result = await resolveLocationParents(location.id) - expect(result).toEqual([location]) - }) - - it('should return child location only if the location parent is Location/0', async () => { - const topLevel = '0' as UUID - const location = fixtures.savedLocation({ - id: 'uuid1' as UUID, - partOf: { - reference: `Location/${topLevel}` - } - }) - - mockHearthLocations([location]) - - const result = await resolveLocationParents(location.id) - expect(result).toEqual([location]) - }) - - it('should resolve a single level parent correctly', async () => { - const parent = fixtures.savedLocation({ - id: 'uuid1' as UUID, - partOf: undefined - }) - - const child = fixtures.savedLocation({ - id: 'uuid2' as UUID, - partOf: { reference: `Location/${parent.id}` } - }) - - mockHearthLocations([child, parent]) - - const result = await resolveLocationParents(child.id) - expect(result).toEqual([parent, child]) - }) - - it('should resolve multiple levels of parents correctly', async () => { - const grandparent = fixtures.savedLocation({ - id: 'uuid1' as UUID, - partOf: undefined - }) - const parent = fixtures.savedLocation({ - id: 'uuid2' as UUID, - partOf: { reference: `Location/${grandparent.id}` } - }) - const child = fixtures.savedLocation({ - id: 'uuid3' as UUID, - partOf: { reference: `Location/${parent.id}` } - }) - const locations = [grandparent, parent, child] - - mockHearthLocations(locations) - - const result = await resolveLocationParents(child.id) - expect(result).toEqual(locations) - }) -}) diff --git a/packages/config/src/handlers/locations/location.ts b/packages/config/src/handlers/locations/location.ts deleted file mode 100644 index aa2b055f8d3..00000000000 --- a/packages/config/src/handlers/locations/location.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { SavedLocation } from '@opencrvs/commons/types' -import { fetchFromHearth } from '@config/services/hearth' -import * as Hapi from '@hapi/hapi' - -export async function fetchLocationHandler( - request: Hapi.Request, - h: Hapi.ResponseToolkit -) { - const locationId = request.params.locationId - const response = await fetchFromHearth( - `Location/${locationId}` - ) - - return response -} diff --git a/packages/config/src/handlers/locations/locationTreeSolver.test.ts b/packages/config/src/handlers/locations/locationTreeSolver.test.ts deleted file mode 100644 index 2cf78fcfb60..00000000000 --- a/packages/config/src/handlers/locations/locationTreeSolver.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { MongoMemoryServer } from 'mongodb-memory-server' - -import { UUID } from '@opencrvs/commons' -import * as fixtures from '@opencrvs/commons/fixtures' -import { Location, SavedLocation } from '@opencrvs/commons/types' -import { Collection, MongoClient } from 'mongodb' - -let client: MongoClient -let mongoServer: MongoMemoryServer -let collection: Collection -let OLD_ENV: NodeJS.ProcessEnv - -const parent = fixtures.savedLocation({ - id: 'uuid1' as UUID, - partOf: undefined -}) -const child = fixtures.savedLocation({ - id: 'uuid2' as UUID, - partOf: { reference: parent.id as `Location/${UUID}` } -}) -const grandchild = fixtures.savedLocation({ - id: 'uuid3' as UUID, - partOf: { reference: child.id as `Location/${UUID}` } -}) - -const lateLoadModule = async () => { - /* eslint-disable @typescript-eslint/no-require-imports */ - /* eslint-disable @typescript-eslint/no-var-requires */ - const { resolveLocationChildren } = require('./locationTreeSolver') - return resolveLocationChildren -} - -describe('resolveChildren', () => { - beforeAll(async () => { - mongoServer = await MongoMemoryServer.create({ - binary: { checkMD5: false } - }) - const uri = mongoServer.getUri() - OLD_ENV = process.env - process.env.HEARTH_MONGO_URL = mongoServer.getUri() - - client = new MongoClient(uri) - const connectedClient = await client.connect() - - const db = connectedClient.db() - collection = db.collection('Location_view_with_plain_ids') - - jest.doMock('@config/config/hearthClient', () => ({ - __esModule: true, - default: client - })) - }, 60000 /* Timeout to allow mongo binary download*/) - - afterAll(async () => { - process.env = OLD_ENV - if (client) { - await client.close() - } - if (mongoServer) { - await mongoServer.stop() - } - }) - - beforeEach(async () => { - await collection.deleteMany({}) - }) - - describe('given a location with no children', () => { - test('should return empty array', async () => { - // late import to allow env vars to be set before the module is loaded - const resolveLocationChildren = await lateLoadModule() - - await collection.insertMany([grandchild]) - - const children = (await resolveLocationChildren( - 'uuid1' as UUID - )) as SavedLocation[] - - expect(children).toHaveLength(0) - }) - }) - - describe('given a location with children', () => { - test('should return all descendants', async () => { - // late import to allow env vars to be set before the module is loaded - const resolveLocationChildren = await lateLoadModule() - - await collection.insertMany([parent, child, grandchild]) - - const children = (await resolveLocationChildren( - 'uuid1' as UUID - )) as SavedLocation[] - - const projectedChild = { - id: child.id, - name: child.name, - type: child.type - } - const projectedGrandchild = { - id: grandchild.id, - name: grandchild.name, - type: grandchild.type - } - - expect(children).toHaveLength(2) - expect(children).toEqual( - expect.arrayContaining([projectedChild, projectedGrandchild]) - ) - }) - }) -}) diff --git a/packages/config/src/handlers/locations/locationTreeSolver.ts b/packages/config/src/handlers/locations/locationTreeSolver.ts deleted file mode 100644 index c46751e1e5d..00000000000 --- a/packages/config/src/handlers/locations/locationTreeSolver.ts +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ - -import { - Location, - resourceIdentifierToUUID, - SavedLocation -} from '@opencrvs/commons/types' -import { logger, UUID } from '@opencrvs/commons' -import { fetchFromHearth } from '@config/services/hearth' -import client from '@config/config/hearthClient' - -export const resolveLocationChildren = async ( - id: UUID, - type: string | undefined -) => { - const db = client.db() - - const childQuery = [ - { - $match: { id: id } - }, - { - $graphLookup: { - from: 'Location_view_with_plain_ids', - startWith: '$id', - connectFromField: 'id', - connectToField: 'partOf.reference', - as: 'children' - } - }, - { - $set: { - children: { - $cond: { - if: { $gt: [type, undefined] }, - then: { - $filter: { - input: '$children', - as: 'child', - cond: { - $eq: [{ $arrayElemAt: ['$$child.type.coding.code', 0] }, type] - } - } - }, - else: '$children' - } - } - } - }, - { - $project: { - children: { - id: 1, - name: 1, - type: 1 - } - } - } - ] - - try { - const result = await db - .collection('Location_view_with_plain_ids') - .aggregate(childQuery) - .toArray() - - return result.length ? result[0].children : [] - } catch (error) { - logger.error(error) - throw error - } -} - -/** Resolves any given location's parents multi-level up to the root node */ -export const resolveLocationParents = async ( - locationId: UUID -): Promise => { - const current = await fetchFromHearth(`Location/${locationId}`) - - if (!current) { - return [] - } - - const id = current.partOf?.reference - ? resourceIdentifierToUUID(current.partOf.reference) - : null - - // Handle case where top level location is Location/0 - if (!id || id === '0') { - return [current] - } - - const parents = await resolveLocationParents(id) - - return [...parents, current] -} diff --git a/packages/config/src/handlers/locations/utils.ts b/packages/config/src/handlers/locations/utils.ts deleted file mode 100644 index 33b7a9aecb9..00000000000 --- a/packages/config/src/handlers/locations/utils.ts +++ /dev/null @@ -1,259 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ - -import { - ExtensionUrl, - Facility as FacilityInput, - Location as LocationInput, - JurisdictionType, - LocationStatistic, - Statistics -} from './handler' -import { - Location, - Extension, - Bundle, - ResourceIdentifier, - OPENCRVS_SPECIFICATION_URL -} from '@opencrvs/commons/types' -import { fetchFromHearth } from '@config/services/hearth' - -export const composeFhirLocation = ( - location: LocationInput | FacilityInput -): Location => { - if (location.code === 'ADMIN_STRUCTURE') { - return { - resourceType: 'Location', - identifier: [ - { - system: `${OPENCRVS_SPECIFICATION_URL}id/statistical-code`, - value: `ADMIN_STRUCTURE_${String(location.statisticalID)}` - }, - { - system: `${OPENCRVS_SPECIFICATION_URL}id/jurisdiction-type`, - value: - location.jurisdictionType === JurisdictionType.LOCATION_LEVEL_1 - ? JurisdictionType.STATE - : location.jurisdictionType === JurisdictionType.LOCATION_LEVEL_2 - ? JurisdictionType.DISTRICT - : location.jurisdictionType - } - ], - name: location.name, - alias: location.alias ? [location.alias] : [], - description: location.statisticalID, - status: 'active', - mode: 'instance', - partOf: { - reference: location.partOf as ResourceIdentifier - }, - type: { - coding: [ - { - system: `${OPENCRVS_SPECIFICATION_URL}location-type`, - code: 'ADMIN_STRUCTURE' - } - ] - }, - physicalType: { - coding: [ - { - code: 'jdn', - display: 'Jurisdiction' - } - ] - } - } - } else { - return { - resourceType: 'Location', - identifier: [ - { - system: `${OPENCRVS_SPECIFICATION_URL}id/internal-id`, - value: `${location.code}_${String(location.statisticalID)}` - } - ], - name: location.name, - alias: location.alias ? [location.alias] : [], - status: 'active', - mode: 'instance', - partOf: { - reference: location.partOf as ResourceIdentifier - }, - type: { - coding: [ - { - system: `${OPENCRVS_SPECIFICATION_URL}location-type`, - code: location.code - } - ] - }, - physicalType: { - coding: [ - { - code: 'bu', - display: 'Building' - } - ] - } - } - } -} - -function setExtensions( - malePopulations: Statistics, - femalePopulations: Statistics, - totalPopulations: Statistics, - birthRates: Statistics -) { - const extensions: Extension[] = [ - { - url: 'http://hl7.org/fhir/StructureDefinition/location-boundary-geojson', - valueAttachment: { - contentType: 'application/geo+json', - data: '' // base64 encoded geoJSON feature object - } - }, - { - url: 'http://opencrvs.org/specs/id/statistics-male-populations', - valueString: JSON.stringify(malePopulations) - }, - { - url: 'http://opencrvs.org/specs/id/statistics-female-populations', - valueString: JSON.stringify(femalePopulations) - }, - { - url: 'http://opencrvs.org/specs/id/statistics-total-populations', - valueString: JSON.stringify(totalPopulations) - }, - { - url: 'http://opencrvs.org/specs/id/statistics-crude-birth-rates', - valueString: JSON.stringify(birthRates) - } - ] - return extensions -} - -export function generateStatisticalExtensions( - sourceStatistic: LocationStatistic[] -) { - const malePopulations: Statistics = [] - const femalePopulations: Statistics = [] - const totalPopulations: Statistics = [] - const birthRates: Statistics = [] - - for (const data of sourceStatistic) { - // Add statistics without validation - femalePopulations.push({ - [data.year]: data.female_population - }) - malePopulations.push({ - [data.year]: data.male_population - }) - totalPopulations.push({ - [data.year]: data.population - }) - birthRates.push({ - [data.year]: data.crude_birth_rate - }) - } - - return setExtensions( - malePopulations, - femalePopulations, - totalPopulations, - birthRates - ) -} - -export function updateStatisticalExtensions( - sourceStatistic: LocationStatistic, - extension: Extension[] -) { - let malePopulations: Statistics = [] - let femalePopulations: Statistics = [] - let totalPopulations: Statistics = [] - let birthRates: Statistics = [] - - for (const data of extension) { - if (data.url === ExtensionUrl.MALE_POPULATION) { - malePopulations = JSON.parse(data.valueString!) - const previousData = malePopulations.find((year) => - Boolean(year[sourceStatistic.year]) - ) - if (previousData) { - previousData[sourceStatistic.year] = sourceStatistic.male_population - } else { - malePopulations.push({ - [sourceStatistic.year]: sourceStatistic.male_population - }) - } - } else if (data.url === ExtensionUrl.FEMALE_POPULATION) { - femalePopulations = JSON.parse(data.valueString!) - const previousData = femalePopulations.find((year) => - Boolean(year[sourceStatistic.year]) - ) - if (previousData) { - previousData[sourceStatistic.year] = sourceStatistic.female_population - } else { - femalePopulations.push({ - [sourceStatistic.year]: sourceStatistic.female_population - }) - } - } else if (data.url === ExtensionUrl.TOTAL_POPULATION) { - totalPopulations = JSON.parse(data.valueString!) - const previousData = totalPopulations.find((year) => - Boolean(year[sourceStatistic.year]) - ) - if (previousData) { - previousData[sourceStatistic.year] = sourceStatistic.population - } else { - totalPopulations.push({ - [sourceStatistic.year]: sourceStatistic.population - }) - } - } else if (data.url === ExtensionUrl.CRUDE_BIRTH_RATE) { - birthRates = JSON.parse(data.valueString!) - const previousData = birthRates.find((year) => - Boolean(year[sourceStatistic.year]) - ) - if (previousData) { - previousData[sourceStatistic.year] = sourceStatistic.crude_birth_rate - } else { - birthRates.push({ - [sourceStatistic.year]: sourceStatistic.crude_birth_rate - }) - } - } - } - - return setExtensions( - malePopulations, - femalePopulations, - totalPopulations, - birthRates - ) -} - -export async function getLocationsByIdentifier(identifier: string) { - const locationSearchResult = await fetchFromHearth>( - `Location/?identifier=${identifier}&_count=0` - ) - - return ( - (locationSearchResult && - locationSearchResult.entry && - locationSearchResult.entry.map( - (locationEntry) => locationEntry.resource as Location - )) || - [] - ) -} diff --git a/packages/data-seeder/package.json b/packages/data-seeder/package.json index 65312b75c3a..8f1d7bb039e 100644 --- a/packages/data-seeder/package.json +++ b/packages/data-seeder/package.json @@ -17,6 +17,7 @@ "prettier": "3.5.3" }, "dependencies": { + "@opencrvs/toolkit": "1.9.0-rc.2ae48a1", "@opencrvs/commons": "^1.3.0", "@types/fhir": "^0.0.37", "@types/node": "^16.18.39", diff --git a/packages/data-seeder/src/locations.ts b/packages/data-seeder/src/locations.ts index 93f3d0e4738..b0eb2973744 100644 --- a/packages/data-seeder/src/locations.ts +++ b/packages/data-seeder/src/locations.ts @@ -9,11 +9,12 @@ * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ import fetch from 'node-fetch' -import { OPENCRVS_SPECIFICATION_URL } from './constants' import { env } from './environment' import { TypeOf, z } from 'zod' import { raise } from './utils' import { fromZodError } from 'zod-validation-error' +import { getUUID } from '@opencrvs/commons' +import { createClient } from '@opencrvs/toolkit/api' const LOCATION_TYPES = [ 'ADMIN_STRUCTURE', @@ -145,94 +146,25 @@ async function getLocations() { ) } }) - return parsedLocations.data + const locations = parsedLocations.data + + const locationIdMap = new Map(locations.map(({ id }) => [id, getUUID()])) + + return locations.map((loc) => ({ + id: locationIdMap.get(loc.id)!, + name: loc.name, + parentId: locationIdMap.get(loc.partOf.split('/')[1]) || null, + locationType: loc.locationType, + externalId: loc.id, + validUntil: null + })) } -const bundleToLocationEntries = (bundle: fhir3.Bundle) => - (bundle.entry ?? []) - .map((bundleEntry) => bundleEntry.resource) - .filter((maybeLocation): maybeLocation is fhir3.Location => - Boolean(maybeLocation) - ) - -function locationBundleToIdentifier( - bundle: fhir3.Bundle -): string[] { - return bundleToLocationEntries(bundle) - .map((location) => getExternalIdFromIdentifier(location.identifier)) - .filter((maybeId): maybeId is string => Boolean(maybeId)) -} - -/** - * Get the externally defined id for location. Defined in country-config. - */ - -const getExternalIdFromIdentifier = ( - identifiers: fhir3.Location['identifier'] -) => - identifiers - ?.find(({ system }) => - [ - `${OPENCRVS_SPECIFICATION_URL}id/statistical-code`, - `${OPENCRVS_SPECIFICATION_URL}id/internal-id` - ].some((identifierSystem) => identifierSystem === system) - ) - ?.value?.split('_') - .pop() - export async function seedLocations(token: string) { - const savedLocations = ( - await Promise.all( - LOCATION_TYPES.map((type) => - getLocationsByType(type) - .then((res) => res.json()) - .then((bundle: fhir3.Bundle) => - locationBundleToIdentifier(bundle) - ) - ) - ) - ).flat() - - const savedLocationsSet = new Set(savedLocations) - const locations = (await getLocations()).filter((location) => { - return !savedLocationsSet.has(location.id) - }) - const res = await fetch(`${env.GATEWAY_HOST}/locations?`, { - method: 'POST', - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/fhir+json' - }, - body: JSON.stringify( - locations - // statisticalID & code are legacy properties - .map(({ id, locationType, ...loc }) => ({ - statisticalID: id, - code: locationType, - ...loc - })) - ) - }) - - if (!res.ok) { - raise(await res.text()) - } + const locations = await getLocations() - const response: fhir3.Bundle = await res.json() - response.entry?.forEach((res, index) => { - if (res.response?.status !== '201') { - // eslint-disable-next-line no-console - console.log( - `Failed to create location resource for: "${locations[index].name}"` - ) - } - }) -} + const url = new URL('events', env.GATEWAY_HOST).toString() + const client = createClient(url, `Bearer ${token}`) -function getLocationsByType(type: string) { - return fetch(`${env.GATEWAY_HOST}/locations?type=${type}&_count=0`, { - headers: { - 'Content-Type': 'application/fhir+json' - } - }) + return await client.locations.set.mutate(locations) } diff --git a/packages/data-seeder/src/users.ts b/packages/data-seeder/src/users.ts index 8ea84458e56..6da5b52db2a 100644 --- a/packages/data-seeder/src/users.ts +++ b/packages/data-seeder/src/users.ts @@ -20,6 +20,7 @@ import { parseConfigurableScope } from '@opencrvs/commons/authentication' import { fromZodError } from 'zod-validation-error' +import { createClient } from '@opencrvs/toolkit/api' const MAX_RETRY = 5 const RETRY_DELAY_IN_MILLISECONDS = 5000 @@ -213,28 +214,6 @@ async function userAlreadyExists( return Boolean(parsedSearchResponse.searchUsers.totalItems) } -async function getOfficeIdFromIdentifier(identifier: string) { - const response = await fetch( - `${env.GATEWAY_HOST}/location?identifier=${identifier}`, - { - headers: { - 'Content-Type': 'application/fhir+json' - } - } - ) - if (!response.ok) { - // eslint-disable-next-line no-console - console.error( - `Error fetching location with identifier ${identifier}`, - response.statusText - ) - throw new Error('Error fetching location') - } - const locationBundle: fhir3.Bundle = await response.json() - - return locationBundle.entry?.[0]?.resource?.id -} - async function callCreateUserMutation(token: string, userPayload: unknown) { return fetch(`${env.GATEWAY_HOST}/graphql`, { method: 'POST', @@ -271,14 +250,13 @@ export async function seedUsers(token: string) { continue } - const primaryOffice = await getOfficeIdFromIdentifier(officeIdentifier) - if (!primaryOffice) { - // eslint-disable-next-line no-console - console.log( - `No office found with id ${officeIdentifier}. Skipping user "${username}"` - ) - continue - } + const externalId = officeIdentifier.split('_').at(-1) + + const url = new URL('events', env.GATEWAY_HOST).toString() + const client = createClient(url, `Bearer ${token}`) + const [primaryOffice] = await client.locations.list.query({ + externalId + }) const userPayload = { ...user, @@ -290,7 +268,7 @@ export async function seedUsers(token: string) { } ], ...(env.ACTIVATE_USERS && { status: 'active' }), - primaryOffice, + primaryOffice: primaryOffice.id, username } let tryNumber = 0 diff --git a/packages/events/package.json b/packages/events/package.json index 79a794ef4fb..279ac0829a3 100644 --- a/packages/events/package.json +++ b/packages/events/package.json @@ -15,7 +15,7 @@ "build": "tsc -p tsconfig.build.json && tsc -p tsconfig.router.json", "build:clean": "rm -rf build", "generate-db-types": "kanel && prettier src/storage/postgres/events/schema --write", - "generate-db-schema": "docker run --rm postgres:17.6 pg_dump postgres://events_migrator:migrator_password@host.docker.internal:5432/events -s > src/tests/postgres-migrations.sql --exclude-schema=analytics" + "generate-db-schema": "docker run --rm postgres:17.6 pg_dump postgres://events_migrator:migrator_password@$( [[ '$OSTYPE' == darwin* ]] && echo host.docker.internal || echo 172.17.0.1 ):5432/events -s > src/tests/postgres-migrations.sql --exclude-schema=analytics" }, "dependencies": { "@elastic/elasticsearch": "8.16.2", diff --git a/packages/events/src/router/event/event.search.scopes.test.ts b/packages/events/src/router/event/event.search.scopes.test.ts index 36ba3487c9a..3bcf1e0abad 100644 --- a/packages/events/src/router/event/event.search.scopes.test.ts +++ b/packages/events/src/router/event/event.search.scopes.test.ts @@ -36,6 +36,7 @@ function generateOfficeLocations(adminAreas: Location[], rng: () => number) { locationType: LocationType.enum.CRVS_OFFICE, parentId: admin.id, id: generateUuid(rng), + externalId: generateUuid(rng), validUntil: null } satisfies Location @@ -43,6 +44,7 @@ function generateOfficeLocations(adminAreas: Location[], rng: () => number) { name: `${admin.name} Health Facility`, locationType: LocationType.enum.HEALTH_FACILITY, parentId: admin.id, + externalId: generateUuid(rng), id: generateUuid(rng), validUntil: null } satisfies Location @@ -66,6 +68,7 @@ async function setupHierarchyWithUsers() { locationType: LocationType.enum.ADMIN_STRUCTURE, parentId: null, id: generateUuid(rng), + externalId: generateUuid(rng), validUntil: null } satisfies Location @@ -74,6 +77,7 @@ async function setupHierarchyWithUsers() { locationType: LocationType.enum.ADMIN_STRUCTURE, parentId: null, id: generateUuid(rng), + externalId: generateUuid(rng), validUntil: null } satisfies Location @@ -82,6 +86,7 @@ async function setupHierarchyWithUsers() { locationType: LocationType.enum.ADMIN_STRUCTURE, parentId: null, id: generateUuid(rng), + externalId: generateUuid(rng), validUntil: null } satisfies Location @@ -90,6 +95,7 @@ async function setupHierarchyWithUsers() { locationType: LocationType.enum.ADMIN_STRUCTURE, parentId: provinceA.id, id: generateUuid(rng), + externalId: generateUuid(rng), validUntil: null } satisfies Location @@ -98,6 +104,7 @@ async function setupHierarchyWithUsers() { locationType: LocationType.enum.ADMIN_STRUCTURE, parentId: districtA.id, id: generateUuid(rng), + externalId: generateUuid(rng), validUntil: null } satisfies Location @@ -106,6 +113,7 @@ async function setupHierarchyWithUsers() { locationType: LocationType.enum.ADMIN_STRUCTURE, parentId: provinceB.id, id: generateUuid(rng), + externalId: generateUuid(rng), validUntil: null } satisfies Location diff --git a/packages/events/src/router/locations/__snapshots__/locations.set.test.ts.snap b/packages/events/src/router/locations/__snapshots__/locations.set.test.ts.snap index f36154679a3..fc63f3eca57 100644 --- a/packages/events/src/router/locations/__snapshots__/locations.set.test.ts.snap +++ b/packages/events/src/router/locations/__snapshots__/locations.set.test.ts.snap @@ -23,17 +23,11 @@ exports[`parent id is a duplicate of administrative area id in locations 1`] = ` "validUntil": null, }, { - "id": "6ba32aee-f566-4993-972f-5989eb23b915", + "id": "435d9c91-0bd8-4ae9-af7b-3f90516c5c43", "name": "Location name 2", "parentId": null, "validUntil": null, }, - { - "id": "25bb4f42-d1c1-478f-b001-2def8aa07f2a", - "name": "Location name 3", - "parentId": null, - "validUntil": null, - }, { "id": "fb2e5318-be7e-4499-a61a-9563b9c4c630", "name": "Parent Admin Area", @@ -58,7 +52,7 @@ exports[`parent id is a duplicate of administrative area id in locations 1`] = ` }, { "administrativeAreaId": null, - "id": "fe99b4fc-5c4f-4a2d-a9ff-27ffad4a5c8b", + "id": "5125bb4f-42d1-4c17-8fb0-012def8aa07f", "locationType": "HEALTH_FACILITY", "name": "Location name 1", "parentId": null, @@ -66,7 +60,7 @@ exports[`parent id is a duplicate of administrative area id in locations 1`] = ` }, { "administrativeAreaId": null, - "id": "6ba32aee-f566-4993-972f-5989eb23b915", + "id": "435d9c91-0bd8-4ae9-af7b-3f90516c5c43", "locationType": "ADMIN_STRUCTURE", "name": "Location name 2", "parentId": null, @@ -74,16 +68,16 @@ exports[`parent id is a duplicate of administrative area id in locations 1`] = ` }, { "administrativeAreaId": null, - "id": "25bb4f42-d1c1-478f-b001-2def8aa07f2a", - "locationType": "ADMIN_STRUCTURE", + "id": "16c434f2-78fd-4203-8ed3-b4dc653fe017", + "locationType": "HEALTH_FACILITY", "name": "Location name 3", "parentId": null, "validUntil": null, }, { "administrativeAreaId": null, - "id": "94230bc4-dc41-4ef4-a3e4-577305ad7fd3", - "locationType": "CRVS_OFFICE", + "id": "aa28878e-b3d2-4db5-aced-c043f15af35b", + "locationType": "HEALTH_FACILITY", "name": "Location name 4", "parentId": null, "validUntil": null, diff --git a/packages/events/src/router/locations/index.ts b/packages/events/src/router/locations/index.ts index c61dbaca929..8efd5d9bf3c 100644 --- a/packages/events/src/router/locations/index.ts +++ b/packages/events/src/router/locations/index.ts @@ -13,6 +13,7 @@ import * as z from 'zod/v4' import { Location, LocationType, SCOPES, UUID } from '@opencrvs/commons' import { router, userAndSystemProcedure } from '@events/router/trpc' import { + getLocationHierarchy, getLocations, setLocations, syncLocations @@ -44,7 +45,8 @@ export const locationRouter = router({ .object({ isActive: z.boolean().optional(), locationIds: z.array(UUID).optional(), - locationType: LocationType.optional() + locationType: LocationType.optional(), + externalId: z.string().optional() }) .optional() ) @@ -53,7 +55,8 @@ export const locationRouter = router({ getLocations({ isActive: input?.isActive, locationIds: input?.locationIds, - locationType: input?.locationType + locationType: input?.locationType, + externalId: input?.externalId }) ), set: userAndSystemProcedure @@ -64,5 +67,12 @@ export const locationRouter = router({ .output(z.void()) .mutation(async ({ input }) => { await setLocations(input) + }), + getLocationHierarchy: userAndSystemProcedure + + .input(z.object({ locationId: UUID })) + .output(z.array(UUID)) + .query(async ({ input }) => { + return getLocationHierarchy(input.locationId) }) }) diff --git a/packages/events/src/router/locations/locations.list.test.ts b/packages/events/src/router/locations/locations.list.test.ts index b4c362feebc..1d4e87077c0 100644 --- a/packages/events/src/router/locations/locations.list.test.ts +++ b/packages/events/src/router/locations/locations.list.test.ts @@ -29,7 +29,8 @@ test('Returns single location in right format', async () => { parentId: null, name: 'Location foobar', validUntil: null, - locationType: LocationType.enum.ADMIN_STRUCTURE + locationType: LocationType.enum.ADMIN_STRUCTURE, + externalId: 'abc123xyz456' } ] diff --git a/packages/events/src/router/locations/locations.set.test.ts b/packages/events/src/router/locations/locations.set.test.ts index c7b0eb5ec91..4156ff1626b 100644 --- a/packages/events/src/router/locations/locations.set.test.ts +++ b/packages/events/src/router/locations/locations.set.test.ts @@ -59,7 +59,8 @@ test('Creates single location', async () => { parentId: null, name: 'Location foobar', validUntil: null, - locationType: LocationType.enum.ADMIN_STRUCTURE + locationType: LocationType.enum.ADMIN_STRUCTURE, + externalId: 'abc123xyz456' } ] @@ -177,28 +178,32 @@ test('parent id is a duplicate of administrative area id in locations', async () parentId: null, locationType: LocationType.enum.ADMIN_STRUCTURE, name: 'Parent Admin Area', - validUntil: null + validUntil: null, + externalId: 'abc123xyz456' }, { id: generateUuid(locationRng), parentId: parentAdminAreaId, locationType: LocationType.enum.ADMIN_STRUCTURE, name: 'Child Admin Area', - validUntil: null + validUntil: null, + externalId: 'abc123xyz467' }, { id: generateUuid(locationRng), parentId: parentAdminAreaId, locationType: LocationType.enum.HEALTH_FACILITY, name: 'Child Health Facility', - validUntil: null + validUntil: null, + externalId: 'abc123xyz468' }, { id: generateUuid(locationRng), parentId: parentAdminAreaId, locationType: LocationType.enum.CRVS_OFFICE, name: 'Child CRVS Office', - validUntil: null + validUntil: null, + externalId: 'abc123xyz469' } ] diff --git a/packages/events/src/router/user/__snapshots__/user.list.test.ts.snap b/packages/events/src/router/user/__snapshots__/user.list.test.ts.snap index 8df1ce2cac1..5d70d792c2c 100644 --- a/packages/events/src/router/user/__snapshots__/user.list.test.ts.snap +++ b/packages/events/src/router/user/__snapshots__/user.list.test.ts.snap @@ -33,7 +33,7 @@ exports[`Returns both normal users and system users 1`] = ` "use": "en", }, ], - "primaryOfficeId": "fe99b4fc-5c4f-4a2d-a9ff-27ffad4a5c8b", + "primaryOfficeId": "5125bb4f-42d1-4c17-8fb0-012def8aa07f", "role": "REGISTRATION_AGENT", "signature": undefined, "type": "user", @@ -51,7 +51,7 @@ exports[`Returns both normal users and system users 1`] = ` "use": "en", }, ], - "primaryOfficeId": "6ba32aee-f566-4993-972f-5989eb23b915", + "primaryOfficeId": "435d9c91-0bd8-4ae9-af7b-3f90516c5c43", "role": "REGISTRATION_AGENT", "signature": undefined, "type": "user", @@ -69,7 +69,7 @@ exports[`Returns both normal users and system users 1`] = ` "use": "en", }, ], - "primaryOfficeId": "25bb4f42-d1c1-478f-b001-2def8aa07f2a", + "primaryOfficeId": "16c434f2-78fd-4203-8ed3-b4dc653fe017", "role": "REGISTRATION_AGENT", "signature": undefined, "type": "user", @@ -87,7 +87,7 @@ exports[`Returns both normal users and system users 1`] = ` "use": "en", }, ], - "primaryOfficeId": "94230bc4-dc41-4ef4-a3e4-577305ad7fd3", + "primaryOfficeId": "aa28878e-b3d2-4db5-aced-c043f15af35b", "role": "REGISTRATION_AGENT", "signature": undefined, "type": "user", @@ -134,7 +134,7 @@ exports[`Returns multiple users with honorifics 1`] = ` "use": "en", }, ], - "primaryOfficeId": "fe99b4fc-5c4f-4a2d-a9ff-27ffad4a5c8b", + "primaryOfficeId": "5125bb4f-42d1-4c17-8fb0-012def8aa07f", "role": "REGISTRATION_AGENT", "signature": undefined, "type": "user", @@ -152,7 +152,7 @@ exports[`Returns multiple users with honorifics 1`] = ` "use": "en", }, ], - "primaryOfficeId": "6ba32aee-f566-4993-972f-5989eb23b915", + "primaryOfficeId": "435d9c91-0bd8-4ae9-af7b-3f90516c5c43", "role": "REGISTRATION_AGENT", "signature": undefined, "type": "user", @@ -170,7 +170,7 @@ exports[`Returns multiple users with honorifics 1`] = ` "use": "en", }, ], - "primaryOfficeId": "25bb4f42-d1c1-478f-b001-2def8aa07f2a", + "primaryOfficeId": "16c434f2-78fd-4203-8ed3-b4dc653fe017", "role": "REGISTRATION_AGENT", "signature": undefined, "type": "user", @@ -188,7 +188,7 @@ exports[`Returns multiple users with honorifics 1`] = ` "use": "en", }, ], - "primaryOfficeId": "94230bc4-dc41-4ef4-a3e4-577305ad7fd3", + "primaryOfficeId": "aa28878e-b3d2-4db5-aced-c043f15af35b", "role": "REGISTRATION_AGENT", "signature": undefined, "type": "user", diff --git a/packages/events/src/router/user/user.actions.test.ts b/packages/events/src/router/user/user.actions.test.ts index f9969fff7d9..d9dd879fd9a 100644 --- a/packages/events/src/router/user/user.actions.test.ts +++ b/packages/events/src/router/user/user.actions.test.ts @@ -112,14 +112,16 @@ test('Finds user in nested location with my jurisdiction scope', async () => { parentId: userOnParentLocation.primaryOfficeId, locationType: LocationType.enum.ADMIN_STRUCTURE, id: childLocationId, - validUntil: null + validUntil: null, + externalId: 'abc123xyz457' }, { name: 'Grandchild office', parentId: childLocationId, locationType: LocationType.enum.CRVS_OFFICE, id: grandchildLocationId, - validUntil: null + validUntil: null, + externalId: 'abc123xyz458' } ]) @@ -170,7 +172,8 @@ test('Find user with appropriate scopes', async () => { parentId: userOnParentLocation.primaryOfficeId, locationType: LocationType.enum.CRVS_OFFICE, id: userToSearchLocationId, - validUntil: null + validUntil: null, + externalId: 'abc123xyz459' } ]) diff --git a/packages/events/src/service/events/reindex.test.ts b/packages/events/src/service/events/reindex.test.ts index 6608f1fcd6a..7d0b8fde985 100644 --- a/packages/events/src/service/events/reindex.test.ts +++ b/packages/events/src/service/events/reindex.test.ts @@ -64,6 +64,7 @@ beforeEach(async () => { { name: 'Adming level 1', parentId: null, + externalId: 'AS0978ASD2A', locationType: LocationType.enum.ADMIN_STRUCTURE, id: adminLevel1Id, validUntil: null @@ -71,6 +72,7 @@ beforeEach(async () => { { name: 'Admin level 2', parentId: adminLevel1Id, + externalId: 'AS0978ASD2B', locationType: LocationType.enum.ADMIN_STRUCTURE, id: adminLevel2Id, validUntil: null @@ -78,6 +80,7 @@ beforeEach(async () => { { name: 'Admin level 2', parentId: adminLevel2Id, + externalId: 'AS0978ASD2C', locationType: LocationType.enum.CRVS_OFFICE, id: crvsOfficeId, validUntil: null diff --git a/packages/events/src/service/locations/locations.ts b/packages/events/src/service/locations/locations.ts index 7032dab8c89..d929704389c 100644 --- a/packages/events/src/service/locations/locations.ts +++ b/packages/events/src/service/locations/locations.ts @@ -28,14 +28,17 @@ export async function setLocations(locations: Location[]) { await locationsRepo.addAdministrativeAreas(administrativeAreas) await locationsRepo.setLocations( - locations.map(({ id, name, parentId, validUntil, locationType }) => ({ - id, - name, - parentId, - administrativeAreaId: parentId, - validUntil: validUntil ? new Date(validUntil).toISOString() : null, - locationType - })) + locations.map( + ({ id, name, parentId, validUntil, locationType, externalId }) => ({ + id, + name, + parentId, + administrativeAreaId: parentId, + validUntil: validUntil ? new Date(validUntil).toISOString() : null, + locationType, + externalId + }) + ) ) } @@ -57,6 +60,7 @@ export async function getLocations(params?: { locationType?: LocationType locationIds?: UUID[] isActive?: boolean + externalId?: string }) { const locations = await locationsRepo.getLocations(params) @@ -84,3 +88,7 @@ export const getChildLocations = async (parentIdToSearch: UUID) => { locationType })) } + +export const getLocationHierarchy = async (locationId: UUID) => { + return locationsRepo.getLocationHierarchyRaw(locationId) +} diff --git a/packages/events/src/storage/postgres/events/locations.ts b/packages/events/src/storage/postgres/events/locations.ts index 30714f5a5c9..f0c7d0bbf09 100644 --- a/packages/events/src/storage/postgres/events/locations.ts +++ b/packages/events/src/storage/postgres/events/locations.ts @@ -108,11 +108,13 @@ export async function setLocations(locations: NewLocations[]) { export async function getLocations({ locationType, locationIds, - isActive + isActive, + externalId }: { locationType?: LocationType locationIds?: UUID[] isActive?: boolean + externalId?: string } = {}) { const db = getClient() @@ -120,10 +122,15 @@ export async function getLocations({ .selectFrom('locations') .select([ 'id', + 'name', + 'parentId', + 'validUntil', + 'locationType', + 'externalId', 'administrativeAreaId' ]) .where('deletedAt', 'is', null) @@ -136,6 +143,10 @@ export async function getLocations({ query = query.where('locationType', '=', locationType) } + if (externalId) { + query = query.where('externalId', '=', externalId) + } + if (locationIds && locationIds.length > 0) { query = query.where('id', 'in', locationIds) } @@ -295,7 +306,7 @@ export async function getLocationById(locationId: UUID) { export async function getLocationHierarchyRaw(locationId: string) { const db = getClient() - const query = sql<{ ids: string[] }>` + const query = sql<{ ids: UUID[] }>` WITH RECURSIVE area_chain AS ( -- Base case: Start with the location itself SELECT diff --git a/packages/events/src/storage/postgres/events/schema/app/Locations.ts b/packages/events/src/storage/postgres/events/schema/app/Locations.ts index 0cdfc75c8fb..5905965e4f4 100644 --- a/packages/events/src/storage/postgres/events/schema/app/Locations.ts +++ b/packages/events/src/storage/postgres/events/schema/app/Locations.ts @@ -28,6 +28,8 @@ export default interface LocationsTable { validUntil: ColumnType administrativeAreaId: ColumnType + + externalId: ColumnType } export type Locations = Selectable diff --git a/packages/events/src/tests/generators.ts b/packages/events/src/tests/generators.ts index 4ac9ab8876c..6e13e2d20a5 100644 --- a/packages/events/src/tests/generators.ts +++ b/packages/events/src/tests/generators.ts @@ -18,7 +18,8 @@ import { Location, LocationType, generateUuid, - pickRandom + pickRandom, + generateTrackingId } from '@opencrvs/commons' import { setLocations } from '../service/locations/locations' @@ -70,6 +71,7 @@ export function payloadGenerator( name: `Location name ${i}`, parentId: null, validUntil: null, + externalId: generateTrackingId(prng) + generateTrackingId(prng), locationType: pickRandom(prng, LocationType.options) })) as Location[] } @@ -79,6 +81,9 @@ export function payloadGenerator( name: location.name ?? `Location name ${i}`, parentId: location.parentId ?? null, validUntil: null, + externalId: + location.externalId ?? + generateTrackingId(prng) + generateTrackingId(prng), locationType: LocationType.enum.ADMIN_STRUCTURE })) as Location[] } diff --git a/packages/events/src/tests/postgres-migrations.sql b/packages/events/src/tests/postgres-migrations.sql index 585e9ab390a..461d1c85b8a 100644 --- a/packages/events/src/tests/postgres-migrations.sql +++ b/packages/events/src/tests/postgres-migrations.sql @@ -194,7 +194,8 @@ CREATE TABLE app.locations ( deleted_at timestamp with time zone, location_type app.location_type, valid_until timestamp with time zone, - administrative_area_id uuid + administrative_area_id uuid, + external_id text ); @@ -364,6 +365,14 @@ ALTER TABLE ONLY app.events ADD CONSTRAINT events_transaction_id_event_type_key UNIQUE (transaction_id, event_type); +-- +-- Name: locations locations_external_id_key; Type: CONSTRAINT; Schema: app; Owner: events_migrator +-- + +ALTER TABLE ONLY app.locations + ADD CONSTRAINT locations_external_id_key UNIQUE (external_id); + + -- -- Name: locations locations_pkey; Type: CONSTRAINT; Schema: app; Owner: events_migrator -- diff --git a/packages/gateway/src/config/proxies.ts b/packages/gateway/src/config/proxies.ts index 9d161f03876..0e71887a47b 100644 --- a/packages/gateway/src/config/proxies.ts +++ b/packages/gateway/src/config/proxies.ts @@ -8,31 +8,11 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ -/* eslint-disable import/no-named-as-default-member */ -import { APPLICATION_CONFIG_URL, AUTH_URL } from '@gateway/constants' -import fetch from '@gateway/fetch' + +import { AUTH_URL } from '@gateway/constants' import { rateLimitedRoute } from '@gateway/rate-limit' -import { api } from '@gateway/v2-events/events/service' -import z from 'zod' import { ServerRoute } from '@hapi/hapi' -const LegacyLocationUpdate = z.object({ - name: z.string().optional(), - alias: z.string().optional(), - status: z.enum(['active', 'inactive']).optional(), - statistics: z - .array( - z.object({ - year: z.number(), - male_population: z.number(), - female_population: z.number(), - population: z.number(), - crude_birth_rate: z.number() - }) - ) - .optional() -}) - export const catchAllProxy = { auth: { method: 'POST', @@ -48,144 +28,6 @@ export const catchAllProxy = { parse: false } } - }, - /** @deprecated old naming strategy from Hearth. - * These are included for backwards compability but `locationS` should be preferred */ - location: { - method: '*', - path: '/location', - handler: (req, h) => - h.proxy({ - uri: `${APPLICATION_CONFIG_URL}locations${req.url.search}`, - passThrough: true - }), - options: { - auth: false, - payload: { - output: 'data', - parse: false - } - } - }, - /** @deprecated old naming strategy */ - locationId: { - method: '*', - path: '/location/{id}', - handler: (_, h) => - h.proxy({ - uri: `${APPLICATION_CONFIG_URL}locations/{id}`, - passThrough: true - }), - options: { - auth: false, - payload: { - output: 'data', - parse: false - } - } - }, - getLocations: { - method: 'GET', - path: '/locations', - handler: (req, h) => - h.proxy({ - uri: `${APPLICATION_CONFIG_URL}locations${req.url.search}`, - passThrough: true - }), - options: { - auth: false - } - }, - updateLocations: { - method: 'PUT', - path: '/locations/{id}', - handler: async (req, h) => { - const parseResult = LegacyLocationUpdate.safeParse(req.payload) - - if (!parseResult.success) { - return h.response().code(400) - } - - const body = parseResult.data - - const response = await fetch( - `${APPLICATION_CONFIG_URL}locations/${req.params.id}`, - { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - Authorization: req.headers.authorization || '' - }, - body: JSON.stringify(body) - } - ) - - if (!response.ok) { - return h.response().code(response.status) - } - - await api.locations.sync.mutate(undefined, { - context: { - headers: { - Authorization: req.headers.authorization - } - } - }) - - return h.response(response.body).code(response.status) - }, - options: { - auth: false - } - }, - createLocations: { - method: 'POST', - path: '/locations', - handler: async (req, h) => { - const body = req.payload - - const response = await fetch(`${APPLICATION_CONFIG_URL}locations`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: req.headers.authorization || '' - }, - body: JSON.stringify(body) - }) - - if (!response.ok) { - return h.response({}).code(response.status) - } - - await api.locations.sync.mutate(undefined, { - context: { - headers: { - Authorization: req.headers.authorization - } - } - }) - - return h.response({}).code(response.status) - }, - options: { - auth: false - } - }, - locationsSuffix: { - method: '*', - path: '/locations/{suffix}', - handler: (_, h) => - h.proxy({ - uri: `${APPLICATION_CONFIG_URL}locations/{suffix}`, - passThrough: true - }), - options: { - auth: false, - payload: { - output: 'data', - parse: false - } - } } } satisfies Record diff --git a/packages/gateway/src/config/routes.ts b/packages/gateway/src/config/routes.ts index 58960031b91..6bb89e49b8a 100644 --- a/packages/gateway/src/config/routes.ts +++ b/packages/gateway/src/config/routes.ts @@ -126,14 +126,6 @@ export const getRoutes = () => { } } }, - catchAllProxy.getLocations, - catchAllProxy.updateLocations, - catchAllProxy.createLocations, - catchAllProxy.locationsSuffix, - - catchAllProxy.location, - catchAllProxy.locationId, - catchAllProxy.auth, authProxy.token, rateLimitedAuthProxy.authenticate, diff --git a/packages/gateway/src/features/user/type-resolvers.ts b/packages/gateway/src/features/user/type-resolvers.ts index 98c3f147c02..dd588750dc1 100644 --- a/packages/gateway/src/features/user/type-resolvers.ts +++ b/packages/gateway/src/features/user/type-resolvers.ts @@ -8,7 +8,8 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ - +import { AppRouter } from '../../v2-events/events/router' +import { env } from '@gateway/environment' import { IAuthHeader, Roles, @@ -33,6 +34,9 @@ import { } from '@opencrvs/commons/types' import { getTokenPayload, scopesInclude } from './utils' import { Scope, SCOPES } from '@opencrvs/commons/authentication' +import { createTRPCClient, httpLink } from '@trpc/client' +import superjson from 'superjson' + interface IAuditHistory { auditedBy: string auditedOn: number @@ -182,8 +186,32 @@ export const userTypeResolvers: GQLResolver = { email(userModel: IUserModelData) { return userModel.emailForNotification }, - async primaryOffice(userModel: IUserModelData, _, { dataSources }) { - return dataSources.locationsAPI.getLocation(userModel.primaryOfficeId) + async primaryOffice(userModel: IUserModelData, _, { headers }) { + const client = createTRPCClient({ + links: [ + httpLink({ + url: `${env.EVENTS_URL}/trpc`, + transformer: superjson, + headers() { + return { + Authorization: headers.Authorization + } + } + }) + ] + }) + + const [office] = await client.locations.list.query( + { + locationIds: [userModel.primaryOfficeId] + }, + { + context: { + headers + } + } + ) + return office }, async localRegistrar( userModel: IUserModelData, diff --git a/packages/migration/src/migrations/events/1764168039557_add_back_external_id_in_locations.sql b/packages/migration/src/migrations/events/1764168039557_add_back_external_id_in_locations.sql new file mode 100644 index 00000000000..6855f0fb020 --- /dev/null +++ b/packages/migration/src/migrations/events/1764168039557_add_back_external_id_in_locations.sql @@ -0,0 +1,7 @@ +-- Up Migration +ALTER TABLE locations ADD COLUMN external_id text UNIQUE; + +-- Down Migration + +ALTER TABLE locations DROP COLUMN external_id; + diff --git a/yarn.lock b/yarn.lock index 2a77e70c2e3..2769e2b475a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6785,6 +6785,20 @@ resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-2.1.0.tgz#0acf32f470af2ceaf47f095cdecd40d68666efda" integrity sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg== +"@opencrvs/toolkit@1.9.0-rc.2ae48a1": + version "1.9.0-rc.2ae48a1" + resolved "https://registry.yarnpkg.com/@opencrvs/toolkit/-/toolkit-1.9.0-rc.2ae48a1.tgz#5daee29ffff06c5a08deaef3191c22038011797b" + integrity sha512-yoyuqzrSiKbDr9C7x5rnMEycV6pMqkJ6BAR9orBIg3Lcge1uXIdWDlYJiG09UMfVVlTWvz5vjdWkXYn2/Ou9Iw== + dependencies: + "@trpc/client" "11.4.3" + "@trpc/server" "11.4.3" + ajv "^8.17.1" + ajv-formats "^3.0.1" + object-hash "^3.0.0" + superjson "1.9.0-0" + uuid "^9.0.0" + zod-openapi "^5.4.3" + "@opentelemetry/api@1.x": version "1.9.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe"