Skip to content

Commit 11bc477

Browse files
author
Clement Mwimo
committed
added the test
1 parent eb652ae commit 11bc477

File tree

2 files changed

+238
-37
lines changed

2 files changed

+238
-37
lines changed

src/views/verification/VerifyExistingMember.tsx

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
import React, { useState, useEffect } from 'react'
2+
import React, { useState, useEffect, useCallback } from 'react'
33
import PropTypes from 'prop-types'
4-
import { useDispatch } from 'react-redux'
4+
import { useDispatch, useSelector } from 'react-redux'
55

66
import { useTokens } from '@kyper/tokenprovider'
77
import { Text } from '@kyper/mui'
@@ -19,6 +19,8 @@ import { PrivateAndSecure } from 'src/components/PrivateAndSecure'
1919
import { LoadingSpinner } from 'src/components/LoadingSpinner'
2020
import { GenericError } from 'src/components/GenericError'
2121
import { useApi } from 'src/context/ApiContext'
22+
import { selectConfig } from 'src/redux/reducers/configSlice'
23+
import { instutionSupportRequestedProducts } from 'src/utilities/Institution'
2224

2325
interface VerifyExistingMemberProps {
2426
members: MemberResponseType[]
@@ -28,56 +30,84 @@ interface VerifyExistingMemberProps {
2830
const VerifyExistingMember: React.FC<VerifyExistingMemberProps> = (props) => {
2931
useAnalyticsPath(...PageviewInfo.CONNECT_VERIFY_EXISTING_MEMBER)
3032
const { api } = useApi()
33+
const config = useSelector(selectConfig)
3134
const dispatch = useDispatch()
3235
const { members, onAddNew } = props
3336
const iavMembers = members.filter(
3437
(member) => member.verification_is_enabled && member.is_managed_by_user, // Only show user-managed members that support verification
3538
)
36-
const [selectedMember, setSelectedMember] = useState<MemberResponseType | null>(null)
37-
const [{ isLoadingInstitution, institutionError }, setInstitution] = useState({
38-
isLoadingInstitution: false,
39-
institutionError: null,
40-
})
39+
const [institutions, setInstitutions] = useState<Map<string, InstitutionResponseType>>(new Map())
40+
const [loading, setLoading] = useState(true)
41+
const [error, setError] = useState<Error | null>(null)
4142

4243
const tokens = useTokens()
4344

4445
const styles = getStyles(tokens)
4546

46-
const handleMemberClick = (selectedMember: MemberResponseType) => {
47-
setSelectedMember(selectedMember)
48-
setInstitution((state) => ({ ...state, isLoadingInstitution: true }))
49-
}
47+
const handleMemberClick = useCallback(
48+
(selectedMember: MemberResponseType) => {
49+
const institution = institutions.get(selectedMember.institution_guid)
50+
if (institution) {
51+
if (selectedMember.is_oauth) {
52+
dispatch(startOauth(selectedMember, institution))
53+
} else {
54+
dispatch(verifyExistingConnection(selectedMember, institution))
55+
}
56+
}
57+
},
58+
[dispatch, institutions],
59+
)
5060

5161
useEffect(() => {
5262
focusElement(document.getElementById('connect-select-institution'))
5363
}, [])
5464

5565
useEffect(() => {
56-
if (!isLoadingInstitution || !selectedMember) return
57-
58-
api
59-
.loadInstitutionByGuid(selectedMember.institution_guid)
60-
.then((institution) => {
61-
if (selectedMember.is_oauth) {
62-
dispatch(startOauth(selectedMember, institution))
63-
} else {
64-
dispatch(verifyExistingConnection(selectedMember, institution))
65-
}
66-
})
67-
.catch((error) => {
68-
setInstitution((state) => ({
69-
...state,
70-
isLoadingInstitution: false,
71-
institutionError: error,
72-
}))
73-
})
74-
}, [isLoadingInstitution, selectedMember])
75-
76-
if (isLoadingInstitution) {
66+
const fetchInstitutions = async () => {
67+
try {
68+
const institutionPromises = iavMembers.map(async (member) => {
69+
const institution = await api.loadInstitutionByGuid(member.institution_guid)
70+
return { guid: member.institution_guid, institution }
71+
})
72+
73+
const results = await Promise.all(institutionPromises)
74+
75+
const institutionMap = new Map<string, InstitutionResponseType>()
76+
results.forEach(({ guid, institution }) => {
77+
if (institution) {
78+
institutionMap.set(guid, institution)
79+
}
80+
})
81+
setInstitutions(institutionMap)
82+
} catch (err) {
83+
setError(err as Error)
84+
} finally {
85+
setLoading(false)
86+
}
87+
}
88+
89+
if (iavMembers.length > 0) {
90+
fetchInstitutions()
91+
} else {
92+
setLoading(false) // No members to load
93+
}
94+
}, [api, iavMembers])
95+
96+
const productSupportingMembers = useCallback(() => {
97+
return iavMembers.filter((member) => {
98+
const institution = institutions.get(member.institution_guid)
99+
if (institution) {
100+
return instutionSupportRequestedProducts(config, institution)
101+
}
102+
return false
103+
})
104+
}, [config, institutions, iavMembers])
105+
106+
if (loading) {
77107
return <LoadingSpinner showText={true} />
78108
}
79109

80-
if (institutionError) {
110+
if (error) {
81111
return (
82112
<GenericError
83113
onAnalyticPageview={() => {}}
@@ -119,11 +149,11 @@ const VerifyExistingMember: React.FC<VerifyExistingMemberProps> = (props) => {
119149
{_n(
120150
'%1 Connected institution',
121151
'%1 Connected institutions',
122-
iavMembers.length,
123-
iavMembers.length,
152+
productSupportingMembers().length,
153+
productSupportingMembers().length,
124154
)}
125155
</Text>
126-
{iavMembers.map((member) => {
156+
{productSupportingMembers().map((member) => {
127157
return (
128158
<UtilityRow
129159
borderType="none"

src/views/verification/__tests__/VerifyExistingMember-test.tsx

Lines changed: 172 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import { render, screen } from 'src/utilities/testingLibrary'
1+
import { render, screen, waitFor } from 'src/utilities/testingLibrary'
22
import React from 'react'
33

44
import VerifyExistingMember from 'src/views/verification/VerifyExistingMember'
55
import { ReadableStatuses } from 'src/const/Statuses'
6+
import { ApiProvider } from 'src/context/ApiContext'
7+
import { apiValue } from 'src/const/apiProviderMock'
8+
import { initialState } from 'src/services/mockedData'
9+
import { COMBO_JOB_DATA_TYPES } from 'src/const/comboJobDataTypes'
610

711
describe('VerifyExistingMember Test', () => {
812
const onAddNewMock = vi.fn()
@@ -39,6 +43,173 @@ describe('VerifyExistingMember Test', () => {
3943
button.click()
4044
expect(onAddNewMock).toHaveBeenCalled()
4145
})
46+
47+
describe('product(s) support', () => {
48+
const mockMembers = [
49+
{
50+
guid: 'MBR-123',
51+
name: 'Member 1',
52+
institution_guid: 'INS-123',
53+
verification_is_enabled: true,
54+
is_managed_by_user: true,
55+
aggregation_status: 1,
56+
connection_status: ReadableStatuses.CONNECTED,
57+
institution_url: 'https://example.com',
58+
is_being_aggregated: false,
59+
is_manual: false,
60+
is_oauth: true,
61+
user_guid: 'USR-123',
62+
},
63+
{
64+
guid: 'MBR-456',
65+
name: 'Member 2',
66+
institution_guid: 'INS-456',
67+
verification_is_enabled: true,
68+
is_managed_by_user: true,
69+
aggregation_status: 6,
70+
connection_status: ReadableStatuses.CONNECTED,
71+
institution_url: 'https://example.com',
72+
is_being_aggregated: false,
73+
is_manual: false,
74+
is_oauth: true,
75+
user_guid: 'USR-456',
76+
},
77+
{
78+
guid: 'MBR-789',
79+
name: 'Member 3',
80+
institution_guid: 'INS-789',
81+
verification_is_enabled: true,
82+
is_managed_by_user: true,
83+
aggregation_status: 6,
84+
connection_status: ReadableStatuses.CONNECTED,
85+
institution_url: 'https://example.com',
86+
is_being_aggregated: false,
87+
is_manual: false,
88+
is_oauth: true,
89+
user_guid: 'USR-789',
90+
},
91+
]
92+
93+
const mockInstitutions = new Map([
94+
[
95+
'INS-123',
96+
{
97+
guid: 'INS-123',
98+
name: 'Institution 1',
99+
account_verification_is_enabled: true,
100+
account_identification_is_enabled: false,
101+
},
102+
],
103+
[
104+
'INS-456',
105+
{
106+
guid: 'INS-456',
107+
name: 'Institution 2',
108+
account_verification_is_enabled: true,
109+
account_identification_is_enabled: true,
110+
},
111+
],
112+
[
113+
'INS-789',
114+
{
115+
guid: 'INS-789',
116+
name: 'Institution 3',
117+
account_identification_is_enabled: true,
118+
account_verification_is_enabled: false,
119+
},
120+
],
121+
])
122+
123+
beforeEach(() => {
124+
vi.resetAllMocks()
125+
})
126+
127+
const loadInstitutionByGuidMock = vi.fn().mockImplementation((guid) => {
128+
return Promise.resolve(mockInstitutions.get(guid))
129+
})
130+
131+
it('should display members that support verification', async () => {
132+
render(
133+
<ApiProvider
134+
apiValue={{
135+
...apiValue,
136+
loadInstitutionByGuid: loadInstitutionByGuidMock,
137+
}}
138+
>
139+
<VerifyExistingMember members={mockMembers} onAddNew={vi.fn()} />,
140+
</ApiProvider>,
141+
{
142+
preloadedState: {
143+
config: {
144+
...initialState.config,
145+
data_request: { products: [COMBO_JOB_DATA_TYPES.ACCOUNT_NUMBER] },
146+
},
147+
},
148+
},
149+
)
150+
151+
await waitFor(() => expect(screen.getByText('Member 1')).toBeInTheDocument())
152+
153+
expect(screen.queryByText('Member 1')).toBeInTheDocument()
154+
expect(screen.queryByText('Member 2')).toBeInTheDocument()
155+
expect(screen.queryByText('Member 3')).not.toBeInTheDocument()
156+
})
157+
158+
it('should display members that support identification', async () => {
159+
render(
160+
<ApiProvider apiValue={{ ...apiValue, loadInstitutionByGuid: loadInstitutionByGuidMock }}>
161+
<VerifyExistingMember members={mockMembers} onAddNew={onAddNewMock} />
162+
</ApiProvider>,
163+
{
164+
preloadedState: {
165+
config: {
166+
...initialState.config,
167+
include_identity: true,
168+
},
169+
},
170+
},
171+
)
172+
173+
await waitFor(() => expect(screen.getByText('Member 2')).toBeInTheDocument())
174+
175+
expect(screen.queryByText('Member 2')).toBeInTheDocument()
176+
177+
expect(screen.queryByText('Member 1')).not.toBeInTheDocument()
178+
expect(screen.queryByText('Member 3')).toBeInTheDocument()
179+
})
180+
181+
// it('should display members that support both verification and identification', async () => {
182+
// render(
183+
// <ApiProvider
184+
// apiValue={{
185+
// ...apiValue,
186+
// loadInstitutionByGuid: vi.fn().mockImplementation((guid) => {
187+
// return Promise.resolve(mockInstitutions.get(guid))
188+
// }),
189+
// }}
190+
// >
191+
// <VerifyExistingMember members={mockMembers} onAddNew={vi.fn()} />,
192+
// </ApiProvider>,
193+
// {
194+
// preloadedState: {
195+
// config: {
196+
// ...initialState.config,
197+
// data_request: {
198+
// products: [COMBO_JOB_DATA_TYPES.ACCOUNT_NUMBER, COMBO_JOB_DATA_TYPES.ACCOUNT_OWNER],
199+
// },
200+
// },
201+
// },
202+
// },
203+
// )
204+
205+
// await waitFor(() => expect(screen.getByText('Member 3')).toBeInTheDocument())
206+
207+
// expect(screen.queryByText('Member 3')).toBeInTheDocument()
208+
209+
// expect(screen.queryByText('Member 1')).not.toBeInTheDocument()
210+
// expect(screen.queryByText('Member 2')).not.toBeInTheDocument()
211+
// })
212+
})
42213
})
43214

44215
const mockMembers = [

0 commit comments

Comments
 (0)