PM-9937 an existing email should be able to add account from a different hosted instance. (#3613)
Some checks failed
Crowdin Push / Crowdin Push (push) Waiting to run
Scan / Check PR run (push) Failing after 0s
Scan / SAST scan (push) Has been skipped
Scan / Quality scan (push) Has been skipped
Test / Check PR run (push) Failing after 0s
Test / Test (push) Has been skipped

This commit is contained in:
Dave Severns 2024-07-23 15:45:56 -04:00 committed by GitHub
parent d2432f7cf7
commit b44a320dc8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 74 additions and 5 deletions

View file

@ -42,21 +42,26 @@ class LandingViewModel @Inject constructor(
isContinueButtonEnabled = authRepository.rememberedEmailAddress != null, isContinueButtonEnabled = authRepository.rememberedEmailAddress != null,
isRememberMeEnabled = authRepository.rememberedEmailAddress != null, isRememberMeEnabled = authRepository.rememberedEmailAddress != null,
selectedEnvironmentType = environmentRepository.environment.type, selectedEnvironmentType = environmentRepository.environment.type,
selectedEnvironmentLabel = environmentRepository.environment.label,
dialog = null, dialog = null,
accountSummaries = authRepository.userStateFlow.value?.toAccountSummaries().orEmpty(), accountSummaries = authRepository.userStateFlow.value?.toAccountSummaries().orEmpty(),
), ),
) { ) {
/** /**
* Returns the [AccountSummary] from the current state that matches the current email input, * Returns the [AccountSummary] from the current state that matches the current email input and
* of `null` if there is no match. * the the current environment, or `null` if there is no match.
*/ */
private val matchingAccountSummary: AccountSummary? private val matchingAccountSummary: AccountSummary?
get() { get() {
val currentEmail = state.emailInput val currentEmail = state.emailInput
val currentEnvironmentLabel = state.selectedEnvironmentLabel
val accountSummaries = state.accountSummaries val accountSummaries = state.accountSummaries
return accountSummaries return accountSummaries
.find { it.email == currentEmail } .find {
it.email == currentEmail &&
it.environmentLabel == currentEnvironmentLabel
}
?.takeUnless { !it.isLoggedIn } ?.takeUnless { !it.isLoggedIn }
} }
@ -221,6 +226,7 @@ class LandingViewModel @Inject constructor(
mutableStateFlow.update { mutableStateFlow.update {
it.copy( it.copy(
selectedEnvironmentType = action.environment.type, selectedEnvironmentType = action.environment.type,
selectedEnvironmentLabel = action.environment.label,
) )
} }
} }
@ -248,6 +254,7 @@ data class LandingState(
val isContinueButtonEnabled: Boolean, val isContinueButtonEnabled: Boolean,
val isRememberMeEnabled: Boolean, val isRememberMeEnabled: Boolean,
val selectedEnvironmentType: Environment.Type, val selectedEnvironmentType: Environment.Type,
val selectedEnvironmentLabel: String,
val dialog: DialogState?, val dialog: DialogState?,
val accountSummaries: List<AccountSummary>, val accountSummaries: List<AccountSummary>,
) : Parcelable { ) : Parcelable {

View file

@ -473,6 +473,7 @@ private val DEFAULT_STATE = LandingState(
isContinueButtonEnabled = true, isContinueButtonEnabled = true,
isRememberMeEnabled = false, isRememberMeEnabled = false,
selectedEnvironmentType = Environment.Type.US, selectedEnvironmentType = Environment.Type.US,
selectedEnvironmentLabel = Environment.Us.label,
dialog = null, dialog = null,
accountSummaries = emptyList(), accountSummaries = emptyList(),
) )

View file

@ -196,7 +196,7 @@ class LandingViewModelTest : BaseViewModelTest() {
@Suppress("MaxLineLength") @Suppress("MaxLineLength")
@Test @Test
fun `ContinueButtonClick with an email input matching an existing account that is logged in should show the account already added dialog`() { fun `ContinueButtonClick with an email input matching an existing account on same environment that is logged in should show the account already added dialog`() {
val rememberedEmail = "active@bitwarden.com" val rememberedEmail = "active@bitwarden.com"
val activeAccount = UserState.Account( val activeAccount = UserState.Account(
userId = "activeUserId", userId = "activeUserId",
@ -246,6 +246,63 @@ class LandingViewModelTest : BaseViewModelTest() {
) )
} }
@Suppress("MaxLineLength")
@Test
fun `ContinueButtonClick with an email input matching an existing account on different environment that is logged in should emit NavigateToLogin`() =
runTest {
val rememberedEmail = "active@bitwarden.com"
val activeAccount = UserState.Account(
userId = "activeUserId",
name = "name",
email = rememberedEmail,
avatarColorHex = "avatarColorHex",
environment = Environment.Us,
isPremium = true,
isLoggedIn = true,
isVaultUnlocked = true,
needsPasswordReset = false,
isBiometricsEnabled = false,
organizations = emptyList(),
needsMasterPassword = false,
trustedDevice = null,
)
val userState = UserState(
activeUserId = "activeUserId",
accounts = listOf(activeAccount),
)
val viewModel = createViewModel(
rememberedEmail = rememberedEmail,
userState = userState,
)
val accountSummaries = userState.toAccountSummaries()
val initialState = DEFAULT_STATE.copy(
emailInput = rememberedEmail,
isContinueButtonEnabled = true,
isRememberMeEnabled = true,
accountSummaries = accountSummaries,
)
assertEquals(
initialState,
viewModel.stateFlow.value,
)
viewModel.eventFlow.test {
viewModel.trySendAction(LandingAction.EnvironmentTypeSelect(Environment.Eu.type))
assertEquals(
initialState.copy(
selectedEnvironmentLabel = Environment.Eu.label,
selectedEnvironmentType = Environment.Eu.type,
),
viewModel.stateFlow.value,
)
viewModel.trySendAction(LandingAction.ContinueButtonClick)
assertEquals(
LandingEvent.NavigateToLogin(rememberedEmail),
awaitItem(),
)
}
}
@Suppress("MaxLineLength") @Suppress("MaxLineLength")
@Test @Test
fun `ContinueButtonClick with an email input matching an existing account that is logged out should emit NavigateToLogin`() = fun `ContinueButtonClick with an email input matching an existing account that is logged out should emit NavigateToLogin`() =
@ -382,7 +439,10 @@ class LandingViewModelTest : BaseViewModelTest() {
awaitItem() awaitItem()
viewModel.trySendAction(LandingAction.EnvironmentTypeSelect(inputEnvironmentType)) viewModel.trySendAction(LandingAction.EnvironmentTypeSelect(inputEnvironmentType))
assertEquals( assertEquals(
DEFAULT_STATE.copy(selectedEnvironmentType = Environment.Type.EU), DEFAULT_STATE.copy(
selectedEnvironmentType = Environment.Type.EU,
selectedEnvironmentLabel = Environment.Eu.label,
),
awaitItem(), awaitItem(),
) )
} }
@ -494,6 +554,7 @@ class LandingViewModelTest : BaseViewModelTest() {
isContinueButtonEnabled = false, isContinueButtonEnabled = false,
isRememberMeEnabled = false, isRememberMeEnabled = false,
selectedEnvironmentType = Environment.Type.US, selectedEnvironmentType = Environment.Type.US,
selectedEnvironmentLabel = Environment.Us.label,
dialog = null, dialog = null,
accountSummaries = emptyList(), accountSummaries = emptyList(),
) )