Add test for LoginRootPresenter
This commit is contained in:
parent
0b6d7b0bc5
commit
7d161730c0
2 changed files with 155 additions and 2 deletions
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,8 +24,16 @@ import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
||||||
const val A_HOMESERVER = "matrix.org"
|
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 {
|
class FakeAuthenticationService : MatrixAuthenticationService {
|
||||||
|
private var homeserver: String = A_HOMESERVER
|
||||||
|
private var loginError: Throwable? = null
|
||||||
|
|
||||||
override fun isLoggedIn(): Flow<Boolean> {
|
override fun isLoggedIn(): Flow<Boolean> {
|
||||||
return flowOf(false)
|
return flowOf(false)
|
||||||
}
|
}
|
||||||
|
|
@ -42,8 +50,12 @@ class FakeAuthenticationService : MatrixAuthenticationService {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun givenHomeserver(homeserver: String) {
|
||||||
|
this.homeserver = homeserver
|
||||||
|
}
|
||||||
|
|
||||||
override fun getHomeserverOrDefault(): String {
|
override fun getHomeserverOrDefault(): String {
|
||||||
return A_HOMESERVER
|
return homeserver
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun setHomeserver(homeserver: String) {
|
override suspend fun setHomeserver(homeserver: String) {
|
||||||
|
|
@ -51,6 +63,12 @@ class FakeAuthenticationService : MatrixAuthenticationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun login(username: String, password: String): SessionId {
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue