Merge pull request #6668 from vector-im/feature/adm/onboarding-vm-tests
`OnboardingViewModel` tests
This commit is contained in:
commit
7e7b98a3c1
@ -23,7 +23,9 @@ import im.vector.app.R
|
|||||||
import im.vector.app.features.login.LoginConfig
|
import im.vector.app.features.login.LoginConfig
|
||||||
import im.vector.app.features.login.LoginMode
|
import im.vector.app.features.login.LoginMode
|
||||||
import im.vector.app.features.login.ReAuthHelper
|
import im.vector.app.features.login.ReAuthHelper
|
||||||
|
import im.vector.app.features.login.ServerType
|
||||||
import im.vector.app.features.login.SignMode
|
import im.vector.app.features.login.SignMode
|
||||||
|
import im.vector.app.features.onboarding.RegistrationStateFixture.aRegistrationState
|
||||||
import im.vector.app.features.onboarding.StartAuthenticationFlowUseCase.StartAuthenticationResult
|
import im.vector.app.features.onboarding.StartAuthenticationFlowUseCase.StartAuthenticationResult
|
||||||
import im.vector.app.test.TestBuildVersionSdkIntProvider
|
import im.vector.app.test.TestBuildVersionSdkIntProvider
|
||||||
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||||
@ -48,10 +50,12 @@ import im.vector.app.test.fixtures.a401ServerError
|
|||||||
import im.vector.app.test.fixtures.aHomeServerCapabilities
|
import im.vector.app.test.fixtures.aHomeServerCapabilities
|
||||||
import im.vector.app.test.test
|
import im.vector.app.test.test
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
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.data.HomeServerConnectionConfig
|
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||||
|
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
||||||
import org.matrix.android.sdk.api.auth.registration.Stage
|
import org.matrix.android.sdk.api.auth.registration.Stage
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
@ -68,13 +72,22 @@ private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canChangeDisplay
|
|||||||
private val ANY_CONTINUING_REGISTRATION_RESULT = RegistrationActionHandler.Result.NextStage(Stage.Dummy(mandatory = true))
|
private val ANY_CONTINUING_REGISTRATION_RESULT = RegistrationActionHandler.Result.NextStage(Stage.Dummy(mandatory = true))
|
||||||
private val A_DIRECT_LOGIN = OnboardingAction.AuthenticateAction.LoginDirect("@a-user:id.org", "a-password", "a-device-name")
|
private val A_DIRECT_LOGIN = OnboardingAction.AuthenticateAction.LoginDirect("@a-user:id.org", "a-password", "a-device-name")
|
||||||
private const val A_HOMESERVER_URL = "https://edited-homeserver.org"
|
private const val A_HOMESERVER_URL = "https://edited-homeserver.org"
|
||||||
|
private val A_DEFAULT_HOMESERVER_URL = "${R.string.matrix_org_server_url.toTestString()}/"
|
||||||
private val A_HOMESERVER_CONFIG = HomeServerConnectionConfig(FakeUri().instance)
|
private val A_HOMESERVER_CONFIG = HomeServerConnectionConfig(FakeUri().instance)
|
||||||
private val SELECTED_HOMESERVER_STATE = SelectedHomeserverState(preferredLoginMode = LoginMode.Password, userFacingUrl = A_HOMESERVER_URL)
|
private val SELECTED_HOMESERVER_STATE = SelectedHomeserverState(preferredLoginMode = LoginMode.Password, userFacingUrl = A_HOMESERVER_URL)
|
||||||
private val SELECTED_HOMESERVER_STATE_SUPPORTED_LOGOUT_DEVICES = SelectedHomeserverState(isLogoutDevicesSupported = true)
|
private val SELECTED_HOMESERVER_STATE_SUPPORTED_LOGOUT_DEVICES = SelectedHomeserverState(isLogoutDevicesSupported = true)
|
||||||
|
private val DEFAULT_SELECTED_HOMESERVER_STATE = SELECTED_HOMESERVER_STATE.copy(userFacingUrl = A_DEFAULT_HOMESERVER_URL)
|
||||||
private const val AN_EMAIL = "hello@example.com"
|
private const val AN_EMAIL = "hello@example.com"
|
||||||
private const val A_PASSWORD = "a-password"
|
private const val A_PASSWORD = "a-password"
|
||||||
private const val A_USERNAME = "hello-world"
|
private const val A_USERNAME = "hello-world"
|
||||||
|
private const val A_DEVICE_NAME = "a-device-name"
|
||||||
private const val A_MATRIX_ID = "@$A_USERNAME:matrix.org"
|
private const val A_MATRIX_ID = "@$A_USERNAME:matrix.org"
|
||||||
|
private const val A_LOGIN_TOKEN = "a-login-token"
|
||||||
|
private val A_REGISTRATION_STATE = aRegistrationState(email = AN_EMAIL)
|
||||||
|
private const val A_SSO_URL = "https://a-sso.url"
|
||||||
|
private const val A_REDIRECT_URI = "https://a-redirect.uri"
|
||||||
|
private const val A_DEVICE_ID = "a-device-id"
|
||||||
|
private val SSO_REGISTRATION_DESCRIPTION = AuthenticationDescription.Register(AuthenticationDescription.AuthenticationType.SSO)
|
||||||
|
|
||||||
class OnboardingViewModelTest {
|
class OnboardingViewModelTest {
|
||||||
|
|
||||||
@ -103,6 +116,81 @@ class OnboardingViewModelTest {
|
|||||||
viewModelWith(initialState)
|
viewModelWith(initialState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given usecase screen enabled, when handling sign up splash action, then emits OpenUseCaseSelection`() = runTest {
|
||||||
|
val test = viewModel.test()
|
||||||
|
fakeVectorFeatures.givenOnboardingUseCaseEnabled()
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.SplashAction.OnGetStarted(OnboardingFlow.SignUp))
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(onboardingFlow = OnboardingFlow.SignUp) }
|
||||||
|
)
|
||||||
|
.assertEvents(OnboardingViewEvents.OpenUseCaseSelection)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given combined login enabled, when handling sign in splash action, then emits OpenCombinedLogin with default homeserver`() = runTest {
|
||||||
|
val test = viewModel.test()
|
||||||
|
fakeVectorFeatures.givenCombinedLoginEnabled()
|
||||||
|
givenCanSuccessfullyUpdateHomeserver(A_DEFAULT_HOMESERVER_URL, DEFAULT_SELECTED_HOMESERVER_STATE)
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.SplashAction.OnIAlreadyHaveAnAccount(OnboardingFlow.SignIn))
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(onboardingFlow = OnboardingFlow.SignIn) },
|
||||||
|
{ copy(isLoading = true) },
|
||||||
|
{ copy(selectedHomeserver = DEFAULT_SELECTED_HOMESERVER_STATE) },
|
||||||
|
{ copy(signMode = SignMode.SignIn) },
|
||||||
|
{ copy(isLoading = false) }
|
||||||
|
)
|
||||||
|
.assertEvents(OnboardingViewEvents.OpenCombinedLogin)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given can successfully login in with token, when logging in with token, then emits AccountSignedIn`() = runTest {
|
||||||
|
val test = viewModel.test()
|
||||||
|
fakeAuthenticationService.givenLoginWizard(fakeLoginWizard)
|
||||||
|
fakeLoginWizard.givenLoginWithTokenResult(A_LOGIN_TOKEN, fakeSession)
|
||||||
|
givenInitialisesSession(fakeSession)
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.LoginWithToken(A_LOGIN_TOKEN))
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(isLoading = true) },
|
||||||
|
{ copy(isLoading = false) }
|
||||||
|
)
|
||||||
|
.assertEvents(OnboardingViewEvents.OnAccountSignedIn)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given can login with username and password, when logging in, then emits AccountSignedIn`() = runTest {
|
||||||
|
val test = viewModel.test()
|
||||||
|
fakeAuthenticationService.givenLoginWizard(fakeLoginWizard)
|
||||||
|
fakeLoginWizard.givenLoginSuccess(A_USERNAME, A_PASSWORD, A_DEVICE_NAME, fakeSession)
|
||||||
|
givenInitialisesSession(fakeSession)
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.AuthenticateAction.Login(A_USERNAME, A_PASSWORD, A_DEVICE_NAME))
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(isLoading = true) },
|
||||||
|
{ copy(isLoading = false) }
|
||||||
|
)
|
||||||
|
.assertEvents(OnboardingViewEvents.OnAccountSignedIn)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given registration started with currentThreePid, when handling InitWith, then emits restored session OnSendEmailSuccess`() = runTest {
|
fun `given registration started with currentThreePid, when handling InitWith, then emits restored session OnSendEmailSuccess`() = runTest {
|
||||||
val test = viewModel.test()
|
val test = viewModel.test()
|
||||||
@ -704,6 +792,123 @@ class OnboardingViewModelTest {
|
|||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given homeserver state, when resetting homeserver url, then resets auth service and state`() = runTest {
|
||||||
|
viewModelWith(initialState.copy(isLoading = true, selectedHomeserver = SELECTED_HOMESERVER_STATE))
|
||||||
|
val test = viewModel.test()
|
||||||
|
fakeAuthenticationService.expectReset()
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.ResetHomeServerUrl)
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(isLoading = false, selectedHomeserver = SelectedHomeserverState()) },
|
||||||
|
)
|
||||||
|
.assertNoEvents()
|
||||||
|
.finish()
|
||||||
|
fakeAuthenticationService.verifyReset()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given server type, when resetting homeserver type, then resets state`() = runTest {
|
||||||
|
viewModelWith(initialState.copy(serverType = ServerType.EMS))
|
||||||
|
val test = viewModel.test()
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.ResetHomeServerType)
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(serverType = ServerType.Unknown) },
|
||||||
|
)
|
||||||
|
.assertNoEvents()
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given sign mode, when resetting sign mode, then resets state`() = runTest {
|
||||||
|
viewModelWith(initialState.copy(isLoading = true, signMode = SignMode.SignIn))
|
||||||
|
val test = viewModel.test()
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.ResetSignMode)
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(isLoading = false, signMode = SignMode.Unknown) },
|
||||||
|
)
|
||||||
|
.assertNoEvents()
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given registration state, when resetting authentication attempt, then cancels pending logic or registration and resets state`() = runTest {
|
||||||
|
viewModelWith(initialState.copy(isLoading = true, registrationState = A_REGISTRATION_STATE))
|
||||||
|
val test = viewModel.test()
|
||||||
|
fakeAuthenticationService.expectedCancelsPendingLogin()
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.ResetAuthenticationAttempt)
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(isLoading = false, registrationState = RegistrationState()) },
|
||||||
|
)
|
||||||
|
.assertNoEvents()
|
||||||
|
.finish()
|
||||||
|
fakeAuthenticationService.verifyCancelsPendingLogin()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given reset state, when resetting reset state, then resets state`() = runTest {
|
||||||
|
viewModelWith(initialState.copy(isLoading = true, resetState = ResetState(AN_EMAIL)))
|
||||||
|
val test = viewModel.test()
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.ResetResetPassword)
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(isLoading = false, resetState = ResetState()) },
|
||||||
|
)
|
||||||
|
.assertNoEvents()
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given registration state, when resetting user name, then resets state`() = runTest {
|
||||||
|
viewModelWith(initialState.copy(registrationState = A_REGISTRATION_STATE))
|
||||||
|
val test = viewModel.test()
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.ResetSelectedRegistrationUserName)
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(registrationState = RegistrationState()) },
|
||||||
|
)
|
||||||
|
.assertNoEvents()
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given returns Sso url, when fetching Sso url, then updates authentication state and returns supplied Sso url`() = runTest {
|
||||||
|
val test = viewModel.test()
|
||||||
|
val provider = SsoIdentityProvider(id = "provider_id", null, null, null)
|
||||||
|
fakeAuthenticationService.givenSsoUrl(A_REDIRECT_URI, A_DEVICE_ID, provider.id, result = A_SSO_URL)
|
||||||
|
|
||||||
|
val result = viewModel.fetchSsoUrl(A_REDIRECT_URI, A_DEVICE_ID, provider)
|
||||||
|
|
||||||
|
result shouldBeEqualTo A_SSO_URL
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(selectedAuthenticationState = SelectedAuthenticationState(SSO_REGISTRATION_DESCRIPTION)) }
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
private fun viewModelWith(state: OnboardingViewState) {
|
private fun viewModelWith(state: OnboardingViewState) {
|
||||||
OnboardingViewModel(
|
OnboardingViewModel(
|
||||||
state,
|
state,
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
object RegistrationStateFixture {
|
||||||
|
|
||||||
|
fun aRegistrationState(
|
||||||
|
email: String? = null,
|
||||||
|
isUserNameAvailable: Boolean = false,
|
||||||
|
selectedMatrixId: String? = null,
|
||||||
|
) = RegistrationState(email, isUserNameAvailable, selectedMatrixId)
|
||||||
|
}
|
@ -18,6 +18,7 @@ package im.vector.app.test.fakes
|
|||||||
|
|
||||||
import io.mockk.coEvery
|
import io.mockk.coEvery
|
||||||
import io.mockk.coJustRun
|
import io.mockk.coJustRun
|
||||||
|
import io.mockk.coVerify
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import org.matrix.android.sdk.api.auth.AuthenticationService
|
import org.matrix.android.sdk.api.auth.AuthenticationService
|
||||||
@ -68,4 +69,16 @@ class FakeAuthenticationService : AuthenticationService by mockk() {
|
|||||||
fun givenDirectAuthenticationThrows(config: HomeServerConnectionConfig, matrixId: String, password: String, deviceName: String, cause: Throwable) {
|
fun givenDirectAuthenticationThrows(config: HomeServerConnectionConfig, matrixId: String, password: String, deviceName: String, cause: Throwable) {
|
||||||
coEvery { directAuthentication(config, matrixId, password, deviceName) } throws cause
|
coEvery { directAuthentication(config, matrixId, password, deviceName) } throws cause
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun verifyReset() {
|
||||||
|
coVerify { reset() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyCancelsPendingLogin() {
|
||||||
|
coVerify { cancelPendingLoginOrRegistration() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenSsoUrl(redirectUri: String, deviceId: String, providerId: String, result: String) {
|
||||||
|
coEvery { getSsoUrl(redirectUri, deviceId, providerId) } returns result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,12 @@
|
|||||||
|
|
||||||
package im.vector.app.test.fakes
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import io.mockk.coEvery
|
||||||
import io.mockk.coJustRun
|
import io.mockk.coJustRun
|
||||||
import io.mockk.coVerify
|
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
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
|
||||||
class FakeLoginWizard : LoginWizard by mockk() {
|
class FakeLoginWizard : LoginWizard by mockk() {
|
||||||
|
|
||||||
@ -27,6 +29,14 @@ class FakeLoginWizard : LoginWizard by mockk() {
|
|||||||
coJustRun { resetPassword(email) }
|
coJustRun { resetPassword(email) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun givenLoginWithTokenResult(token: String, result: Session) {
|
||||||
|
coEvery { loginWithToken(token) } returns result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenLoginSuccess(username: String, password: String, deviceName: String, result: Session) {
|
||||||
|
coEvery { login(username, password, deviceName) } returns result
|
||||||
|
}
|
||||||
|
|
||||||
fun givenConfirmResetPasswordSuccess(password: String) {
|
fun givenConfirmResetPasswordSuccess(password: String) {
|
||||||
coJustRun { resetPasswordMailConfirmed(password) }
|
coJustRun { resetPasswordMailConfirmed(password) }
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,10 @@ class FakeVectorFeatures : VectorFeatures by spyk<DefaultVectorFeatures>() {
|
|||||||
every { isOnboardingCombinedLoginEnabled() } returns true
|
every { isOnboardingCombinedLoginEnabled() } returns true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun givenOnboardingUseCaseEnabled() {
|
||||||
|
every { isOnboardingUseCaseEnabled() } returns true
|
||||||
|
}
|
||||||
|
|
||||||
fun givenCombinedLoginDisabled() {
|
fun givenCombinedLoginDisabled() {
|
||||||
every { isOnboardingCombinedLoginEnabled() } returns false
|
every { isOnboardingCombinedLoginEnabled() } returns false
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user