mirror of
https://github.com/bitwarden/android.git
synced 2025-03-15 18:58:59 +03:00
BIT-1965: Send email 2FA verification when switching to screen. (#1096)
This commit is contained in:
parent
4dddd1e210
commit
a8f76488da
2 changed files with 128 additions and 18 deletions
|
@ -298,11 +298,13 @@ class TwoFactorLoginViewModel @Inject constructor(
|
|||
|
||||
// Display a toast for a successful result.
|
||||
ResendEmailResult.Success -> {
|
||||
sendEvent(
|
||||
TwoFactorLoginEvent.ShowToast(
|
||||
message = R.string.verification_email_sent.asText(),
|
||||
),
|
||||
)
|
||||
if (action.isUserInitiated) {
|
||||
sendEvent(
|
||||
TwoFactorLoginEvent.ShowToast(
|
||||
message = R.string.verification_email_sent.asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -342,6 +344,7 @@ class TwoFactorLoginViewModel @Inject constructor(
|
|||
sendAction(
|
||||
TwoFactorLoginAction.Internal.ReceiveResendEmailResult(
|
||||
resendEmailResult = result,
|
||||
isUserInitiated = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -351,13 +354,35 @@ class TwoFactorLoginViewModel @Inject constructor(
|
|||
* Update the state with the auth method or opens the url for the recovery code.
|
||||
*/
|
||||
private fun handleSelectAuthMethod(action: TwoFactorLoginAction.SelectAuthMethod) {
|
||||
if (action.authMethod == TwoFactorAuthMethod.RECOVERY_CODE) {
|
||||
sendEvent(TwoFactorLoginEvent.NavigateToRecoveryCode)
|
||||
} else {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
authMethod = action.authMethod,
|
||||
)
|
||||
when (action.authMethod) {
|
||||
TwoFactorAuthMethod.RECOVERY_CODE -> {
|
||||
sendEvent(TwoFactorLoginEvent.NavigateToRecoveryCode)
|
||||
}
|
||||
|
||||
TwoFactorAuthMethod.EMAIL -> {
|
||||
if (state.authMethod != TwoFactorAuthMethod.EMAIL) {
|
||||
viewModelScope.launch {
|
||||
val result = authRepository.resendVerificationCodeEmail()
|
||||
sendAction(
|
||||
TwoFactorLoginAction.Internal.ReceiveResendEmailResult(
|
||||
resendEmailResult = result,
|
||||
isUserInitiated = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
mutableStateFlow.update { it.copy(authMethod = action.authMethod) }
|
||||
}
|
||||
|
||||
TwoFactorAuthMethod.AUTHENTICATOR_APP,
|
||||
TwoFactorAuthMethod.DUO,
|
||||
TwoFactorAuthMethod.YUBI_KEY,
|
||||
TwoFactorAuthMethod.U2F,
|
||||
TwoFactorAuthMethod.REMEMBER,
|
||||
TwoFactorAuthMethod.DUO_ORGANIZATION,
|
||||
TwoFactorAuthMethod.FIDO_2_WEB_APP,
|
||||
-> {
|
||||
mutableStateFlow.update { it.copy(authMethod = action.authMethod) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -592,6 +617,7 @@ sealed class TwoFactorLoginAction {
|
|||
*/
|
||||
data class ReceiveResendEmailResult(
|
||||
val resendEmailResult: ResendEmailResult,
|
||||
val isUserInitiated: Boolean,
|
||||
) : Internal()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -467,7 +467,11 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() {
|
|||
} returns ResendEmailResult.Error(message = null)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(
|
||||
DEFAULT_STATE,
|
||||
awaitItem(),
|
||||
)
|
||||
viewModel.actionChannel.trySend(
|
||||
TwoFactorLoginAction.SelectAuthMethod(
|
||||
TwoFactorAuthMethod.EMAIL,
|
||||
|
@ -475,11 +479,8 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() {
|
|||
)
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(authMethod = TwoFactorAuthMethod.EMAIL),
|
||||
viewModel.stateFlow.value,
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
viewModel.actionChannel.trySend(TwoFactorLoginAction.ResendEmailClick)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
authMethod = TwoFactorAuthMethod.EMAIL,
|
||||
|
@ -488,7 +489,29 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() {
|
|||
message = R.string.verification_email_not_sent.asText(),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
viewModel.actionChannel.trySend(TwoFactorLoginAction.ResendEmailClick)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
authMethod = TwoFactorAuthMethod.EMAIL,
|
||||
dialogState = TwoFactorLoginState.DialogState.Loading(
|
||||
message = R.string.submitting.asText(),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
authMethod = TwoFactorAuthMethod.EMAIL,
|
||||
dialogState = TwoFactorLoginState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.verification_email_not_sent.asText(),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -528,6 +551,67 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `ReceiveResendEmailResult with ResendEmailResult Success and isUserInitiated true should ShowToast`() =
|
||||
runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.actionChannel.trySend(
|
||||
TwoFactorLoginAction.Internal.ReceiveResendEmailResult(
|
||||
resendEmailResult = ResendEmailResult.Success,
|
||||
isUserInitiated = true,
|
||||
),
|
||||
)
|
||||
assertEquals(
|
||||
TwoFactorLoginEvent.ShowToast(
|
||||
message = R.string.verification_email_sent.asText(),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `ReceiveResendEmailResult with ResendEmailResult Success and isUserInitiated false should not emit any events`() =
|
||||
runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.actionChannel.trySend(
|
||||
TwoFactorLoginAction.Internal.ReceiveResendEmailResult(
|
||||
resendEmailResult = ResendEmailResult.Success,
|
||||
isUserInitiated = false,
|
||||
),
|
||||
)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ReceiveResendEmailResult with ResendEmailResult Error should not emit any events`() =
|
||||
runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(DEFAULT_STATE, awaitItem())
|
||||
viewModel.actionChannel.trySend(
|
||||
TwoFactorLoginAction.Internal.ReceiveResendEmailResult(
|
||||
resendEmailResult = ResendEmailResult.Error(message = null),
|
||||
isUserInitiated = true,
|
||||
),
|
||||
)
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialogState = TwoFactorLoginState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.verification_email_not_sent.asText(),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createViewModel(
|
||||
state: TwoFactorLoginState? = null,
|
||||
): TwoFactorLoginViewModel =
|
||||
|
|
Loading…
Add table
Reference in a new issue