handling the reset password completion step within the view model and emitting view events to move the flow forwards

This commit is contained in:
Adam Brown 2022-06-08 09:51:11 +01:00
parent 47cedfb522
commit 16481df0f7
7 changed files with 53 additions and 18 deletions

View File

@ -47,10 +47,11 @@ sealed class OnboardingViewEvents : VectorViewEvents {
object OnHomeserverEdited : OnboardingViewEvents() object OnHomeserverEdited : OnboardingViewEvents()
data class OnSignModeSelected(val signMode: SignMode) : OnboardingViewEvents() data class OnSignModeSelected(val signMode: SignMode) : OnboardingViewEvents()
object OnForgetPasswordClicked : OnboardingViewEvents() object OnForgetPasswordClicked : OnboardingViewEvents()
data class OnResetPasswordSendThreePidDone(val email: String) : OnboardingViewEvents()
object OnResetPasswordMailConfirmationSuccess : OnboardingViewEvents() data class OnResetPasswordEmailConfirmationSent(val email: String) : OnboardingViewEvents()
object OpenResetPasswordComplete : OnboardingViewEvents()
object OnResetPasswordBreakerConfirmed : OnboardingViewEvents() object OnResetPasswordBreakerConfirmed : OnboardingViewEvents()
object OnResetPasswordMailConfirmationSuccessDone : OnboardingViewEvents() object OnResetPasswordComplete : OnboardingViewEvents()
data class OnSendEmailSuccess(val email: String) : OnboardingViewEvents() data class OnSendEmailSuccess(val email: String) : OnboardingViewEvents()
data class OnSendMsisdnSuccess(val msisdn: String) : OnboardingViewEvents() data class OnSendMsisdnSuccess(val msisdn: String) : OnboardingViewEvents()

View File

@ -443,7 +443,7 @@ class OnboardingViewModel @AssistedInject constructor(
private fun handleResetPassword(action: OnboardingAction.ResetPassword) { private fun handleResetPassword(action: OnboardingAction.ResetPassword) {
startResetPasswordFlow(action.email) { startResetPasswordFlow(action.email) {
setState { copy(isLoading = false, resetState = createResetState(action, selectedHomeserver)) } setState { copy(isLoading = false, resetState = createResetState(action, selectedHomeserver)) }
_viewEvents.post(OnboardingViewEvents.OnResetPasswordSendThreePidDone(action.email)) _viewEvents.post(OnboardingViewEvents.OnResetPasswordEmailConfirmationSent(action.email))
} }
} }
@ -506,7 +506,11 @@ class OnboardingViewModel @AssistedInject constructor(
runCatching { loginWizard.resetPasswordMailConfirmed(newPassword, logoutAllDevices = logoutAllDevices) }.fold( runCatching { loginWizard.resetPasswordMailConfirmed(newPassword, logoutAllDevices = logoutAllDevices) }.fold(
onSuccess = { onSuccess = {
setState { copy(isLoading = false, resetState = ResetState()) } setState { copy(isLoading = false, resetState = ResetState()) }
_viewEvents.post(OnboardingViewEvents.OnResetPasswordMailConfirmationSuccess) val nextEvent = when {
vectorFeatures.isOnboardingCombinedLoginEnabled() -> OnboardingViewEvents.OnResetPasswordComplete
else -> OnboardingViewEvents.OpenResetPasswordComplete
}
_viewEvents.post(nextEvent)
}, },
onFailure = { onFailure = {
setState { copy(isLoading = false) } setState { copy(isLoading = false) }

View File

@ -41,7 +41,7 @@ class FtueAuthResetPasswordSuccessFragment @Inject constructor() : AbstractFtueA
} }
private fun submit() { private fun submit() {
viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnResetPasswordMailConfirmationSuccessDone)) viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnResetPasswordComplete))
} }
override fun resetViewModel() { override fun resetViewModel() {

View File

@ -29,7 +29,6 @@ import androidx.fragment.app.FragmentTransaction
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.POP_BACK_STACK_EXCLUSIVE
import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragment
import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.extensions.addFragmentToBackstack
import im.vector.app.core.extensions.popBackstack import im.vector.app.core.extensions.popBackstack
@ -166,7 +165,7 @@ class FtueAuthVariant(
vectorFeatures.isOnboardingCombinedLoginEnabled() -> addLoginStageFragmentToBackstack(FtueAuthResetPasswordEmailEntryFragment::class.java) vectorFeatures.isOnboardingCombinedLoginEnabled() -> addLoginStageFragmentToBackstack(FtueAuthResetPasswordEmailEntryFragment::class.java)
else -> addLoginStageFragmentToBackstack(FtueAuthResetPasswordFragment::class.java) else -> addLoginStageFragmentToBackstack(FtueAuthResetPasswordFragment::class.java)
} }
is OnboardingViewEvents.OnResetPasswordSendThreePidDone -> { is OnboardingViewEvents.OnResetPasswordEmailConfirmationSent -> {
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE) supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE)
when { when {
vectorFeatures.isOnboardingCombinedLoginEnabled() -> addLoginStageFragmentToBackstack( vectorFeatures.isOnboardingCombinedLoginEnabled() -> addLoginStageFragmentToBackstack(
@ -187,12 +186,11 @@ class FtueAuthVariant(
option = commonOption option = commonOption
) )
} }
is OnboardingViewEvents.OnResetPasswordMailConfirmationSuccess -> { is OnboardingViewEvents.OpenResetPasswordComplete -> {
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE) supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE)
addLoginStageFragmentToBackstack(FtueAuthResetPasswordSuccessFragment::class.java) addLoginStageFragmentToBackstack(FtueAuthResetPasswordSuccessFragment::class.java)
} }
is OnboardingViewEvents.OnResetPasswordMailConfirmationSuccessDone -> { OnboardingViewEvents.OnResetPasswordComplete -> {
// Go back to the login fragment
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE) supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE)
} }
is OnboardingViewEvents.OnSendEmailSuccess -> { is OnboardingViewEvents.OnSendEmailSuccess -> {

View File

@ -478,7 +478,7 @@ class OnboardingViewModelTest {
} }
@Test @Test
fun `given can successfully reset password, when resetting password, then emits reset done event`() = runTest { fun `given can successfully start password reset, when resetting password, then emits confirmation email sent`() = runTest {
viewModelWith(initialState.copy(selectedHomeserver = SELECTED_HOMESERVER_STATE_SUPPORTED_LOGOUT_DEVICES)) viewModelWith(initialState.copy(selectedHomeserver = SELECTED_HOMESERVER_STATE_SUPPORTED_LOGOUT_DEVICES))
val test = viewModel.test() val test = viewModel.test()
fakeLoginWizard.givenResetPasswordSuccess(AN_EMAIL) fakeLoginWizard.givenResetPasswordSuccess(AN_EMAIL)
@ -495,12 +495,12 @@ class OnboardingViewModelTest {
copy(isLoading = false, resetState = resetState) copy(isLoading = false, resetState = resetState)
} }
) )
.assertEvents(OnboardingViewEvents.OnResetPasswordSendThreePidDone(AN_EMAIL)) .assertEvents(OnboardingViewEvents.OnResetPasswordEmailConfirmationSent(AN_EMAIL))
.finish() .finish()
} }
@Test @Test
fun `given reset state, when resending reset password email, then emits reset success`() = runTest { fun `given reset state, when resending reset password email, then triggers reset password and emits nothing`() = runTest {
viewModelWith(initialState.copy(resetState = ResetState(AN_EMAIL, A_PASSWORD))) viewModelWith(initialState.copy(resetState = ResetState(AN_EMAIL, A_PASSWORD)))
val test = viewModel.test() val test = viewModel.test()
fakeLoginWizard.givenResetPasswordSuccess(AN_EMAIL) fakeLoginWizard.givenResetPasswordSuccess(AN_EMAIL)
@ -516,12 +516,14 @@ class OnboardingViewModelTest {
) )
.assertNoEvents() .assertNoEvents()
.finish() .finish()
fakeLoginWizard.verifyResetPassword(AN_EMAIL)
} }
@Test @Test
fun `given can successfully confirm reset password, when confirm reset password, then emits reset success`() = runTest { fun `given can successfully confirm reset password, when confirm reset password, then opens reset password complete`() = runTest {
viewModelWith(initialState.copy(resetState = ResetState(AN_EMAIL, A_PASSWORD))) viewModelWith(initialState.copy(resetState = ResetState(AN_EMAIL, A_PASSWORD)))
val test = viewModel.test() val test = viewModel.test()
fakeVectorFeatures.givenCombinedLoginDisabled()
fakeLoginWizard.givenConfirmResetPasswordSuccess(A_PASSWORD) fakeLoginWizard.givenConfirmResetPasswordSuccess(A_PASSWORD)
fakeAuthenticationService.givenLoginWizard(fakeLoginWizard) fakeAuthenticationService.givenLoginWizard(fakeLoginWizard)
@ -533,10 +535,31 @@ class OnboardingViewModelTest {
{ copy(isLoading = true) }, { copy(isLoading = true) },
{ copy(isLoading = false, resetState = ResetState()) } { copy(isLoading = false, resetState = ResetState()) }
) )
.assertEvents(OnboardingViewEvents.OnResetPasswordMailConfirmationSuccess) .assertEvents(OnboardingViewEvents.OpenResetPasswordComplete)
.finish() .finish()
} }
@Test
fun `given can successfully confirm reset password, when confirm reset password, then emits reset password complete`() = runTest {
viewModelWith(initialState.copy(resetState = ResetState(AN_EMAIL, A_PASSWORD)))
val test = viewModel.test()
fakeVectorFeatures.givenCombinedLoginEnabled()
fakeLoginWizard.givenConfirmResetPasswordSuccess(A_PASSWORD)
fakeAuthenticationService.givenLoginWizard(fakeLoginWizard)
viewModel.handle(OnboardingAction.ResetPasswordMailConfirmed)
test
.assertStatesChanges(
initialState,
{ copy(isLoading = true) },
{ copy(isLoading = false, resetState = ResetState()) }
)
.assertEvents(OnboardingViewEvents.OnResetPasswordComplete)
.finish()
}
private fun viewModelWith(state: OnboardingViewState) { private fun viewModelWith(state: OnboardingViewState) {
OnboardingViewModel( OnboardingViewModel(
state, state,

View File

@ -17,6 +17,7 @@
package im.vector.app.test.fakes package im.vector.app.test.fakes
import io.mockk.coJustRun import io.mockk.coJustRun
import io.mockk.coVerify
import io.mockk.mockk import io.mockk.mockk
import org.matrix.android.sdk.api.auth.login.LoginWizard import org.matrix.android.sdk.api.auth.login.LoginWizard
@ -29,4 +30,8 @@ class FakeLoginWizard : LoginWizard by mockk() {
fun givenConfirmResetPasswordSuccess(password: String) { fun givenConfirmResetPasswordSuccess(password: String) {
coJustRun { resetPasswordMailConfirmed(password) } coJustRun { resetPasswordMailConfirmed(password) }
} }
fun verifyResetPassword(email: String) {
coVerify { resetPassword(email) }
}
} }

View File

@ -27,7 +27,11 @@ class FakeVectorFeatures : VectorFeatures by spyk<DefaultVectorFeatures>() {
every { isOnboardingPersonalizeEnabled() } returns true every { isOnboardingPersonalizeEnabled() } returns true
} }
fun givenCombinedRegisterEnabled() { fun givenCombinedLoginEnabled() {
every { isOnboardingCombinedRegisterEnabled() } returns true every { isOnboardingCombinedLoginEnabled() } returns true
}
fun givenCombinedLoginDisabled() {
every { isOnboardingCombinedLoginEnabled() } returns false
} }
} }