Merge pull request #5408 from vector-im/feature/adm/onboarding-tests
FTUE - Onboarding registration steps unit tests
This commit is contained in:
commit
ea9c9ae490
|
@ -0,0 +1 @@
|
||||||
|
Improved onboarding registration unit test coverage
|
|
@ -22,63 +22,49 @@ import im.vector.app.features.login.LoginConfig
|
||||||
import im.vector.app.features.login.ServerType
|
import im.vector.app.features.login.ServerType
|
||||||
import im.vector.app.features.login.SignMode
|
import im.vector.app.features.login.SignMode
|
||||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
import org.matrix.android.sdk.api.auth.data.Credentials
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
|
||||||
import org.matrix.android.sdk.internal.network.ssl.Fingerprint
|
import org.matrix.android.sdk.internal.network.ssl.Fingerprint
|
||||||
|
|
||||||
sealed class OnboardingAction : VectorViewModelAction {
|
sealed interface OnboardingAction : VectorViewModelAction {
|
||||||
data class OnGetStarted(val resetLoginConfig: Boolean, val onboardingFlow: OnboardingFlow) : OnboardingAction()
|
data class OnGetStarted(val resetLoginConfig: Boolean, val onboardingFlow: OnboardingFlow) : OnboardingAction
|
||||||
data class OnIAlreadyHaveAnAccount(val resetLoginConfig: Boolean, val onboardingFlow: OnboardingFlow) : OnboardingAction()
|
data class OnIAlreadyHaveAnAccount(val resetLoginConfig: Boolean, val onboardingFlow: OnboardingFlow) : OnboardingAction
|
||||||
|
|
||||||
data class UpdateServerType(val serverType: ServerType) : OnboardingAction()
|
data class UpdateServerType(val serverType: ServerType) : OnboardingAction
|
||||||
data class UpdateHomeServer(val homeServerUrl: String) : OnboardingAction()
|
data class UpdateHomeServer(val homeServerUrl: String) : OnboardingAction
|
||||||
data class UpdateUseCase(val useCase: FtueUseCase) : OnboardingAction()
|
data class UpdateUseCase(val useCase: FtueUseCase) : OnboardingAction
|
||||||
object ResetUseCase : OnboardingAction()
|
object ResetUseCase : OnboardingAction
|
||||||
data class UpdateSignMode(val signMode: SignMode) : OnboardingAction()
|
data class UpdateSignMode(val signMode: SignMode) : OnboardingAction
|
||||||
data class LoginWithToken(val loginToken: String) : OnboardingAction()
|
data class LoginWithToken(val loginToken: String) : OnboardingAction
|
||||||
data class WebLoginSuccess(val credentials: Credentials) : OnboardingAction()
|
data class WebLoginSuccess(val credentials: Credentials) : OnboardingAction
|
||||||
data class InitWith(val loginConfig: LoginConfig?) : OnboardingAction()
|
data class InitWith(val loginConfig: LoginConfig?) : OnboardingAction
|
||||||
data class ResetPassword(val email: String, val newPassword: String) : OnboardingAction()
|
data class ResetPassword(val email: String, val newPassword: String) : OnboardingAction
|
||||||
object ResetPasswordMailConfirmed : OnboardingAction()
|
object ResetPasswordMailConfirmed : OnboardingAction
|
||||||
|
|
||||||
// Login or Register, depending on the signMode
|
// Login or Register, depending on the signMode
|
||||||
data class LoginOrRegister(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction()
|
data class LoginOrRegister(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
|
||||||
|
object StopEmailValidationCheck : OnboardingAction
|
||||||
|
|
||||||
// Register actions
|
data class PostRegisterAction(val registerAction: RegisterAction) : OnboardingAction
|
||||||
open class RegisterAction : OnboardingAction()
|
|
||||||
|
|
||||||
data class AddThreePid(val threePid: RegisterThreePid) : RegisterAction()
|
|
||||||
object SendAgainThreePid : RegisterAction()
|
|
||||||
|
|
||||||
// TODO Confirm Email (from link in the email, open in the phone, intercepted by the app)
|
|
||||||
data class ValidateThreePid(val code: String) : RegisterAction()
|
|
||||||
|
|
||||||
data class CheckIfEmailHasBeenValidated(val delayMillis: Long) : RegisterAction()
|
|
||||||
object StopEmailValidationCheck : RegisterAction()
|
|
||||||
|
|
||||||
data class CaptchaDone(val captchaResponse: String) : RegisterAction()
|
|
||||||
object AcceptTerms : RegisterAction()
|
|
||||||
object RegisterDummy : RegisterAction()
|
|
||||||
|
|
||||||
// Reset actions
|
// Reset actions
|
||||||
open class ResetAction : OnboardingAction()
|
sealed interface ResetAction : OnboardingAction
|
||||||
|
|
||||||
object ResetHomeServerType : ResetAction()
|
object ResetHomeServerType : ResetAction
|
||||||
object ResetHomeServerUrl : ResetAction()
|
object ResetHomeServerUrl : ResetAction
|
||||||
object ResetSignMode : ResetAction()
|
object ResetSignMode : ResetAction
|
||||||
object ResetLogin : ResetAction()
|
object ResetLogin : ResetAction
|
||||||
object ResetResetPassword : ResetAction()
|
object ResetResetPassword : ResetAction
|
||||||
|
|
||||||
// Homeserver history
|
// Homeserver history
|
||||||
object ClearHomeServerHistory : OnboardingAction()
|
object ClearHomeServerHistory : OnboardingAction
|
||||||
|
|
||||||
data class PostViewEvent(val viewEvent: OnboardingViewEvents) : OnboardingAction()
|
data class PostViewEvent(val viewEvent: OnboardingViewEvents) : OnboardingAction
|
||||||
|
|
||||||
data class UserAcceptCertificate(val fingerprint: Fingerprint) : OnboardingAction()
|
data class UserAcceptCertificate(val fingerprint: Fingerprint) : OnboardingAction
|
||||||
|
|
||||||
object PersonalizeProfile : OnboardingAction()
|
object PersonalizeProfile : OnboardingAction
|
||||||
data class UpdateDisplayName(val displayName: String) : OnboardingAction()
|
data class UpdateDisplayName(val displayName: String) : OnboardingAction
|
||||||
object UpdateDisplayNameSkipped : OnboardingAction()
|
object UpdateDisplayNameSkipped : OnboardingAction
|
||||||
data class ProfilePictureSelected(val uri: Uri) : OnboardingAction()
|
data class ProfilePictureSelected(val uri: Uri) : OnboardingAction
|
||||||
object SaveSelectedProfilePicture : OnboardingAction()
|
object SaveSelectedProfilePicture : OnboardingAction
|
||||||
object UpdateProfilePictureSkipped : OnboardingAction()
|
object UpdateProfilePictureSkipped : OnboardingAction
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
private val vectorFeatures: VectorFeatures,
|
private val vectorFeatures: VectorFeatures,
|
||||||
private val analyticsTracker: AnalyticsTracker,
|
private val analyticsTracker: AnalyticsTracker,
|
||||||
private val uriFilenameResolver: UriFilenameResolver,
|
private val uriFilenameResolver: UriFilenameResolver,
|
||||||
|
private val registrationActionHandler: RegistrationActionHandler,
|
||||||
private val vectorOverrides: VectorOverrides
|
private val vectorOverrides: VectorOverrides
|
||||||
) : VectorViewModel<OnboardingViewState, OnboardingAction, OnboardingViewEvents>(initialState) {
|
) : VectorViewModel<OnboardingViewState, OnboardingAction, OnboardingViewEvents>(initialState) {
|
||||||
|
|
||||||
|
@ -116,16 +117,16 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash()
|
private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash()
|
||||||
|
|
||||||
|
private val registrationWizard: RegistrationWizard
|
||||||
|
get() = authenticationService.getRegistrationWizard()
|
||||||
|
|
||||||
val currentThreePid: String?
|
val currentThreePid: String?
|
||||||
get() = registrationWizard?.currentThreePid
|
get() = registrationWizard.currentThreePid
|
||||||
|
|
||||||
// True when login and password has been sent with success to the homeserver
|
// True when login and password has been sent with success to the homeserver
|
||||||
val isRegistrationStarted: Boolean
|
val isRegistrationStarted: Boolean
|
||||||
get() = authenticationService.isRegistrationStarted
|
get() = authenticationService.isRegistrationStarted
|
||||||
|
|
||||||
private val registrationWizard: RegistrationWizard?
|
|
||||||
get() = authenticationService.getRegistrationWizard()
|
|
||||||
|
|
||||||
private val loginWizard: LoginWizard?
|
private val loginWizard: LoginWizard?
|
||||||
get() = authenticationService.getLoginWizard()
|
get() = authenticationService.getLoginWizard()
|
||||||
|
|
||||||
|
@ -153,7 +154,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
is OnboardingAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
is OnboardingAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
||||||
is OnboardingAction.ResetPassword -> handleResetPassword(action)
|
is OnboardingAction.ResetPassword -> handleResetPassword(action)
|
||||||
is OnboardingAction.ResetPasswordMailConfirmed -> handleResetPasswordMailConfirmed()
|
is OnboardingAction.ResetPasswordMailConfirmed -> handleResetPasswordMailConfirmed()
|
||||||
is OnboardingAction.RegisterAction -> handleRegisterAction(action)
|
is OnboardingAction.PostRegisterAction -> handleRegisterAction(action.registerAction)
|
||||||
is OnboardingAction.ResetAction -> handleResetAction(action)
|
is OnboardingAction.ResetAction -> handleResetAction(action)
|
||||||
is OnboardingAction.UserAcceptCertificate -> handleUserAcceptCertificate(action)
|
is OnboardingAction.UserAcceptCertificate -> handleUserAcceptCertificate(action)
|
||||||
OnboardingAction.ClearHomeServerHistory -> handleClearHomeServerHistory()
|
OnboardingAction.ClearHomeServerHistory -> handleClearHomeServerHistory()
|
||||||
|
@ -164,6 +165,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
is OnboardingAction.ProfilePictureSelected -> handleProfilePictureSelected(action)
|
is OnboardingAction.ProfilePictureSelected -> handleProfilePictureSelected(action)
|
||||||
OnboardingAction.SaveSelectedProfilePicture -> updateProfilePicture()
|
OnboardingAction.SaveSelectedProfilePicture -> updateProfilePicture()
|
||||||
is OnboardingAction.PostViewEvent -> _viewEvents.post(action.viewEvent)
|
is OnboardingAction.PostViewEvent -> _viewEvents.post(action.viewEvent)
|
||||||
|
OnboardingAction.StopEmailValidationCheck -> cancelWaitForEmailValidation()
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,131 +268,41 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleRegisterAction(action: OnboardingAction.RegisterAction) {
|
private fun handleRegisterAction(action: RegisterAction) {
|
||||||
when (action) {
|
|
||||||
is OnboardingAction.CaptchaDone -> handleCaptchaDone(action)
|
|
||||||
is OnboardingAction.AcceptTerms -> handleAcceptTerms()
|
|
||||||
is OnboardingAction.RegisterDummy -> handleRegisterDummy()
|
|
||||||
is OnboardingAction.AddThreePid -> handleAddThreePid(action)
|
|
||||||
is OnboardingAction.SendAgainThreePid -> handleSendAgainThreePid()
|
|
||||||
is OnboardingAction.ValidateThreePid -> handleValidateThreePid(action)
|
|
||||||
is OnboardingAction.CheckIfEmailHasBeenValidated -> handleCheckIfEmailHasBeenValidated(action)
|
|
||||||
is OnboardingAction.StopEmailValidationCheck -> handleStopEmailValidationCheck()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleCheckIfEmailHasBeenValidated(action: OnboardingAction.CheckIfEmailHasBeenValidated) {
|
|
||||||
// We do not want the common progress bar to be displayed, so we do not change asyncRegistration value in the state
|
|
||||||
currentJob = executeRegistrationStep(withLoading = false) {
|
|
||||||
it.checkIfEmailHasBeenValidated(action.delayMillis)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleStopEmailValidationCheck() {
|
|
||||||
currentJob = null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleValidateThreePid(action: OnboardingAction.ValidateThreePid) {
|
|
||||||
currentJob = executeRegistrationStep {
|
|
||||||
it.handleValidateThreePid(action.code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun executeRegistrationStep(withLoading: Boolean = true,
|
|
||||||
block: suspend (RegistrationWizard) -> RegistrationResult): Job {
|
|
||||||
if (withLoading) {
|
|
||||||
setState { copy(asyncRegistration = Loading()) }
|
|
||||||
}
|
|
||||||
return viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
registrationWizard?.let { block(it) }
|
|
||||||
/*
|
|
||||||
// Simulate registration disabled
|
|
||||||
throw Failure.ServerError(MatrixError(
|
|
||||||
code = MatrixError.FORBIDDEN,
|
|
||||||
message = "Registration is disabled"
|
|
||||||
), 403))
|
|
||||||
*/
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
if (failure !is CancellationException) {
|
|
||||||
_viewEvents.post(OnboardingViewEvents.Failure(failure))
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
?.let { data ->
|
|
||||||
when (data) {
|
|
||||||
is RegistrationResult.Success -> onSessionCreated(data.session, isAccountCreated = true)
|
|
||||||
is RegistrationResult.FlowResponse -> onFlowResponse(data.flowResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncRegistration = Uninitialized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleAddThreePid(action: OnboardingAction.AddThreePid) {
|
|
||||||
setState { copy(asyncRegistration = Loading()) }
|
|
||||||
currentJob = viewModelScope.launch {
|
currentJob = viewModelScope.launch {
|
||||||
try {
|
if (action.hasLoadingState()) {
|
||||||
registrationWizard?.addThreePid(action.threePid)
|
setState { copy(asyncRegistration = Loading()) }
|
||||||
} catch (failure: Throwable) {
|
|
||||||
_viewEvents.post(OnboardingViewEvents.Failure(failure))
|
|
||||||
}
|
}
|
||||||
setState {
|
runCatching { registrationActionHandler.handleRegisterAction(registrationWizard, action) }
|
||||||
copy(
|
.fold(
|
||||||
asyncRegistration = Uninitialized
|
onSuccess = {
|
||||||
)
|
when {
|
||||||
}
|
action.ignoresResult() -> {
|
||||||
}
|
// do nothing
|
||||||
}
|
}
|
||||||
|
else -> when (it) {
|
||||||
private fun handleSendAgainThreePid() {
|
is RegistrationResult.Success -> onSessionCreated(it.session, isAccountCreated = true)
|
||||||
setState { copy(asyncRegistration = Loading()) }
|
is RegistrationResult.FlowResponse -> onFlowResponse(it.flowResult)
|
||||||
currentJob = viewModelScope.launch {
|
}
|
||||||
try {
|
}
|
||||||
registrationWizard?.sendAgainThreePid()
|
},
|
||||||
} catch (failure: Throwable) {
|
onFailure = {
|
||||||
_viewEvents.post(OnboardingViewEvents.Failure(failure))
|
if (it !is CancellationException) {
|
||||||
}
|
_viewEvents.post(OnboardingViewEvents.Failure(it))
|
||||||
setState {
|
}
|
||||||
copy(
|
}
|
||||||
asyncRegistration = Uninitialized
|
)
|
||||||
)
|
setState { copy(asyncRegistration = Uninitialized) }
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleAcceptTerms() {
|
|
||||||
currentJob = executeRegistrationStep {
|
|
||||||
it.acceptTerms()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleRegisterDummy() {
|
|
||||||
currentJob = executeRegistrationStep {
|
|
||||||
it.dummy()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleRegisterWith(action: OnboardingAction.LoginOrRegister) {
|
private fun handleRegisterWith(action: OnboardingAction.LoginOrRegister) {
|
||||||
reAuthHelper.data = action.password
|
reAuthHelper.data = action.password
|
||||||
currentJob = executeRegistrationStep {
|
handleRegisterAction(RegisterAction.CreateAccount(
|
||||||
it.createAccount(
|
action.username,
|
||||||
action.username,
|
action.password,
|
||||||
action.password,
|
action.initialDeviceName
|
||||||
action.initialDeviceName
|
))
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleCaptchaDone(action: OnboardingAction.CaptchaDone) {
|
|
||||||
currentJob = executeRegistrationStep {
|
|
||||||
it.performReCaptcha(action.captchaResponse)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleResetAction(action: OnboardingAction.ResetAction) {
|
private fun handleResetAction(action: OnboardingAction.ResetAction) {
|
||||||
|
@ -461,7 +373,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
when (action.signMode) {
|
when (action.signMode) {
|
||||||
SignMode.SignUp -> startRegistrationFlow()
|
SignMode.SignUp -> handleRegisterAction(RegisterAction.StartRegistration)
|
||||||
SignMode.SignIn -> startAuthenticationFlow()
|
SignMode.SignIn -> startAuthenticationFlow()
|
||||||
SignMode.SignInWithMatrixId -> _viewEvents.post(OnboardingViewEvents.OnSignModeSelected(SignMode.SignInWithMatrixId))
|
SignMode.SignInWithMatrixId -> _viewEvents.post(OnboardingViewEvents.OnSignModeSelected(SignMode.SignInWithMatrixId))
|
||||||
SignMode.Unknown -> Unit
|
SignMode.Unknown -> Unit
|
||||||
|
@ -499,7 +411,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
// If there is a pending email validation continue on this step
|
// If there is a pending email validation continue on this step
|
||||||
try {
|
try {
|
||||||
if (registrationWizard?.isRegistrationStarted == true) {
|
if (registrationWizard.isRegistrationStarted) {
|
||||||
currentThreePid?.let {
|
currentThreePid?.let {
|
||||||
handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnSendEmailSuccess(it)))
|
handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnSendEmailSuccess(it)))
|
||||||
}
|
}
|
||||||
|
@ -730,12 +642,6 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startRegistrationFlow() {
|
|
||||||
currentJob = executeRegistrationStep {
|
|
||||||
it.getRegistrationFlow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startAuthenticationFlow() {
|
private fun startAuthenticationFlow() {
|
||||||
// Ensure Wizard is ready
|
// Ensure Wizard is ready
|
||||||
loginWizard
|
loginWizard
|
||||||
|
@ -745,8 +651,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
private fun onFlowResponse(flowResult: FlowResult) {
|
private fun onFlowResponse(flowResult: FlowResult) {
|
||||||
// If dummy stage is mandatory, and password is already sent, do the dummy stage now
|
// If dummy stage is mandatory, and password is already sent, do the dummy stage now
|
||||||
if (isRegistrationStarted &&
|
if (isRegistrationStarted && flowResult.missingStages.any { it is Stage.Dummy && it.mandatory }) {
|
||||||
flowResult.missingStages.any { it is Stage.Dummy && it.mandatory }) {
|
|
||||||
handleRegisterDummy()
|
handleRegisterDummy()
|
||||||
} else {
|
} else {
|
||||||
// Notify the user
|
// Notify the user
|
||||||
|
@ -754,6 +659,10 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleRegisterDummy() {
|
||||||
|
handleRegisterAction(RegisterAction.RegisterDummy)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun onSessionCreated(session: Session, isAccountCreated: Boolean) {
|
private suspend fun onSessionCreated(session: Session, isAccountCreated: Boolean) {
|
||||||
val state = awaitState()
|
val state = awaitState()
|
||||||
state.useCase?.let { useCase ->
|
state.useCase?.let { useCase ->
|
||||||
|
@ -1006,6 +915,10 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
private fun completePersonalization() {
|
private fun completePersonalization() {
|
||||||
_viewEvents.post(OnboardingViewEvents.OnPersonalizationComplete)
|
_viewEvents.post(OnboardingViewEvents.OnPersonalizationComplete)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun cancelWaitForEmailValidation() {
|
||||||
|
currentJob = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LoginMode.supportsSignModeScreen(): Boolean {
|
private fun LoginMode.supportsSignModeScreen(): Boolean {
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.onboarding
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class RegistrationActionHandler @Inject constructor() {
|
||||||
|
|
||||||
|
suspend fun handleRegisterAction(registrationWizard: RegistrationWizard, action: RegisterAction): RegistrationResult {
|
||||||
|
return when (action) {
|
||||||
|
RegisterAction.StartRegistration -> registrationWizard.getRegistrationFlow()
|
||||||
|
is RegisterAction.CaptchaDone -> registrationWizard.performReCaptcha(action.captchaResponse)
|
||||||
|
is RegisterAction.AcceptTerms -> registrationWizard.acceptTerms()
|
||||||
|
is RegisterAction.RegisterDummy -> registrationWizard.dummy()
|
||||||
|
is RegisterAction.AddThreePid -> registrationWizard.addThreePid(action.threePid)
|
||||||
|
is RegisterAction.SendAgainThreePid -> registrationWizard.sendAgainThreePid()
|
||||||
|
is RegisterAction.ValidateThreePid -> registrationWizard.handleValidateThreePid(action.code)
|
||||||
|
is RegisterAction.CheckIfEmailHasBeenValidated -> registrationWizard.checkIfEmailHasBeenValidated(action.delayMillis)
|
||||||
|
is RegisterAction.CreateAccount -> registrationWizard.createAccount(action.username, action.password, action.initialDeviceName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface RegisterAction {
|
||||||
|
object StartRegistration : RegisterAction
|
||||||
|
data class CreateAccount(val username: String, val password: String, val initialDeviceName: String) : RegisterAction
|
||||||
|
|
||||||
|
data class AddThreePid(val threePid: RegisterThreePid) : RegisterAction
|
||||||
|
object SendAgainThreePid : RegisterAction
|
||||||
|
|
||||||
|
// TODO Confirm Email (from link in the email, open in the phone, intercepted by the app)
|
||||||
|
data class ValidateThreePid(val code: String) : RegisterAction
|
||||||
|
|
||||||
|
data class CheckIfEmailHasBeenValidated(val delayMillis: Long) : RegisterAction
|
||||||
|
|
||||||
|
data class CaptchaDone(val captchaResponse: String) : RegisterAction
|
||||||
|
object AcceptTerms : RegisterAction
|
||||||
|
object RegisterDummy : RegisterAction
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RegisterAction.ignoresResult() = when (this) {
|
||||||
|
is RegisterAction.AddThreePid -> true
|
||||||
|
is RegisterAction.SendAgainThreePid -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RegisterAction.hasLoadingState() = when (this) {
|
||||||
|
is RegisterAction.CheckIfEmailHasBeenValidated -> false
|
||||||
|
else -> true
|
||||||
|
}
|
|
@ -39,6 +39,7 @@ import im.vector.app.databinding.FragmentLoginCaptchaBinding
|
||||||
import im.vector.app.features.login.JavascriptResponse
|
import im.vector.app.features.login.JavascriptResponse
|
||||||
import im.vector.app.features.onboarding.OnboardingAction
|
import im.vector.app.features.onboarding.OnboardingAction
|
||||||
import im.vector.app.features.onboarding.OnboardingViewState
|
import im.vector.app.features.onboarding.OnboardingViewState
|
||||||
|
import im.vector.app.features.onboarding.RegisterAction
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -181,7 +182,7 @@ class FtueAuthCaptchaFragment @Inject constructor(
|
||||||
|
|
||||||
val response = javascriptResponse?.response
|
val response = javascriptResponse?.response
|
||||||
if (javascriptResponse?.action == "verifyCallback" && response != null) {
|
if (javascriptResponse?.action == "verifyCallback" && response != null) {
|
||||||
viewModel.handle(OnboardingAction.CaptchaDone(response))
|
viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.CaptchaDone(response)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -37,6 +37,7 @@ import im.vector.app.databinding.FragmentLoginGenericTextInputFormBinding
|
||||||
import im.vector.app.features.login.TextInputFormFragmentMode
|
import im.vector.app.features.login.TextInputFormFragmentMode
|
||||||
import im.vector.app.features.onboarding.OnboardingAction
|
import im.vector.app.features.onboarding.OnboardingAction
|
||||||
import im.vector.app.features.onboarding.OnboardingViewEvents
|
import im.vector.app.features.onboarding.OnboardingViewEvents
|
||||||
|
import im.vector.app.features.onboarding.RegisterAction
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
@ -138,7 +139,7 @@ class FtueAuthGenericTextInputFormFragment @Inject constructor() : AbstractFtueA
|
||||||
private fun onOtherButtonClicked() {
|
private fun onOtherButtonClicked() {
|
||||||
when (params.mode) {
|
when (params.mode) {
|
||||||
TextInputFormFragmentMode.ConfirmMsisdn -> {
|
TextInputFormFragmentMode.ConfirmMsisdn -> {
|
||||||
viewModel.handle(OnboardingAction.SendAgainThreePid)
|
viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.SendAgainThreePid))
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// Should not happen, button is not displayed
|
// Should not happen, button is not displayed
|
||||||
|
@ -152,19 +153,19 @@ class FtueAuthGenericTextInputFormFragment @Inject constructor() : AbstractFtueA
|
||||||
|
|
||||||
if (text.isEmpty()) {
|
if (text.isEmpty()) {
|
||||||
// Perform dummy action
|
// Perform dummy action
|
||||||
viewModel.handle(OnboardingAction.RegisterDummy)
|
viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.RegisterDummy))
|
||||||
} else {
|
} else {
|
||||||
when (params.mode) {
|
when (params.mode) {
|
||||||
TextInputFormFragmentMode.SetEmail -> {
|
TextInputFormFragmentMode.SetEmail -> {
|
||||||
viewModel.handle(OnboardingAction.AddThreePid(RegisterThreePid.Email(text)))
|
viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.AddThreePid(RegisterThreePid.Email(text))))
|
||||||
}
|
}
|
||||||
TextInputFormFragmentMode.SetMsisdn -> {
|
TextInputFormFragmentMode.SetMsisdn -> {
|
||||||
getCountryCodeOrShowError(text)?.let { countryCode ->
|
getCountryCodeOrShowError(text)?.let { countryCode ->
|
||||||
viewModel.handle(OnboardingAction.AddThreePid(RegisterThreePid.Msisdn(text, countryCode)))
|
viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.AddThreePid(RegisterThreePid.Msisdn(text, countryCode))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TextInputFormFragmentMode.ConfirmMsisdn -> {
|
TextInputFormFragmentMode.ConfirmMsisdn -> {
|
||||||
viewModel.handle(OnboardingAction.ValidateThreePid(text))
|
viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.ValidateThreePid(text)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import com.airbnb.mvrx.args
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.databinding.FragmentLoginWaitForEmailBinding
|
import im.vector.app.databinding.FragmentLoginWaitForEmailBinding
|
||||||
import im.vector.app.features.onboarding.OnboardingAction
|
import im.vector.app.features.onboarding.OnboardingAction
|
||||||
|
import im.vector.app.features.onboarding.RegisterAction
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.matrix.android.sdk.api.failure.is401
|
import org.matrix.android.sdk.api.failure.is401
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -54,7 +55,7 @@ class FtueAuthWaitForEmailFragment @Inject constructor() : AbstractFtueAuthFragm
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
viewModel.handle(OnboardingAction.CheckIfEmailHasBeenValidated(0))
|
viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.CheckIfEmailHasBeenValidated(0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
|
@ -70,7 +71,7 @@ class FtueAuthWaitForEmailFragment @Inject constructor() : AbstractFtueAuthFragm
|
||||||
override fun onError(throwable: Throwable) {
|
override fun onError(throwable: Throwable) {
|
||||||
if (throwable.is401()) {
|
if (throwable.is401()) {
|
||||||
// Try again, with a delay
|
// Try again, with a delay
|
||||||
viewModel.handle(OnboardingAction.CheckIfEmailHasBeenValidated(10_000))
|
viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.CheckIfEmailHasBeenValidated(10_000)))
|
||||||
} else {
|
} else {
|
||||||
super.onError(throwable)
|
super.onError(throwable)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import im.vector.app.features.login.terms.LoginTermsViewState
|
||||||
import im.vector.app.features.login.terms.PolicyController
|
import im.vector.app.features.login.terms.PolicyController
|
||||||
import im.vector.app.features.onboarding.OnboardingAction
|
import im.vector.app.features.onboarding.OnboardingAction
|
||||||
import im.vector.app.features.onboarding.OnboardingViewState
|
import im.vector.app.features.onboarding.OnboardingViewState
|
||||||
|
import im.vector.app.features.onboarding.RegisterAction
|
||||||
import im.vector.app.features.onboarding.ftueauth.AbstractFtueAuthFragment
|
import im.vector.app.features.onboarding.ftueauth.AbstractFtueAuthFragment
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.matrix.android.sdk.internal.auth.registration.LocalizedFlowDataLoginTerms
|
import org.matrix.android.sdk.internal.auth.registration.LocalizedFlowDataLoginTerms
|
||||||
|
@ -111,7 +112,7 @@ class FtueAuthTermsFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun submit() {
|
private fun submit() {
|
||||||
viewModel.handle(OnboardingAction.AcceptTerms)
|
viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.AcceptTerms))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateWithState(state: OnboardingViewState) {
|
override fun updateWithState(state: OnboardingViewState) {
|
||||||
|
|
|
@ -23,12 +23,14 @@ import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
import com.airbnb.mvrx.test.MvRxTestRule
|
import com.airbnb.mvrx.test.MvRxTestRule
|
||||||
import im.vector.app.features.login.ReAuthHelper
|
import im.vector.app.features.login.ReAuthHelper
|
||||||
|
import im.vector.app.features.login.SignMode
|
||||||
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||||
import im.vector.app.test.fakes.FakeAnalyticsTracker
|
import im.vector.app.test.fakes.FakeAnalyticsTracker
|
||||||
import im.vector.app.test.fakes.FakeAuthenticationService
|
import im.vector.app.test.fakes.FakeAuthenticationService
|
||||||
import im.vector.app.test.fakes.FakeContext
|
import im.vector.app.test.fakes.FakeContext
|
||||||
import im.vector.app.test.fakes.FakeHomeServerConnectionConfigFactory
|
import im.vector.app.test.fakes.FakeHomeServerConnectionConfigFactory
|
||||||
import im.vector.app.test.fakes.FakeHomeServerHistoryService
|
import im.vector.app.test.fakes.FakeHomeServerHistoryService
|
||||||
|
import im.vector.app.test.fakes.FakeRegisterActionHandler
|
||||||
import im.vector.app.test.fakes.FakeRegistrationWizard
|
import im.vector.app.test.fakes.FakeRegistrationWizard
|
||||||
import im.vector.app.test.fakes.FakeSession
|
import im.vector.app.test.fakes.FakeSession
|
||||||
import im.vector.app.test.fakes.FakeStringProvider
|
import im.vector.app.test.fakes.FakeStringProvider
|
||||||
|
@ -36,20 +38,27 @@ import im.vector.app.test.fakes.FakeUri
|
||||||
import im.vector.app.test.fakes.FakeUriFilenameResolver
|
import im.vector.app.test.fakes.FakeUriFilenameResolver
|
||||||
import im.vector.app.test.fakes.FakeVectorFeatures
|
import im.vector.app.test.fakes.FakeVectorFeatures
|
||||||
import im.vector.app.test.fakes.FakeVectorOverrides
|
import im.vector.app.test.fakes.FakeVectorOverrides
|
||||||
|
import im.vector.app.test.fixtures.aHomeServerCapabilities
|
||||||
import im.vector.app.test.test
|
import im.vector.app.test.test
|
||||||
import kotlinx.coroutines.test.runBlockingTest
|
import kotlinx.coroutines.test.runBlockingTest
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.FlowResult
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.Stage
|
||||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
||||||
|
|
||||||
private const val A_DISPLAY_NAME = "a display name"
|
private const val A_DISPLAY_NAME = "a display name"
|
||||||
private const val A_PICTURE_FILENAME = "a-picture.png"
|
private const val A_PICTURE_FILENAME = "a-picture.png"
|
||||||
private val AN_ERROR = RuntimeException("an error!")
|
private val AN_ERROR = RuntimeException("an error!")
|
||||||
private val AN_UNSUPPORTED_PERSONALISATION_STATE = PersonalizationState(
|
private val A_LOADABLE_REGISTER_ACTION = RegisterAction.StartRegistration
|
||||||
supportsChangingDisplayName = false,
|
private val A_NON_LOADABLE_REGISTER_ACTION = RegisterAction.CheckIfEmailHasBeenValidated(delayMillis = -1L)
|
||||||
supportsChangingProfilePicture = false
|
private val A_RESULT_IGNORED_REGISTER_ACTION = RegisterAction.AddThreePid(RegisterThreePid.Email("an email"))
|
||||||
)
|
private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canChangeDisplayName = true, canChangeAvatar = true)
|
||||||
|
private val AN_IGNORED_FLOW_RESULT = FlowResult(missingStages = emptyList(), completedStages = emptyList())
|
||||||
|
private val ANY_CONTINUING_REGISTRATION_RESULT = RegistrationResult.FlowResponse(AN_IGNORED_FLOW_RESULT)
|
||||||
|
|
||||||
class OnboardingViewModelTest {
|
class OnboardingViewModelTest {
|
||||||
|
|
||||||
|
@ -63,6 +72,7 @@ class OnboardingViewModelTest {
|
||||||
private val fakeUriFilenameResolver = FakeUriFilenameResolver()
|
private val fakeUriFilenameResolver = FakeUriFilenameResolver()
|
||||||
private val fakeActiveSessionHolder = FakeActiveSessionHolder(fakeSession)
|
private val fakeActiveSessionHolder = FakeActiveSessionHolder(fakeSession)
|
||||||
private val fakeAuthenticationService = FakeAuthenticationService()
|
private val fakeAuthenticationService = FakeAuthenticationService()
|
||||||
|
private val fakeRegisterActionHandler = FakeRegisterActionHandler()
|
||||||
|
|
||||||
lateinit var viewModel: OnboardingViewModel
|
lateinit var viewModel: OnboardingViewModel
|
||||||
|
|
||||||
|
@ -72,7 +82,7 @@ class OnboardingViewModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `when handling PostViewEvent then emits contents as view event`() = runBlockingTest {
|
fun `when handling PostViewEvent, then emits contents as view event`() = runBlockingTest {
|
||||||
val test = viewModel.test(this)
|
val test = viewModel.test(this)
|
||||||
|
|
||||||
viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnTakeMeHome))
|
viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnTakeMeHome))
|
||||||
|
@ -83,7 +93,7 @@ class OnboardingViewModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given supports changing display name when handling PersonalizeProfile then emits contents choose display name`() = runBlockingTest {
|
fun `given supports changing display name, when handling PersonalizeProfile, then emits contents choose display name`() = runBlockingTest {
|
||||||
val initialState = initialState.copy(personalizationState = PersonalizationState(supportsChangingDisplayName = true, supportsChangingProfilePicture = false))
|
val initialState = initialState.copy(personalizationState = PersonalizationState(supportsChangingDisplayName = true, supportsChangingProfilePicture = false))
|
||||||
viewModel = createViewModel(initialState)
|
viewModel = createViewModel(initialState)
|
||||||
val test = viewModel.test(this)
|
val test = viewModel.test(this)
|
||||||
|
@ -96,7 +106,7 @@ class OnboardingViewModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given only supports changing profile picture when handling PersonalizeProfile then emits contents choose profile picture`() = runBlockingTest {
|
fun `given only supports changing profile picture, when handling PersonalizeProfile, then emits contents choose profile picture`() = runBlockingTest {
|
||||||
val initialState = initialState.copy(personalizationState = PersonalizationState(supportsChangingDisplayName = false, supportsChangingProfilePicture = true))
|
val initialState = initialState.copy(personalizationState = PersonalizationState(supportsChangingDisplayName = false, supportsChangingProfilePicture = true))
|
||||||
viewModel = createViewModel(initialState)
|
viewModel = createViewModel(initialState)
|
||||||
val test = viewModel.test(this)
|
val test = viewModel.test(this)
|
||||||
|
@ -109,34 +119,109 @@ class OnboardingViewModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given homeserver does not support personalisation when registering account then updates state and emits account created event`() = runBlockingTest {
|
fun `when handling SignUp then sets sign mode to sign up and starts registration`() = runBlockingTest {
|
||||||
fakeSession.fakeHomeServerCapabilitiesService.givenCapabilities(HomeServerCapabilities(canChangeDisplayName = false, canChangeAvatar = false))
|
givenRegistrationResultFor(RegisterAction.StartRegistration, ANY_CONTINUING_REGISTRATION_RESULT)
|
||||||
givenSuccessfullyCreatesAccount()
|
|
||||||
val test = viewModel.test(this)
|
val test = viewModel.test(this)
|
||||||
|
|
||||||
viewModel.handle(OnboardingAction.RegisterDummy)
|
viewModel.handle(OnboardingAction.UpdateSignMode(SignMode.SignUp))
|
||||||
|
|
||||||
test
|
test
|
||||||
.assertStates(
|
.assertStatesChanges(
|
||||||
initialState,
|
initialState,
|
||||||
initialState.copy(asyncRegistration = Loading()),
|
{ copy(signMode = SignMode.SignUp) },
|
||||||
initialState.copy(
|
{ copy(asyncRegistration = Loading()) },
|
||||||
asyncLoginAction = Success(Unit),
|
{ copy(asyncRegistration = Uninitialized) }
|
||||||
asyncRegistration = Loading(),
|
)
|
||||||
personalizationState = AN_UNSUPPORTED_PERSONALISATION_STATE
|
.assertEvents(OnboardingViewEvents.RegistrationFlowResult(ANY_CONTINUING_REGISTRATION_RESULT.flowResult, isRegistrationStarted = true))
|
||||||
),
|
.finish()
|
||||||
initialState.copy(
|
}
|
||||||
asyncLoginAction = Success(Unit),
|
|
||||||
asyncRegistration = Uninitialized,
|
@Test
|
||||||
personalizationState = AN_UNSUPPORTED_PERSONALISATION_STATE
|
fun `given register action requires more steps, when handling action, then posts next steps`() = runBlockingTest {
|
||||||
)
|
val test = viewModel.test(this)
|
||||||
|
givenRegistrationResultFor(A_LOADABLE_REGISTER_ACTION, ANY_CONTINUING_REGISTRATION_RESULT)
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.PostRegisterAction(A_LOADABLE_REGISTER_ACTION))
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(asyncRegistration = Loading()) },
|
||||||
|
{ copy(asyncRegistration = Uninitialized) }
|
||||||
|
)
|
||||||
|
.assertEvents(OnboardingViewEvents.RegistrationFlowResult(ANY_CONTINUING_REGISTRATION_RESULT.flowResult, isRegistrationStarted = true))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given register action is non loadable, when handling action, then posts next steps without loading`() = runBlockingTest {
|
||||||
|
val test = viewModel.test(this)
|
||||||
|
givenRegistrationResultFor(A_NON_LOADABLE_REGISTER_ACTION, ANY_CONTINUING_REGISTRATION_RESULT)
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.PostRegisterAction(A_NON_LOADABLE_REGISTER_ACTION))
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertState(initialState)
|
||||||
|
.assertEvents(OnboardingViewEvents.RegistrationFlowResult(ANY_CONTINUING_REGISTRATION_RESULT.flowResult, isRegistrationStarted = true))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given register action ignores result, when handling action, then does nothing on success`() = runBlockingTest {
|
||||||
|
val test = viewModel.test(this)
|
||||||
|
givenRegistrationResultFor(A_RESULT_IGNORED_REGISTER_ACTION, RegistrationResult.FlowResponse(AN_IGNORED_FLOW_RESULT))
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.PostRegisterAction(A_RESULT_IGNORED_REGISTER_ACTION))
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(asyncRegistration = Loading()) },
|
||||||
|
{ copy(asyncRegistration = Uninitialized) }
|
||||||
|
)
|
||||||
|
.assertNoEvents()
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when registering account, then updates state and emits account created event`() = runBlockingTest {
|
||||||
|
givenRegistrationResultFor(A_LOADABLE_REGISTER_ACTION, RegistrationResult.Success(fakeSession))
|
||||||
|
givenSuccessfullyCreatesAccount(A_HOMESERVER_CAPABILITIES)
|
||||||
|
val test = viewModel.test(this)
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.PostRegisterAction(A_LOADABLE_REGISTER_ACTION))
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(asyncRegistration = Loading()) },
|
||||||
|
{ copy(asyncLoginAction = Success(Unit), personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState()) },
|
||||||
|
{ copy(asyncLoginAction = Success(Unit), asyncRegistration = Uninitialized) }
|
||||||
)
|
)
|
||||||
.assertEvents(OnboardingViewEvents.OnAccountCreated)
|
.assertEvents(OnboardingViewEvents.OnAccountCreated)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given changing profile picture is supported when updating display name then updates upstream user display name and moves to choose profile picture`() = runBlockingTest {
|
fun `given registration has started and has dummy step to do, when handling action, then ignores other steps and executes dummy`() = runBlockingTest {
|
||||||
|
givenSuccessfulRegistrationForStartAndDummySteps(missingStages = listOf(Stage.Dummy(mandatory = true)))
|
||||||
|
val test = viewModel.test(this)
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.PostRegisterAction(A_LOADABLE_REGISTER_ACTION))
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(asyncRegistration = Loading()) },
|
||||||
|
{ copy(asyncLoginAction = Success(Unit), personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState()) },
|
||||||
|
{ copy(asyncRegistration = Uninitialized) }
|
||||||
|
)
|
||||||
|
.assertEvents(OnboardingViewEvents.OnAccountCreated)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given changing profile picture is supported, when updating display name, then updates upstream user display name and moves to choose profile picture`() = runBlockingTest {
|
||||||
val personalisedInitialState = initialState.copy(personalizationState = PersonalizationState(supportsChangingProfilePicture = true))
|
val personalisedInitialState = initialState.copy(personalizationState = PersonalizationState(supportsChangingProfilePicture = true))
|
||||||
viewModel = createViewModel(personalisedInitialState)
|
viewModel = createViewModel(personalisedInitialState)
|
||||||
val test = viewModel.test(this)
|
val test = viewModel.test(this)
|
||||||
|
@ -144,14 +229,14 @@ class OnboardingViewModelTest {
|
||||||
viewModel.handle(OnboardingAction.UpdateDisplayName(A_DISPLAY_NAME))
|
viewModel.handle(OnboardingAction.UpdateDisplayName(A_DISPLAY_NAME))
|
||||||
|
|
||||||
test
|
test
|
||||||
.assertStates(expectedSuccessfulDisplayNameUpdateStates(personalisedInitialState))
|
.assertStatesChanges(personalisedInitialState, expectedSuccessfulDisplayNameUpdateStates())
|
||||||
.assertEvents(OnboardingViewEvents.OnChooseProfilePicture)
|
.assertEvents(OnboardingViewEvents.OnChooseProfilePicture)
|
||||||
.finish()
|
.finish()
|
||||||
fakeSession.fakeProfileService.verifyUpdatedName(fakeSession.myUserId, A_DISPLAY_NAME)
|
fakeSession.fakeProfileService.verifyUpdatedName(fakeSession.myUserId, A_DISPLAY_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given changing profile picture is not supported when updating display name then updates upstream user display name and completes personalization`() = runBlockingTest {
|
fun `given changing profile picture is not supported, when updating display name, then updates upstream user display name and completes personalization`() = runBlockingTest {
|
||||||
val personalisedInitialState = initialState.copy(personalizationState = PersonalizationState(supportsChangingProfilePicture = false))
|
val personalisedInitialState = initialState.copy(personalizationState = PersonalizationState(supportsChangingProfilePicture = false))
|
||||||
viewModel = createViewModel(personalisedInitialState)
|
viewModel = createViewModel(personalisedInitialState)
|
||||||
val test = viewModel.test(this)
|
val test = viewModel.test(this)
|
||||||
|
@ -159,31 +244,31 @@ class OnboardingViewModelTest {
|
||||||
viewModel.handle(OnboardingAction.UpdateDisplayName(A_DISPLAY_NAME))
|
viewModel.handle(OnboardingAction.UpdateDisplayName(A_DISPLAY_NAME))
|
||||||
|
|
||||||
test
|
test
|
||||||
.assertStates(expectedSuccessfulDisplayNameUpdateStates(personalisedInitialState))
|
.assertStatesChanges(personalisedInitialState, expectedSuccessfulDisplayNameUpdateStates())
|
||||||
.assertEvents(OnboardingViewEvents.OnPersonalizationComplete)
|
.assertEvents(OnboardingViewEvents.OnPersonalizationComplete)
|
||||||
.finish()
|
.finish()
|
||||||
fakeSession.fakeProfileService.verifyUpdatedName(fakeSession.myUserId, A_DISPLAY_NAME)
|
fakeSession.fakeProfileService.verifyUpdatedName(fakeSession.myUserId, A_DISPLAY_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given upstream failure when handling display name update then emits failure event`() = runBlockingTest {
|
fun `given upstream failure, when handling display name update, then emits failure event`() = runBlockingTest {
|
||||||
val test = viewModel.test(this)
|
val test = viewModel.test(this)
|
||||||
fakeSession.fakeProfileService.givenSetDisplayNameErrors(AN_ERROR)
|
fakeSession.fakeProfileService.givenSetDisplayNameErrors(AN_ERROR)
|
||||||
|
|
||||||
viewModel.handle(OnboardingAction.UpdateDisplayName(A_DISPLAY_NAME))
|
viewModel.handle(OnboardingAction.UpdateDisplayName(A_DISPLAY_NAME))
|
||||||
|
|
||||||
test
|
test
|
||||||
.assertStates(
|
.assertStatesChanges(
|
||||||
initialState,
|
initialState,
|
||||||
initialState.copy(asyncDisplayName = Loading()),
|
{ copy(asyncDisplayName = Loading()) },
|
||||||
initialState.copy(asyncDisplayName = Fail(AN_ERROR)),
|
{ copy(asyncDisplayName = Fail(AN_ERROR)) },
|
||||||
)
|
)
|
||||||
.assertEvents(OnboardingViewEvents.Failure(AN_ERROR))
|
.assertEvents(OnboardingViewEvents.Failure(AN_ERROR))
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `when handling profile picture selected then updates selected picture state`() = runBlockingTest {
|
fun `when handling profile picture selected, then updates selected picture state`() = runBlockingTest {
|
||||||
val test = viewModel.test(this)
|
val test = viewModel.test(this)
|
||||||
|
|
||||||
viewModel.handle(OnboardingAction.ProfilePictureSelected(fakeUri.instance))
|
viewModel.handle(OnboardingAction.ProfilePictureSelected(fakeUri.instance))
|
||||||
|
@ -198,7 +283,7 @@ class OnboardingViewModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given a selected picture when handling save selected profile picture then updates upstream avatar and completes personalization`() = runBlockingTest {
|
fun `given a selected picture, when handling save selected profile picture, then updates upstream avatar and completes personalization`() = runBlockingTest {
|
||||||
val initialStateWithPicture = givenPictureSelected(fakeUri.instance, A_PICTURE_FILENAME)
|
val initialStateWithPicture = givenPictureSelected(fakeUri.instance, A_PICTURE_FILENAME)
|
||||||
viewModel = createViewModel(initialStateWithPicture)
|
viewModel = createViewModel(initialStateWithPicture)
|
||||||
val test = viewModel.test(this)
|
val test = viewModel.test(this)
|
||||||
|
@ -213,7 +298,7 @@ class OnboardingViewModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given upstream update avatar fails when saving selected profile picture then emits failure event`() = runBlockingTest {
|
fun `given upstream update avatar fails, when saving selected profile picture, then emits failure event`() = runBlockingTest {
|
||||||
fakeSession.fakeProfileService.givenUpdateAvatarErrors(AN_ERROR)
|
fakeSession.fakeProfileService.givenUpdateAvatarErrors(AN_ERROR)
|
||||||
val initialStateWithPicture = givenPictureSelected(fakeUri.instance, A_PICTURE_FILENAME)
|
val initialStateWithPicture = givenPictureSelected(fakeUri.instance, A_PICTURE_FILENAME)
|
||||||
viewModel = createViewModel(initialStateWithPicture)
|
viewModel = createViewModel(initialStateWithPicture)
|
||||||
|
@ -228,7 +313,7 @@ class OnboardingViewModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given no selected picture when saving selected profile picture then emits failure event`() = runBlockingTest {
|
fun `given no selected picture, when saving selected profile picture, then emits failure event`() = runBlockingTest {
|
||||||
val test = viewModel.test(this)
|
val test = viewModel.test(this)
|
||||||
|
|
||||||
viewModel.handle(OnboardingAction.SaveSelectedProfilePicture)
|
viewModel.handle(OnboardingAction.SaveSelectedProfilePicture)
|
||||||
|
@ -240,7 +325,7 @@ class OnboardingViewModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `when handling profile picture skipped then completes personalization`() = runBlockingTest {
|
fun `when handling profile skipped, then completes personalization`() = runBlockingTest {
|
||||||
val test = viewModel.test(this)
|
val test = viewModel.test(this)
|
||||||
|
|
||||||
viewModel.handle(OnboardingAction.UpdateProfilePictureSkipped)
|
viewModel.handle(OnboardingAction.UpdateProfilePictureSkipped)
|
||||||
|
@ -264,6 +349,7 @@ class OnboardingViewModelTest {
|
||||||
FakeVectorFeatures(),
|
FakeVectorFeatures(),
|
||||||
FakeAnalyticsTracker(),
|
FakeAnalyticsTracker(),
|
||||||
fakeUriFilenameResolver.instance,
|
fakeUriFilenameResolver.instance,
|
||||||
|
fakeRegisterActionHandler.instance,
|
||||||
FakeVectorOverrides()
|
FakeVectorOverrides()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -286,22 +372,42 @@ class OnboardingViewModelTest {
|
||||||
state.copy(asyncProfilePicture = Fail(cause))
|
state.copy(asyncProfilePicture = Fail(cause))
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun givenSuccessfullyCreatesAccount() {
|
private fun expectedSuccessfulDisplayNameUpdateStates(): List<OnboardingViewState.() -> OnboardingViewState> {
|
||||||
|
return listOf(
|
||||||
|
{ copy(asyncDisplayName = Loading()) },
|
||||||
|
{ copy(asyncDisplayName = Success(Unit), personalizationState = personalizationState.copy(displayName = A_DISPLAY_NAME)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun givenSuccessfulRegistrationForStartAndDummySteps(missingStages: List<Stage>) {
|
||||||
|
val flowResult = FlowResult(missingStages = missingStages, completedStages = emptyList())
|
||||||
|
givenRegistrationResultsFor(listOf(
|
||||||
|
A_LOADABLE_REGISTER_ACTION to RegistrationResult.FlowResponse(flowResult),
|
||||||
|
RegisterAction.RegisterDummy to RegistrationResult.Success(fakeSession)
|
||||||
|
))
|
||||||
|
givenSuccessfullyCreatesAccount(A_HOMESERVER_CAPABILITIES)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun givenSuccessfullyCreatesAccount(homeServerCapabilities: HomeServerCapabilities) {
|
||||||
|
fakeSession.fakeHomeServerCapabilitiesService.givenCapabilities(homeServerCapabilities)
|
||||||
fakeActiveSessionHolder.expectSetsActiveSession(fakeSession)
|
fakeActiveSessionHolder.expectSetsActiveSession(fakeSession)
|
||||||
val registrationWizard = FakeRegistrationWizard().also { it.givenSuccessfulDummy(fakeSession) }
|
|
||||||
fakeAuthenticationService.givenRegistrationWizard(registrationWizard)
|
|
||||||
fakeAuthenticationService.expectReset()
|
fakeAuthenticationService.expectReset()
|
||||||
fakeSession.expectStartsSyncing()
|
fakeSession.expectStartsSyncing()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun expectedSuccessfulDisplayNameUpdateStates(personalisedInitialState: OnboardingViewState): List<OnboardingViewState> {
|
private fun givenRegistrationResultFor(action: RegisterAction, result: RegistrationResult) {
|
||||||
return listOf(
|
givenRegistrationResultsFor(listOf(action to result))
|
||||||
personalisedInitialState,
|
}
|
||||||
personalisedInitialState.copy(asyncDisplayName = Loading()),
|
|
||||||
personalisedInitialState.copy(
|
private fun givenRegistrationResultsFor(results: List<Pair<RegisterAction, RegistrationResult>>) {
|
||||||
asyncDisplayName = Success(Unit),
|
fakeAuthenticationService.givenRegistrationStarted(true)
|
||||||
personalizationState = personalisedInitialState.personalizationState.copy(displayName = A_DISPLAY_NAME)
|
val registrationWizard = FakeRegistrationWizard()
|
||||||
)
|
fakeAuthenticationService.givenRegistrationWizard(registrationWizard)
|
||||||
)
|
fakeRegisterActionHandler.givenResultsFor(registrationWizard, results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun HomeServerCapabilities.toPersonalisationState() = PersonalizationState(
|
||||||
|
supportsChangingDisplayName = canChangeDisplayName,
|
||||||
|
supportsChangingProfilePicture = canChangeAvatar
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.onboarding
|
||||||
|
|
||||||
|
import im.vector.app.test.fakes.FakeRegistrationWizard
|
||||||
|
import im.vector.app.test.fakes.FakeSession
|
||||||
|
import io.mockk.coVerifyAll
|
||||||
|
import kotlinx.coroutines.test.runBlockingTest
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
||||||
|
|
||||||
|
private val A_SESSION = FakeSession()
|
||||||
|
private val AN_EXPECTED_RESULT = RegistrationResult.Success(A_SESSION)
|
||||||
|
private const val A_USERNAME = "a username"
|
||||||
|
private const val A_PASSWORD = "a password"
|
||||||
|
private const val AN_INITIAL_DEVICE_NAME = "a device name"
|
||||||
|
private const val A_CAPTCHA_RESPONSE = "a captcha response"
|
||||||
|
private const val A_PID_CODE = "a pid code"
|
||||||
|
private const val EMAIL_VALIDATED_DELAY = 10000L
|
||||||
|
private val A_PID_TO_REGISTER = RegisterThreePid.Email("an email")
|
||||||
|
|
||||||
|
class RegistrationActionHandlerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when handling register action then delegates to wizard`() = runBlockingTest {
|
||||||
|
val cases = listOf(
|
||||||
|
case(RegisterAction.StartRegistration) { getRegistrationFlow() },
|
||||||
|
case(RegisterAction.CaptchaDone(A_CAPTCHA_RESPONSE)) { performReCaptcha(A_CAPTCHA_RESPONSE) },
|
||||||
|
case(RegisterAction.AcceptTerms) { acceptTerms() },
|
||||||
|
case(RegisterAction.RegisterDummy) { dummy() },
|
||||||
|
case(RegisterAction.AddThreePid(A_PID_TO_REGISTER)) { addThreePid(A_PID_TO_REGISTER) },
|
||||||
|
case(RegisterAction.SendAgainThreePid) { sendAgainThreePid() },
|
||||||
|
case(RegisterAction.ValidateThreePid(A_PID_CODE)) { handleValidateThreePid(A_PID_CODE) },
|
||||||
|
case(RegisterAction.CheckIfEmailHasBeenValidated(EMAIL_VALIDATED_DELAY)) { checkIfEmailHasBeenValidated(EMAIL_VALIDATED_DELAY) },
|
||||||
|
case(RegisterAction.CreateAccount(A_USERNAME, A_PASSWORD, AN_INITIAL_DEVICE_NAME)) {
|
||||||
|
createAccount(A_USERNAME, A_PASSWORD, AN_INITIAL_DEVICE_NAME)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
cases.forEach { testSuccessfulActionDelegation(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun testSuccessfulActionDelegation(case: Case) {
|
||||||
|
val registrationActionHandler = RegistrationActionHandler()
|
||||||
|
val fakeRegistrationWizard = FakeRegistrationWizard()
|
||||||
|
fakeRegistrationWizard.givenSuccessFor(result = A_SESSION, case.expect)
|
||||||
|
|
||||||
|
val result = registrationActionHandler.handleRegisterAction(fakeRegistrationWizard, case.action)
|
||||||
|
|
||||||
|
coVerifyAll { case.expect(fakeRegistrationWizard) }
|
||||||
|
result shouldBeEqualTo AN_EXPECTED_RESULT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun case(action: RegisterAction, expect: suspend RegistrationWizard.() -> RegistrationResult) = Case(action, expect)
|
||||||
|
|
||||||
|
private class Case(val action: RegisterAction, val expect: suspend RegistrationWizard.() -> RegistrationResult)
|
|
@ -55,6 +55,25 @@ class ViewModelTest<S, VE>(
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun assertStatesChanges(initial: S, vararg expected: S.() -> S): ViewModelTest<S, VE> {
|
||||||
|
return assertStatesChanges(initial, expected.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the expected states are in the same order as the actual state emissions
|
||||||
|
* Each expected lambda is given the previous expected state, starting with the initial
|
||||||
|
*/
|
||||||
|
fun assertStatesChanges(initial: S, expected: List<S.() -> S>): ViewModelTest<S, VE> {
|
||||||
|
val reducedExpectedStates = expected.fold(mutableListOf(initial)) { acc, curr ->
|
||||||
|
val next = curr.invoke(acc.last())
|
||||||
|
acc.add(next)
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
|
||||||
|
states.assertValues(reducedExpectedStates)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
fun assertStates(expected: List<S>): ViewModelTest<S, VE> {
|
fun assertStates(expected: List<S>): ViewModelTest<S, VE> {
|
||||||
states.assertValues(expected)
|
states.assertValues(expected)
|
||||||
return this
|
return this
|
||||||
|
|
|
@ -23,10 +23,15 @@ import org.matrix.android.sdk.api.auth.AuthenticationService
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
||||||
|
|
||||||
class FakeAuthenticationService : AuthenticationService by mockk() {
|
class FakeAuthenticationService : AuthenticationService by mockk() {
|
||||||
|
|
||||||
fun givenRegistrationWizard(registrationWizard: RegistrationWizard) {
|
fun givenRegistrationWizard(registrationWizard: RegistrationWizard) {
|
||||||
every { getRegistrationWizard() } returns registrationWizard
|
every { getRegistrationWizard() } returns registrationWizard
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun givenRegistrationStarted(started: Boolean) {
|
||||||
|
every { isRegistrationStarted } returns started
|
||||||
|
}
|
||||||
|
|
||||||
fun expectReset() {
|
fun expectReset() {
|
||||||
coJustRun { reset() }
|
coJustRun { reset() }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import im.vector.app.features.onboarding.RegisterAction
|
||||||
|
import im.vector.app.features.onboarding.RegistrationActionHandler
|
||||||
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
||||||
|
|
||||||
|
class FakeRegisterActionHandler {
|
||||||
|
|
||||||
|
val instance = mockk<RegistrationActionHandler>()
|
||||||
|
|
||||||
|
fun givenResultsFor(wizard: RegistrationWizard, result: List<Pair<RegisterAction, RegistrationResult>>) {
|
||||||
|
coEvery { instance.handleRegisterAction(wizard, any()) } answers { call ->
|
||||||
|
val actionArg = call.invocation.args[1] as RegisterAction
|
||||||
|
result.first { it.first == actionArg }.second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,9 +22,9 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
|
||||||
class FakeRegistrationWizard : RegistrationWizard by mockk() {
|
class FakeRegistrationWizard : RegistrationWizard by mockk(relaxed = false) {
|
||||||
|
|
||||||
fun givenSuccessfulDummy(session: Session) {
|
fun givenSuccessFor(result: Session, expect: suspend RegistrationWizard.() -> RegistrationResult) {
|
||||||
coEvery { dummy() } returns RegistrationResult.Success(session)
|
coEvery { expect(this@FakeRegistrationWizard) } returns RegistrationResult.Success(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,5 +23,5 @@ class FakeVectorFeatures : VectorFeatures {
|
||||||
override fun isOnboardingAlreadyHaveAccountSplashEnabled() = true
|
override fun isOnboardingAlreadyHaveAccountSplashEnabled() = true
|
||||||
override fun isOnboardingSplashCarouselEnabled() = true
|
override fun isOnboardingSplashCarouselEnabled() = true
|
||||||
override fun isOnboardingUseCaseEnabled() = true
|
override fun isOnboardingUseCaseEnabled() = true
|
||||||
override fun isOnboardingPersonalizeEnabled() = false
|
override fun isOnboardingPersonalizeEnabled() = true
|
||||||
}
|
}
|
||||||
|
|
40
vector/src/test/java/im/vector/app/test/fixtures/HomeserverCapabilityFixture.kt
vendored
Normal file
40
vector/src/test/java/im/vector/app/test/fixtures/HomeserverCapabilityFixture.kt
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fixtures
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
||||||
|
import org.matrix.android.sdk.api.session.homeserver.RoomVersionCapabilities
|
||||||
|
|
||||||
|
fun aHomeServerCapabilities(
|
||||||
|
canChangePassword: Boolean = true,
|
||||||
|
canChangeDisplayName: Boolean = true,
|
||||||
|
canChangeAvatar: Boolean = true,
|
||||||
|
canChange3pid: Boolean = true,
|
||||||
|
maxUploadFileSize: Long = 100L,
|
||||||
|
lastVersionIdentityServerSupported: Boolean = false,
|
||||||
|
defaultIdentityServerUrl: String? = null,
|
||||||
|
roomVersions: RoomVersionCapabilities? = null
|
||||||
|
) = HomeServerCapabilities(
|
||||||
|
canChangePassword,
|
||||||
|
canChangeDisplayName,
|
||||||
|
canChangeAvatar,
|
||||||
|
canChange3pid,
|
||||||
|
maxUploadFileSize,
|
||||||
|
lastVersionIdentityServerSupported,
|
||||||
|
defaultIdentityServerUrl,
|
||||||
|
roomVersions
|
||||||
|
)
|
Loading…
Reference in New Issue