Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions src/views/oauth/OAuthStartError-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react'
import { describe, expect, it, vi } from 'vitest'

import { OAuthStartError } from 'src/views/oauth/OAuthStartError'
import { OAuthStep } from 'src/views/oauth/OAuthStep'
import { render, screen } from 'src/utilities/testingLibrary'
import { apiValue as apiValueMock } from 'src/const/apiProviderMock'
import { institutionData, member } from 'src/services/mockedData'

describe('OAuthStartError', () => {
const defaultProps = {
institution: institutionData.institution,
oauthStartError: { response: { status: 500 } },
onOAuthTryAgain: () => {},
}

describe('Rendering', () => {
it('renders the institution, the error title and message, and a Try again button', () => {
render(<OAuthStartError {...defaultProps} />)

expect(screen.getByText(institutionData.institution.name)).toBeInTheDocument()
expect(screen.getByText('Something went wrong')).toBeInTheDocument()
expect(screen.getByText('Please try again or come back later.')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'Try again' })).toBeInTheDocument()
})

it('shows the 403 error message when verification is not enabled', () => {
render(<OAuthStartError {...defaultProps} oauthStartError={{ response: { status: 403 } }} />)

expect(
screen.getByText('Verification must be enabled to use this feature.'),
).toBeInTheDocument()
})

it('shows the 409 error message for a conflicting request', () => {
render(<OAuthStartError {...defaultProps} oauthStartError={{ response: { status: 409 } }} />)

expect(
screen.getByText(
'Oops! There was a problem. Please check your username and password, and try again.',
),
).toBeInTheDocument()
})
})

describe('Try again', () => {
it('restarts the OAuth flow and shows the sign-in view when clicked', async () => {
const OAuthStepComponent = OAuthStep as unknown as React.ComponentType<{
institution: typeof institutionData.institution
onGoBack: () => void
}>
const addMember = vi
.fn()
.mockRejectedValueOnce({ response: { status: 500 } })
.mockResolvedValue({ member: member.member })

const { user } = render(
<OAuthStepComponent institution={institutionData.institution} onGoBack={() => {}} />,
{
apiValue: { ...apiValueMock, addMember } as unknown as typeof apiValueMock,
},
)

expect(await screen.findByText('Something went wrong')).toBeInTheDocument()
await user.click(screen.getByRole('button', { name: 'Try again' }))
expect(
await screen.findByText(`Log in at ${institutionData.institution.name}`),
).toBeInTheDocument()
})
})
})
120 changes: 120 additions & 0 deletions src/views/search/views/PopularInstitutionsList-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React from 'react'
import { describe, expect, it } from 'vitest'

import RenderConnectStep from 'src/components/RenderConnectStep'
import { render, screen } from 'src/utilities/testingLibrary'
import { initialState } from 'src/services/mockedData'
import { apiValue as apiValueMock } from 'src/const/apiProviderMock'
import { AccountTypes } from 'src/views/manualAccount/constants'
import { STEPS } from 'src/const/Connect'

type StateOverrides = {
enableManualAccounts?: boolean
mode?: string
hasAtriumApi?: boolean
}

const buildSearchState = ({
enableManualAccounts = true,
mode = 'aggregation',
hasAtriumApi = false,
}: StateOverrides = {}) => ({
...initialState,
config: { ...initialState.config, mode },
connect: {
...initialState.connect,
location: [{ step: STEPS.SEARCH }],
},
profiles: {
...initialState.profiles,
client: { ...initialState.profiles.client, has_atrium_api: hasAtriumApi },
widgetProfile: {
...initialState.profiles.widgetProfile,
enable_manual_accounts: enableManualAccounts,
},
},
})

const renderSearchStep = (preloadedState = buildSearchState()) =>
render(
<RenderConnectStep
availableAccountTypes={[AccountTypes.CHECKING, AccountTypes.SAVINGS]}
handleConsentGoBack={() => {}}
handleCredentialsGoBack={() => {}}
navigationRef={React.createRef()}
onManualAccountAdded={() => {}}
onUpsertMember={() => {}}
setConnectLocalState={() => {}}
/>,
{ apiValue: apiValueMock, preloadedState },
)

describe('PopularInstitutionsList', () => {
describe('Rendering', () => {
it('renders the enabled popular institutions, the search button, and hides client-disabled ones', async () => {
renderSearchStep()

expect(await screen.findByText('Gringotts')).toBeInTheDocument()
expect(screen.getByText('American Express Credit Card')).toBeInTheDocument()
expect(screen.getByText('Discover Credit Card')).toBeInTheDocument()
expect(screen.getByText('Capital One')).toBeInTheDocument()
expect(
screen.getByRole('button', { name: 'Search for your institution' }),
).toBeInTheDocument()
// Chase is disabled_by_client and should be filtered out.
expect(screen.queryByText('Chase')).not.toBeInTheDocument()
})
})

describe('Search button', () => {
it('focuses the search input when the search button is clicked', async () => {
const { user } = renderSearchStep()

await user.click(await screen.findByRole('button', { name: 'Search for your institution' }))

expect(screen.getByTestId('search-input')).toHaveFocus()
})
})

describe('Institution selection', () => {
it('advances past the search screen when an institution is selected', async () => {
const { user } = renderSearchStep()

await user.click(await screen.findByRole('button', { name: 'Add account with Gringotts' }))

expect(await screen.findByText('Log in at Test Bank')).toBeInTheDocument()
expect(screen.queryByTestId('search-header')).not.toBeInTheDocument()
})
})

describe('Manual accounts', () => {
it('shows the add-account-manually button and navigates to the menu when clicked', async () => {
const { user } = renderSearchStep(buildSearchState({ enableManualAccounts: true }))

await user.click(await screen.findByRole('button', { name: 'Add account manually' }))

expect(await screen.findByTestId('manual-account-menu-container')).toBeInTheDocument()
})

it('hides the button when manual accounts are disabled', async () => {
renderSearchStep(buildSearchState({ enableManualAccounts: false }))

await screen.findByRole('button', { name: 'Search for your institution' })
expect(screen.queryByRole('button', { name: 'Add account manually' })).not.toBeInTheDocument()
})

it('hides the button in verification mode', async () => {
renderSearchStep(buildSearchState({ mode: 'verification' }))

await screen.findByRole('button', { name: 'Search for your institution' })
expect(screen.queryByRole('button', { name: 'Add account manually' })).not.toBeInTheDocument()
})

it('hides the button when the client uses the Atrium API', async () => {
renderSearchStep(buildSearchState({ hasAtriumApi: true }))

await screen.findByRole('button', { name: 'Search for your institution' })
expect(screen.queryByRole('button', { name: 'Add account manually' })).not.toBeInTheDocument()
})
})
})
14 changes: 14 additions & 0 deletions src/views/search/views/SearchFailed-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react'
import { describe, expect, it } from 'vitest'

import { SearchFailed } from 'src/views/search/views/SearchFailed'
import { render, screen } from 'src/utilities/testingLibrary'

describe('SearchFailed', () => {
it('renders the search error title and message', () => {
render(<SearchFailed />)

expect(screen.getByText(/Search isn.t working/)).toBeVisible()
expect(screen.getByText('Something went wrong. Please try again.')).toBeVisible()
})
})
145 changes: 145 additions & 0 deletions src/views/search/views/SearchNoResult-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import React from 'react'
import { describe, expect, it } from 'vitest'

import RenderConnectStep from 'src/components/RenderConnectStep'
import { fireEvent, render, screen } from 'src/utilities/testingLibrary'
import { initialState } from 'src/services/mockedData'
import { apiValue as apiValueMock } from 'src/const/apiProviderMock'
import { AccountTypes } from 'src/views/manualAccount/constants'
import { STEPS } from 'src/const/Connect'

type StateOverrides = {
mode?: string
enableManualAccounts?: boolean
hasAtriumApi?: boolean
accountVerificationEnabled?: boolean
isMicrodepositsEnabled?: boolean
showMicrodeposits?: boolean
}

const buildSearchState = ({
mode = 'aggregation',
enableManualAccounts = true,
hasAtriumApi = false,
accountVerificationEnabled = false,
isMicrodepositsEnabled = false,
showMicrodeposits = false,
}: StateOverrides = {}) => ({
...initialState,
config: { ...initialState.config, mode },
connect: {
...initialState.connect,
location: [{ step: STEPS.SEARCH }],
},
profiles: {
...initialState.profiles,
client: { ...initialState.profiles.client, has_atrium_api: hasAtriumApi },
clientProfile: {
...initialState.profiles.clientProfile,
account_verification_is_enabled: accountVerificationEnabled,
is_microdeposits_enabled: isMicrodepositsEnabled,
},
widgetProfile: {
...initialState.profiles.widgetProfile,
enable_manual_accounts: enableManualAccounts,
show_microdeposits_in_connect: showMicrodeposits,
},
},
})

const microdepositsEnabledState = () =>
buildSearchState({
mode: 'verification',
accountVerificationEnabled: true,
isMicrodepositsEnabled: true,
showMicrodeposits: true,
})

const renderNoResultStep = (preloadedState = buildSearchState()) =>
render(
<RenderConnectStep
availableAccountTypes={[AccountTypes.CHECKING, AccountTypes.SAVINGS]}
handleConsentGoBack={() => {}}
handleCredentialsGoBack={() => {}}
navigationRef={React.createRef()}
onManualAccountAdded={() => {}}
onUpsertMember={() => {}}
setConnectLocalState={() => {}}
/>,
{
apiValue: { ...apiValueMock, loadInstitutions: () => Promise.resolve([]) },
preloadedState,
},
)

const startSearch = async (term = 'test bank') => {
fireEvent.change(await screen.findByPlaceholderText('Search'), { target: { value: term } })
}

describe('SearchNoResult', () => {
describe('Rendering', () => {
it('renders the no-results message with the search term and a suggestion', async () => {
renderNoResultStep()

await startSearch('test bank')

const message = await screen.findByTestId('0-search-results')
expect(message).toHaveTextContent(/No results found/)
expect(message).toHaveTextContent(/test bank/)
expect(
screen.getByText('Check spelling and try again, or try searching for another institution.'),
).toBeVisible()
})
})

describe('Manual accounts', () => {
it('shows the add-account-manually button and navigates to the menu when clicked', async () => {
const { user } = renderNoResultStep()

await startSearch()

await user.click(await screen.findByRole('button', { name: 'Add account manually' }))

expect(await screen.findByTestId('manual-account-menu-container')).toBeInTheDocument()
})

it.each([
['manual accounts are disabled', buildSearchState({ enableManualAccounts: false })],
['in verification mode', buildSearchState({ mode: 'verification' })],
['the client uses the Atrium API', buildSearchState({ hasAtriumApi: true })],
])('hides the button when %s', async (_case, state) => {
renderNoResultStep(state)

await startSearch()
await screen.findByTestId('0-search-results')

expect(screen.queryByRole('button', { name: 'Add account manually' })).not.toBeInTheDocument()
})
})

describe('Microdeposits', () => {
it('shows the connect-with-account-numbers button and navigates to microdeposits', async () => {
const { user } = renderNoResultStep(microdepositsEnabledState())

await startSearch()

await user.click(await screen.findByRole('button', { name: 'Connect with account numbers' }))

expect(await screen.findByText('Enter routing number')).toBeInTheDocument()
})

it.each([
['not in verification mode', buildSearchState()],
['verification is not enabled', buildSearchState({ mode: 'verification' })],
])('hides the button when %s', async (_case, state) => {
renderNoResultStep(state)

await startSearch()
await screen.findByTestId('0-search-results')

expect(
screen.queryByRole('button', { name: 'Connect with account numbers' }),
).not.toBeInTheDocument()
})
})
})
Loading
Loading