Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix disabled Save to Link button issue #8025

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
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ private fun NetworkingLinkSignupMainContent(
Uninitialized, is Loading -> FullScreenGenericLoading()
is Success -> NetworkingLinkSignupLoaded(
scrollState = scrollState,
validForm = state.valid(),
validForm = state.valid,
payload = payload(),
lookupAccountSync = state.lookupAccount,
saveAccountToLinkSync = state.saveAccountToLink,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ internal class NetworkingLinkSignupViewModel @Inject constructor(
onSuccess = { consumerSession ->
if (consumerSession.exists) {
eventTracker.track(NetworkingReturningConsumer(PANE))
navigationManager.tryNavigateTo(NetworkingSaveToLinkVerification(referrer = PANE))
navigateToLinkVerification()
} else {
eventTracker.track(NetworkingNewConsumer(PANE))
}
Expand Down Expand Up @@ -193,12 +193,25 @@ internal class NetworkingLinkSignupViewModel @Inject constructor(
}

fun onSaveAccount() {
withState { state ->
eventTracker.track(Click(eventName = "click.save_to_link", pane = PANE))

val hasExistingAccount = state.lookupAccount()?.exists == true
if (hasExistingAccount) {
navigateToLinkVerification()
} else {
saveNewAccount()
}
}
}

private fun saveNewAccount() {
suspend {
eventTracker.track(Click(eventName = "click.save_to_link", pane = PANE))
val state = awaitState()
val selectedAccounts = getCachedAccounts()
val phoneController = state.payload()!!.phoneController
require(state.valid()) { "Form invalid! ${state.validEmail} ${state.validPhone}" }
require(state.valid) { "Form invalid! ${state.validEmail} ${state.validPhone}" }
saveAccountToLink.new(
country = phoneController.getCountryCode(),
email = state.validEmail!!,
Expand All @@ -208,6 +221,10 @@ internal class NetworkingLinkSignupViewModel @Inject constructor(
}.execute { copy(saveAccountToLink = it) }
}

private fun navigateToLinkVerification() {
navigationManager.tryNavigateTo(NetworkingSaveToLinkVerification(referrer = PANE))
}

fun onClickableTextClick(uri: String) = viewModelScope.launch {
// if clicked uri contains an eventName query param, track click event.
uriUtils.getQueryParameter(uri, "eventName")?.let { eventName ->
Expand Down Expand Up @@ -277,9 +294,11 @@ internal data class NetworkingLinkSignupState(
val showFullForm: Boolean
get() = lookupAccount()?.let { !it.exists } ?: false

fun valid(): Boolean {
return validEmail != null && validPhone != null
}
val valid: Boolean
get() {
val hasExistingAccount = lookupAccount()?.exists == true
return validEmail != null && (hasExistingAccount || validPhone != null)
}

data class Payload(
val merchantName: String?,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.stripe.android.financialconnections.features.networkinglinksignup

import app.cash.turbine.test
import com.airbnb.mvrx.test.MavericksTestRule
import com.google.common.truth.Truth.assertThat
import com.stripe.android.core.Logger
Expand All @@ -11,11 +12,14 @@ import com.stripe.android.financialconnections.domain.GetManifest
import com.stripe.android.financialconnections.domain.LookupAccount
import com.stripe.android.financialconnections.domain.SaveAccountToLink
import com.stripe.android.financialconnections.domain.SynchronizeFinancialConnectionsSession
import com.stripe.android.financialconnections.model.FinancialConnectionsSessionManifest.Pane.NETWORKING_LINK_SIGNUP_PANE
import com.stripe.android.financialconnections.model.NetworkingLinkSignupBody
import com.stripe.android.financialconnections.model.NetworkingLinkSignupPane
import com.stripe.android.financialconnections.model.TextUpdate
import com.stripe.android.financialconnections.navigation.Destination.NetworkingSaveToLinkVerification
import com.stripe.android.financialconnections.navigation.NavigationIntent
import com.stripe.android.financialconnections.navigation.NavigationManagerImpl
import com.stripe.android.financialconnections.repository.SaveToLinkWithStripeSucceededRepository
import com.stripe.android.financialconnections.utils.TestNavigationManager
import com.stripe.android.financialconnections.utils.UriUtils
import com.stripe.android.model.ConsumerSessionLookup
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand All @@ -26,6 +30,8 @@ import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever

@OptIn(ExperimentalCoroutinesApi::class)
Expand All @@ -39,7 +45,7 @@ class NetworkingLinkSignupViewModelTest {
private val getManifest = mock<GetManifest>()
private val eventTracker = TestFinancialConnectionsAnalyticsTracker()
private val getAuthorizationSessionAccounts = mock<GetCachedAccounts>()
private val navigationManager = TestNavigationManager()
private val navigationManager = NavigationManagerImpl()
private val lookupAccount = mock<LookupAccount>()
private val saveAccountToLink = mock<SaveAccountToLink>()
private val sync = mock<SynchronizeFinancialConnectionsSession>()
Expand Down Expand Up @@ -85,6 +91,86 @@ class NetworkingLinkSignupViewModelTest {
assertThat(payload.emailController.fieldValue.first()).isEqualTo("test@test.com")
}

@Test
fun `Redirects to verification screen if entering returning user email`() = runTest {
val manifest = ApiKeyFixtures.sessionManifest()

whenever(sync()).thenReturn(
syncResponse().copy(
text = TextUpdate(
consent = null,
networkingLinkSignupPane = networkingLinkSignupPane()
)
)
)
whenever(getManifest()).thenReturn(manifest)
whenever(lookupAccount(any())).thenReturn(ConsumerSessionLookup(exists = true))

val viewModel = buildViewModel(NetworkingLinkSignupState())

navigationManager.navigationFlow.test {
val state = viewModel.awaitState()
val payload = requireNotNull(state.payload())
payload.emailController.onValueChange("email@email.com")

assertThat(awaitItem()).isEqualTo(
NavigationIntent.NavigateTo(
route = NetworkingSaveToLinkVerification(referrer = NETWORKING_LINK_SIGNUP_PANE),
popUpToCurrent = false,
inclusive = false,
isSingleTop = true,
)
)
}
}

@Test
fun `Enables Save To Link button if we encounter a returning user`() = runTest {
val manifest = ApiKeyFixtures.sessionManifest()

whenever(sync()).thenReturn(
syncResponse().copy(
text = TextUpdate(
consent = null,
networkingLinkSignupPane = networkingLinkSignupPane()
)
)
)
whenever(getManifest()).thenReturn(manifest)
whenever(lookupAccount(any())).thenReturn(ConsumerSessionLookup(exists = true))

val viewModel = buildViewModel(NetworkingLinkSignupState())

navigationManager.navigationFlow.test {
val state = viewModel.awaitState()
val payload = requireNotNull(state.payload())
payload.emailController.onValueChange("email@email.com")

assertThat(awaitItem()).isEqualTo(
NavigationIntent.NavigateTo(
route = NetworkingSaveToLinkVerification(referrer = NETWORKING_LINK_SIGNUP_PANE),
popUpToCurrent = false,
inclusive = false,
isSingleTop = true,
)
)

// Simulate the user pressing Save To Link after returning from the OTP screen
viewModel.onSaveAccount()

verify(saveAccountToLink, never()).new(any(), any(), any(), any())

assertThat(awaitItem()).isEqualTo(
NavigationIntent.NavigateTo(
route = NetworkingSaveToLinkVerification(referrer = NETWORKING_LINK_SIGNUP_PANE),
popUpToCurrent = false,
inclusive = false,
isSingleTop = true,
)
)
}
}

private fun networkingLinkSignupPane() = NetworkingLinkSignupPane(
aboveCta = "Above CTA",
body = NetworkingLinkSignupBody(emptyList()),
Expand Down
Loading