From 8fb1f2655ea57a3725d79bca70c082b3b341a894 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 7 Feb 2023 17:34:22 +0100 Subject: [PATCH] Add test for `LoginRootPresenter` --- .../login/root/LoginRootPresenterTest.kt | 135 ++++++++++++++++++ .../auth/FakeAuthenticationService.kt | 22 ++- 2 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 features/login/src/test/kotlin/io/element/android/features/login/root/LoginRootPresenterTest.kt diff --git a/features/login/src/test/kotlin/io/element/android/features/login/root/LoginRootPresenterTest.kt b/features/login/src/test/kotlin/io/element/android/features/login/root/LoginRootPresenterTest.kt new file mode 100644 index 0000000000..ba3b5e644a --- /dev/null +++ b/features/login/src/test/kotlin/io/element/android/features/login/root/LoginRootPresenterTest.kt @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2023 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package io.element.android.features.login.root + +import app.cash.molecule.RecompositionClock +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.core.SessionId +import io.element.android.libraries.matrixtest.auth.A_FAILURE +import io.element.android.libraries.matrixtest.auth.A_HOMESERVER +import io.element.android.libraries.matrixtest.auth.A_HOMESERVER_2 +import io.element.android.libraries.matrixtest.auth.A_LOGIN +import io.element.android.libraries.matrixtest.auth.A_PASSWORD +import io.element.android.libraries.matrixtest.auth.A_SESSION_ID +import io.element.android.libraries.matrixtest.auth.FakeAuthenticationService +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class LoginRootPresenterTest { + @Test + fun `present - initial state`() = runTest { + val presenter = LoginRootPresenter( + FakeAuthenticationService(), + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.homeserver).isEqualTo(A_HOMESERVER) + assertThat(initialState.loggedInState).isEqualTo(LoggedInState.NotLoggedIn) + assertThat(initialState.formState).isEqualTo(LoginFormState.Default) + assertThat(initialState.submitEnabled).isFalse() + } + } + + @Test + fun `present - enter login and password`() = runTest { + val presenter = LoginRootPresenter( + FakeAuthenticationService(), + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink.invoke(LoginRootEvents.SetLogin(A_LOGIN)) + val loginState = awaitItem() + assertThat(loginState.formState).isEqualTo(LoginFormState(login = A_LOGIN, password = "")) + assertThat(loginState.submitEnabled).isFalse() + initialState.eventSink.invoke(LoginRootEvents.SetPassword(A_PASSWORD)) + val loginAndPasswordState = awaitItem() + assertThat(loginAndPasswordState.formState).isEqualTo(LoginFormState(login = A_LOGIN, password = A_PASSWORD)) + assertThat(loginAndPasswordState.submitEnabled).isTrue() + } + } + + @Test + fun `present - submit`() = runTest { + val presenter = LoginRootPresenter( + FakeAuthenticationService(), + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink.invoke(LoginRootEvents.SetLogin(A_LOGIN)) + initialState.eventSink.invoke(LoginRootEvents.SetPassword(A_PASSWORD)) + skipItems(1) + val loginAndPasswordState = awaitItem() + loginAndPasswordState.eventSink.invoke(LoginRootEvents.Submit) + val submitState = awaitItem() + assertThat(submitState.loggedInState).isEqualTo(LoggedInState.LoggingIn) + val loggedInState = awaitItem() + assertThat(loggedInState.loggedInState).isEqualTo(LoggedInState.LoggedIn(SessionId(A_SESSION_ID))) + } + } + + @Test + fun `present - submit with error`() = runTest { + val authenticationService = FakeAuthenticationService() + val presenter = LoginRootPresenter( + authenticationService, + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink.invoke(LoginRootEvents.SetLogin(A_LOGIN)) + initialState.eventSink.invoke(LoginRootEvents.SetPassword(A_PASSWORD)) + skipItems(1) + val loginAndPasswordState = awaitItem() + authenticationService.givenLoginError(A_FAILURE) + loginAndPasswordState.eventSink.invoke(LoginRootEvents.Submit) + val submitState = awaitItem() + assertThat(submitState.loggedInState).isEqualTo(LoggedInState.LoggingIn) + val loggedInState = awaitItem() + assertThat(loggedInState.loggedInState).isEqualTo(LoggedInState.ErrorLoggingIn(A_FAILURE)) + } + } + + @Test + fun `present - refresh server`() = runTest { + val authenticationService = FakeAuthenticationService() + val presenter = LoginRootPresenter( + authenticationService, + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.homeserver).isEqualTo(A_HOMESERVER) + authenticationService.givenHomeserver(A_HOMESERVER_2) + initialState.eventSink.invoke(LoginRootEvents.RefreshHomeServer) + val refreshedState = awaitItem() + assertThat(refreshedState.homeserver).isEqualTo(A_HOMESERVER_2) + } + } +} diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/auth/FakeAuthenticationService.kt b/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/auth/FakeAuthenticationService.kt index e637ebb722..936bd01545 100644 --- a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/auth/FakeAuthenticationService.kt +++ b/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/auth/FakeAuthenticationService.kt @@ -24,8 +24,16 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf const val A_HOMESERVER = "matrix.org" +const val A_HOMESERVER_2 = "matrix-client.org" +const val A_SESSION_ID = "sessionId" +const val A_LOGIN = "login" +const val A_PASSWORD = "password" +val A_FAILURE = Throwable("error") class FakeAuthenticationService : MatrixAuthenticationService { + private var homeserver: String = A_HOMESERVER + private var loginError: Throwable? = null + override fun isLoggedIn(): Flow { return flowOf(false) } @@ -42,8 +50,12 @@ class FakeAuthenticationService : MatrixAuthenticationService { return null } + fun givenHomeserver(homeserver: String) { + this.homeserver = homeserver + } + override fun getHomeserverOrDefault(): String { - return A_HOMESERVER + return homeserver } override suspend fun setHomeserver(homeserver: String) { @@ -51,6 +63,12 @@ class FakeAuthenticationService : MatrixAuthenticationService { } override suspend fun login(username: String, password: String): SessionId { - return SessionId("test") + delay(100) + loginError?.let { throw it } + return SessionId(A_SESSION_ID) + } + + fun givenLoginError(throwable: Throwable?) { + loginError = throwable } }