-
Notifications
You must be signed in to change notification settings - Fork 662
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
Restore Link Sign Up viewmodel and tests #9377
Conversation
Diffuse output:
APK
DEX
|
} | ||
}.collectLatest { email -> | ||
delay(LOOKUP_DEBOUNCE) | ||
if (email != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Collection block will be cancelled and replaced when new email is emitted. Debounce is meant to delay lookup in case new email gets emitted quickly after a previous emit. Old version used a debouncer as well.
748d028
to
c5921fe
Compare
private val navController: NavHostController = mock() | ||
private val linkAccountManager: LinkAccountManager = mock() | ||
private val linkEventsReporter: LinkEventsReporter = mock() | ||
private val logger = mock<Logger>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you use fakes or real implementations for these instead of mocks, unless there is some reason that isn't possible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LinkAccountManager and NavController aren't interfaces so we can't have fakes for this. I'm using fakes for logger and linkEventsReporter
now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect it's a fairly simple refactor to make LinkAccountManager an interface and it's likely worth it, given how many tests are modifying its behavior and validating that it's called correctly.
You can refactor like this:
- rename the existing LinkAccountManager to something like RealLinkAccountManager or DefaultLinkAccountManager
- add an interface called LinkAccountManager, which RealLinkAccountManager implements
- use a fake in the new tests you added
link/src/main/java/com/stripe/android/link/account/LinkAccountManager.kt
Outdated
Show resolved
Hide resolved
val errorMessage: ResolvableString? = null | ||
) : SignUpScreenState | ||
|
||
data object Loading : SignUpScreenState |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed? I would expect we'd have a loading state for Link as a whole, and then each individual screen shouldn't need to have a loading state.
If there's specific data we are waiting on, do we need the whole screen to be in a loading state? Or can we either:
- wait to transition to the screen until we have the info we need or
- keep the specific UI that's dependent on the async state in a loading state, rather than keeping the whole UI in a loading state
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took this out. The original link implementation checked for log in state on both the LinkViewModel and SignUpViewModel, but you're right, it shouldn't be duplicated.
val emailController: TextFieldController, | ||
val phoneNumberController: PhoneNumberController, | ||
val nameController: TextFieldController, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it possible to not use these controllers? I feel like it should be possible to use their validation logic without actually using the controllers + UI directly. Motivation here is just that these are overly complicated imo, when we know exactly the fields we need to use and how they should look
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to avoid the controllers, but the textfields associated with these are complicated, especially PhoneNumberTextField
. Creating TextField
s that match the existing would be increase the scope.
c5921fe
to
02ca344
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than increasing the visibility of the fake within this file, can you move it into its own file? I think it's a bit weird for one test to depend on another test
override fun onInlineSignupCheckboxChecked() { | ||
throw NotImplementedError() | ||
} | ||
override fun onInlineSignupCheckboxChecked() = Unit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The NotImplementedError
s here were serving a purpose, which is ensuring that those analytics functions are not called from tests where we don't expect them to be called. Can this change be reverted?
linkAccountManager.lookupConsumer(email).fold( | ||
onSuccess = { | ||
if (it != null) { | ||
onAccountFetched(it) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be done before we even get to this screen? It seems like this would always navigate away from the sign up screen, so I wonder if we should do it while loading or something instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be checked on the LinkVM. The user will not get to this screen if we find an account. The first lookup here will return null (most likely) since we already checked beforehand.
This VM also listens to email changes. If the lookup is successful after an email change, we would navigate to the next screen as well. This is not likely and I'm certain it won't be the case since we don't have persistent log in anymore. I decided to keep in case we change our mind to add persistence later on. The old link implementation had persistence and did a lookup anytime the email changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha, it sounds like this check is unnecessary right now then, is that right? If we add persistence back, we should add this code snippet back. But I think we should avoid adding this unless it's needed.
link/src/main/java/com/stripe/android/link/ui/signup/SignUpScreenState.kt
Show resolved
Hide resolved
private val navController: NavHostController = mock() | ||
private val linkAccountManager: LinkAccountManager = mock() | ||
private val linkEventsReporter: LinkEventsReporter = mock() | ||
private val logger = mock<Logger>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect it's a fairly simple refactor to make LinkAccountManager an interface and it's likely worth it, given how many tests are modifying its behavior and validating that it's called correctly.
You can refactor like this:
- rename the existing LinkAccountManager to something like RealLinkAccountManager or DefaultLinkAccountManager
- add an interface called LinkAccountManager, which RealLinkAccountManager implements
- use a fake in the new tests you added
02ca344
to
16ee79b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of these comments are blocking, so feel free to merge this and follow up on the comments if that's easier!
).fold( | ||
onSuccess = { | ||
onAccountFetched(it) | ||
linkEventsReporter.onSignupCompleted() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(for a follow up PR) should the analytics be handled as part of link account manager instead of here? I'd expect the analytics to be the same wherever linkAccountManager.signUp is called from
} | ||
|
||
private fun onError(error: Throwable) { | ||
logger.error("Error: ", error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this logger indicate somehow that the error is coming from within the signup VM?
override suspend fun lookupConsumer(email: String, startSession: Boolean): Result<LinkAccount?> { | ||
calledCount += 1 | ||
emails.add(email) | ||
delay(100) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(just clarifying) why do we need the delay here? I thought that the delay was handled within the VM, so we wouldn't also need one here
assertThat(linkAccountManager.emails.size).isEqualTo(1) | ||
assertThat(linkAccountManager.emails.firstOrNull()).isEqualTo("[email protected]") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you might be able to use assertThat(linkAccountManager.emails).containsExactly("[email protected]")
to simplify these asserts
linkAccountManager.lookupConsumer(email).fold( | ||
onSuccess = { | ||
if (it != null) { | ||
onAccountFetched(it) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha, it sounds like this check is unnecessary right now then, is that right? If we add persistence back, we should add this code snippet back. But I think we should avoid adding this unless it's needed.
Summary
hasUserLoggedOut(email)
for LinkAccountManagerMotivation
JIRA
Testing
Screenshots
Changelog