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

Restore Link Sign Up viewmodel and tests #9377

Merged
merged 12 commits into from
Oct 9, 2024
Merged

Conversation

toluo-stripe
Copy link
Contributor

@toluo-stripe toluo-stripe commented Oct 1, 2024

Summary

  • Link SignUp ViewModel
  • hasUserLoggedOut(email) for LinkAccountManager

Motivation

JIRA

Testing

  • Added tests
  • Modified tests
  • Manually verified

Screenshots

Before After
before screenshot after screenshot

Changelog

Copy link
Contributor

github-actions bot commented Oct 1, 2024

Diffuse output:

OLD: paymentsheet-example-release-master.apk (signature: V1, V2)
NEW: paymentsheet-example-release-pr.apk (signature: V1, V2)

          │          compressed           │         uncompressed         
          ├───────────┬───────────┬───────┼───────────┬───────────┬──────
 APK      │ old       │ new       │ diff  │ old       │ new       │ diff 
──────────┼───────────┼───────────┼───────┼───────────┼───────────┼──────
      dex │   3.9 MiB │   3.9 MiB │ -41 B │   8.6 MiB │   8.6 MiB │ -8 B 
     arsc │   2.3 MiB │   2.3 MiB │   0 B │   2.3 MiB │   2.3 MiB │  0 B 
 manifest │   5.1 KiB │   5.1 KiB │   0 B │  25.6 KiB │  25.6 KiB │  0 B 
      res │ 933.6 KiB │ 933.6 KiB │   0 B │   1.5 MiB │   1.5 MiB │  0 B 
   native │   2.6 MiB │   2.6 MiB │   0 B │     6 MiB │     6 MiB │  0 B 
    asset │   2.9 MiB │   2.9 MiB │   0 B │   2.9 MiB │   2.9 MiB │  0 B 
    other │   196 KiB │   196 KiB │  +7 B │ 430.6 KiB │ 430.6 KiB │  0 B 
──────────┼───────────┼───────────┼───────┼───────────┼───────────┼──────
    total │  12.8 MiB │  12.8 MiB │ -34 B │  21.7 MiB │  21.7 MiB │ -8 B 

 DEX     │ old   │ new   │ diff      
─────────┼───────┼───────┼───────────
   files │     1 │     1 │ 0         
 strings │ 42625 │ 42625 │ 0 (+1 -1) 
   types │ 14130 │ 14130 │ 0 (+0 -0) 
 classes │ 11764 │ 11764 │ 0 (+0 -0) 
 methods │ 60402 │ 60402 │ 0 (+0 -0) 
  fields │ 40064 │ 40064 │ 0 (+0 -0) 

 ARSC    │ old  │ new  │ diff 
─────────┼──────┼──────┼──────
 configs │  242 │  242 │  0   
 entries │ 6253 │ 6253 │  0
APK
    compressed    │   uncompressed   │                        
──────────┬───────┼───────────┬──────┤                        
 size     │ diff  │ size      │ diff │ path                   
──────────┼───────┼───────────┼──────┼────────────────────────
  3.9 MiB │ -41 B │   8.6 MiB │ -8 B │ ∆ classes.dex          
 53.4 KiB │  +5 B │ 118.3 KiB │  0 B │ ∆ META-INF/CERT.SF     
 50.1 KiB │  +2 B │ 118.2 KiB │  0 B │ ∆ META-INF/MANIFEST.MF 
──────────┼───────┼───────────┼──────┼────────────────────────
    4 MiB │ -34 B │   8.8 MiB │ -8 B │ (total)
DEX
STRINGS:

   old   │ new   │ diff      
  ───────┼───────┼───────────
   42625 │ 42625 │ 0 (+1 -1) 
  
  + ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":21,"pg-map-id":"74c4ce8","r8-mode":"full","version":"8.5.35"}
  
  - ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":21,"pg-map-id":"410b8d6","r8-mode":"full","version":"8.5.35"}

Comment on lines 116 to 101
}
}.collectLatest { email ->
delay(LOOKUP_DEBOUNCE)
if (email != null) {
Copy link
Contributor Author

@toluo-stripe toluo-stripe Oct 2, 2024

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.

@toluo-stripe toluo-stripe changed the title Tolu/link signup vm Link Sign Up viewmodel and tests Oct 2, 2024
@toluo-stripe toluo-stripe marked this pull request as ready for review October 2, 2024 20:12
@toluo-stripe toluo-stripe requested review from a team as code owners October 2, 2024 20:12
@toluo-stripe toluo-stripe changed the title Link Sign Up viewmodel and tests Restore Link Sign Up viewmodel and tests Oct 2, 2024
samer-stripe
samer-stripe previously approved these changes Oct 2, 2024
Comment on lines 65 to 68
private val navController: NavHostController = mock()
private val linkAccountManager: LinkAccountManager = mock()
private val linkEventsReporter: LinkEventsReporter = mock()
private val logger = mock<Logger>()
Copy link
Collaborator

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?

Copy link
Contributor Author

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.

Copy link
Collaborator

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

val errorMessage: ResolvableString? = null
) : SignUpScreenState

data object Loading : SignUpScreenState
Copy link
Collaborator

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

Copy link
Contributor Author

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.

Comment on lines 9 to 11
val emailController: TextFieldController,
val phoneNumberController: PhoneNumberController,
val nameController: TextFieldController,
Copy link
Collaborator

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

Copy link
Contributor Author

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 TextFields that match the existing would be increase the scope.

Copy link
Collaborator

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NotImplementedErrors 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?

Comment on lines +151 to +148
linkAccountManager.lookupConsumer(email).fold(
onSuccess = {
if (it != null) {
onAccountFetched(it)
Copy link
Collaborator

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

Copy link
Contributor Author

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.

Copy link
Collaborator

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.

Comment on lines 65 to 68
private val navController: NavHostController = mock()
private val linkAccountManager: LinkAccountManager = mock()
private val linkEventsReporter: LinkEventsReporter = mock()
private val logger = mock<Logger>()
Copy link
Collaborator

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

Copy link
Collaborator

@amk-stripe amk-stripe left a 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()
Copy link
Collaborator

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)
Copy link
Collaborator

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)
Copy link
Collaborator

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

Comment on lines +137 to +138
assertThat(linkAccountManager.emails.size).isEqualTo(1)
assertThat(linkAccountManager.emails.firstOrNull()).isEqualTo("[email protected]")
Copy link
Collaborator

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

Comment on lines +151 to +148
linkAccountManager.lookupConsumer(email).fold(
onSuccess = {
if (it != null) {
onAccountFetched(it)
Copy link
Collaborator

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.

@toluo-stripe toluo-stripe merged commit f837bac into master Oct 9, 2024
16 checks passed
@toluo-stripe toluo-stripe deleted the tolu/link_signup_vm branch October 9, 2024 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants