Add CreateAccountScreen
This commit is contained in:
parent
8f06c24bd3
commit
849f150138
15 changed files with 544 additions and 13 deletions
|
@ -3,6 +3,7 @@ package app.k9mail.feature.account.setup.domain
|
|||
import app.k9mail.autodiscovery.api.AutoDiscoveryResult
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountOptions
|
||||
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
|
||||
interface DomainContract {
|
||||
|
@ -19,7 +20,7 @@ interface DomainContract {
|
|||
outgoingServerSettings: ServerSettings,
|
||||
authorizationState: String?,
|
||||
options: AccountOptions,
|
||||
): String
|
||||
): AccountCreatorResult
|
||||
}
|
||||
|
||||
fun interface ValidateEmailAddress {
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
package app.k9mail.feature.account.setup.domain.entity
|
||||
|
||||
@JvmInline
|
||||
value class AccountUuid(val value: String)
|
|
@ -18,7 +18,7 @@ class CreateAccount(
|
|||
outgoingServerSettings: ServerSettings,
|
||||
authorizationState: String?,
|
||||
options: AccountOptions,
|
||||
): String {
|
||||
): AccountCreatorResult {
|
||||
val account = Account(
|
||||
uuid = uuidGenerator(),
|
||||
emailAddress = emailAddress,
|
||||
|
@ -28,9 +28,6 @@ class CreateAccount(
|
|||
options = options,
|
||||
)
|
||||
|
||||
return when (val result = accountCreator.createAccount(account)) {
|
||||
is AccountCreatorResult.Success -> result.accountUuid
|
||||
is AccountCreatorResult.Error -> "" // TODO change to meaningful error
|
||||
}
|
||||
return accountCreator.createAccount(account)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import androidx.lifecycle.viewModelScope
|
|||
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
|
||||
import app.k9mail.feature.account.common.domain.AccountDomainContract
|
||||
import app.k9mail.feature.account.common.domain.entity.AuthorizationState
|
||||
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult
|
||||
import app.k9mail.feature.account.setup.domain.DomainContract.UseCase
|
||||
import app.k9mail.feature.account.setup.ui.AccountSetupContract.Effect
|
||||
import app.k9mail.feature.account.setup.ui.AccountSetupContract.Event
|
||||
|
@ -126,7 +127,11 @@ class AccountSetupViewModel(
|
|||
options = accountState.options!!,
|
||||
)
|
||||
|
||||
navigateNext(result)
|
||||
if (result is AccountCreatorResult.Success) {
|
||||
navigateNext(result.accountUuid)
|
||||
} else {
|
||||
error("Creating account failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package app.k9mail.feature.account.setup.ui.createaccount
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingErrorView
|
||||
import app.k9mail.core.ui.compose.designsystem.molecule.ErrorView
|
||||
import app.k9mail.core.ui.compose.designsystem.molecule.LoadingView
|
||||
import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer
|
||||
import app.k9mail.feature.account.common.ui.loadingerror.rememberContentLoadingErrorViewState
|
||||
import app.k9mail.feature.account.common.ui.view.SuccessView
|
||||
import app.k9mail.feature.account.setup.R
|
||||
|
||||
@Composable
|
||||
internal fun CreateAccountContent(
|
||||
state: CreateAccountContract.State,
|
||||
contentPadding: PaddingValues,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ResponsiveWidthContainer(
|
||||
modifier = Modifier
|
||||
.padding(contentPadding)
|
||||
.testTag("CreateAccountContent")
|
||||
.then(modifier),
|
||||
) {
|
||||
ContentLoadingErrorView(
|
||||
state = rememberContentLoadingErrorViewState(state),
|
||||
loading = {
|
||||
LoadingView(
|
||||
message = stringResource(R.string.account_setup_create_account_creating),
|
||||
)
|
||||
},
|
||||
error = {
|
||||
ErrorView(
|
||||
title = stringResource(R.string.account_setup_create_account_error),
|
||||
)
|
||||
},
|
||||
content = {
|
||||
SuccessView(
|
||||
message = stringResource(R.string.account_setup_create_account_created),
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package app.k9mail.feature.account.setup.ui.createaccount
|
||||
|
||||
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
|
||||
import app.k9mail.feature.account.common.ui.loadingerror.LoadingErrorState
|
||||
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult.Error
|
||||
import app.k9mail.feature.account.setup.domain.entity.AccountUuid
|
||||
|
||||
interface CreateAccountContract {
|
||||
|
||||
interface ViewModel : UnidirectionalViewModel<State, Event, Effect>
|
||||
|
||||
data class State(
|
||||
override val isLoading: Boolean = true,
|
||||
override val error: Error? = null,
|
||||
) : LoadingErrorState<Error>
|
||||
|
||||
sealed interface Event {
|
||||
data object CreateAccount : Event
|
||||
data object OnBackClicked : Event
|
||||
}
|
||||
|
||||
sealed interface Effect {
|
||||
data class NavigateNext(val accountUuid: AccountUuid) : Effect
|
||||
data object NavigateBack : Effect
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package app.k9mail.feature.account.setup.ui.createaccount
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.core.ui.compose.common.PreviewDevices
|
||||
import app.k9mail.core.ui.compose.common.mvi.observe
|
||||
import app.k9mail.core.ui.compose.designsystem.template.Scaffold
|
||||
import app.k9mail.core.ui.compose.theme.K9Theme
|
||||
import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
|
||||
import app.k9mail.feature.account.common.ui.AppTitleTopHeader
|
||||
import app.k9mail.feature.account.common.ui.WizardNavigationBar
|
||||
import app.k9mail.feature.account.common.ui.WizardNavigationBarState
|
||||
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult
|
||||
import app.k9mail.feature.account.setup.domain.entity.AccountUuid
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.Effect
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.Event
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.ViewModel
|
||||
|
||||
@Composable
|
||||
internal fun CreateAccountScreen(
|
||||
onNext: (AccountUuid) -> Unit,
|
||||
onBack: () -> Unit,
|
||||
viewModel: ViewModel,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val (state, dispatch) = viewModel.observe { effect ->
|
||||
when (effect) {
|
||||
Effect.NavigateBack -> onBack()
|
||||
is Effect.NavigateNext -> onNext(effect.accountUuid)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = Unit) {
|
||||
dispatch(Event.CreateAccount)
|
||||
}
|
||||
|
||||
BackHandler {
|
||||
dispatch(Event.OnBackClicked)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
AppTitleTopHeader()
|
||||
},
|
||||
bottomBar = {
|
||||
WizardNavigationBar(
|
||||
onNextClick = {},
|
||||
onBackClick = {
|
||||
dispatch(Event.OnBackClicked)
|
||||
},
|
||||
state = WizardNavigationBarState(
|
||||
showNext = false,
|
||||
isBackEnabled = state.value.error != null,
|
||||
),
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
) { innerPadding ->
|
||||
CreateAccountContent(
|
||||
state = state.value,
|
||||
contentPadding = innerPadding,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@PreviewDevices
|
||||
internal fun AccountOptionsScreenK9Preview() {
|
||||
K9Theme {
|
||||
CreateAccountScreen(
|
||||
onNext = {},
|
||||
onBack = {},
|
||||
viewModel = CreateAccountViewModel(
|
||||
createAccount = { _, _, _, _, _ -> AccountCreatorResult.Success("irrelevant") },
|
||||
accountStateRepository = InMemoryAccountStateRepository(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package app.k9mail.feature.account.setup.ui.createaccount
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
|
||||
import app.k9mail.feature.account.common.domain.AccountDomainContract.AccountStateRepository
|
||||
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult
|
||||
import app.k9mail.feature.account.setup.domain.DomainContract.UseCase.CreateAccount
|
||||
import app.k9mail.feature.account.setup.domain.entity.AccountUuid
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.Effect
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.Event
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.State
|
||||
import kotlinx.coroutines.cancelChildren
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
private const val CONTINUE_NEXT_DELAY = 2000L
|
||||
|
||||
class CreateAccountViewModel(
|
||||
private val createAccount: CreateAccount,
|
||||
private val accountStateRepository: AccountStateRepository,
|
||||
initialState: State = State(),
|
||||
) : BaseViewModel<State, Event, Effect>(initialState),
|
||||
CreateAccountContract.ViewModel {
|
||||
|
||||
override fun event(event: Event) {
|
||||
when (event) {
|
||||
Event.CreateAccount -> handleOneTimeEvent(event, ::createAccount)
|
||||
Event.OnBackClicked -> maybeNavigateBack()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAccount() {
|
||||
val accountState = accountStateRepository.getState()
|
||||
|
||||
viewModelScope.launch {
|
||||
val result = createAccount.execute(
|
||||
emailAddress = accountState.emailAddress ?: "",
|
||||
incomingServerSettings = accountState.incomingServerSettings!!,
|
||||
outgoingServerSettings = accountState.outgoingServerSettings!!,
|
||||
authorizationState = accountState.authorizationState?.state,
|
||||
options = accountState.options!!,
|
||||
)
|
||||
|
||||
when (result) {
|
||||
is AccountCreatorResult.Success -> showSuccess(AccountUuid(result.accountUuid))
|
||||
is AccountCreatorResult.Error -> showError(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSuccess(accountUuid: AccountUuid) {
|
||||
updateState {
|
||||
it.copy(
|
||||
isLoading = false,
|
||||
error = null,
|
||||
)
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
delay(CONTINUE_NEXT_DELAY)
|
||||
navigateNext(accountUuid)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showError(error: AccountCreatorResult.Error) {
|
||||
updateState {
|
||||
it.copy(
|
||||
isLoading = false,
|
||||
error = error,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun maybeNavigateBack() {
|
||||
if (!state.value.isLoading) {
|
||||
navigateBack()
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateBack() {
|
||||
viewModelScope.coroutineContext.cancelChildren()
|
||||
emitEffect(Effect.NavigateBack)
|
||||
}
|
||||
|
||||
private fun navigateNext(accountUuid: AccountUuid) {
|
||||
viewModelScope.coroutineContext.cancelChildren()
|
||||
emitEffect(Effect.NavigateNext(accountUuid))
|
||||
}
|
||||
}
|
|
@ -38,4 +38,8 @@
|
|||
<string name="account_setup_options_email_check_frequency_never">Never</string>
|
||||
<string name="account_setup_options_email_display_count_label">Number of messages to display</string>
|
||||
<string name="account_setup_options_show_notifications_label">Show notifications</string>
|
||||
|
||||
<string name="account_setup_create_account_creating">Creating account…</string>
|
||||
<string name="account_setup_create_account_error">An error occurred while trying to create the account</string>
|
||||
<string name="account_setup_create_account_created">Account successfully created</string>
|
||||
</resources>
|
||||
|
|
|
@ -63,7 +63,7 @@ class CreateAccountTest {
|
|||
options,
|
||||
)
|
||||
|
||||
assertThat(result).isEqualTo("uuid")
|
||||
assertThat(result).isEqualTo(AccountCreatorResult.Success("uuid"))
|
||||
assertThat(recordedAccount).isEqualTo(
|
||||
Account(
|
||||
uuid = "uuid",
|
||||
|
|
|
@ -7,6 +7,7 @@ import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
|
|||
import app.k9mail.feature.account.common.domain.entity.AccountOptions
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountState
|
||||
import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
|
||||
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult
|
||||
import app.k9mail.feature.account.setup.ui.AccountSetupContract.Effect
|
||||
import app.k9mail.feature.account.setup.ui.AccountSetupContract.SetupStep
|
||||
import app.k9mail.feature.account.setup.ui.AccountSetupContract.State
|
||||
|
@ -42,7 +43,7 @@ class AccountSetupViewModelTest {
|
|||
createAccountAuthorizationState = authState
|
||||
createAccountOptions = options
|
||||
|
||||
"accountUuid"
|
||||
AccountCreatorResult.Success("accountUuid")
|
||||
},
|
||||
accountStateRepository = accountStateRepository,
|
||||
)
|
||||
|
@ -154,7 +155,7 @@ class AccountSetupViewModelTest {
|
|||
fun `should rewind step state on back event`() = runTest {
|
||||
val initialState = State(setupStep = SetupStep.OPTIONS)
|
||||
val viewModel = AccountSetupViewModel(
|
||||
createAccount = { _, _, _, _, _ -> "accountUuid" },
|
||||
createAccount = { _, _, _, _, _ -> AccountCreatorResult.Success("accountUuid") },
|
||||
accountStateRepository = InMemoryAccountStateRepository(),
|
||||
initialState = initialState,
|
||||
)
|
||||
|
@ -204,7 +205,7 @@ class AccountSetupViewModelTest {
|
|||
isAutomaticConfig = true,
|
||||
)
|
||||
val viewModel = AccountSetupViewModel(
|
||||
createAccount = { _, _, _, _, _ -> "accountUuid" },
|
||||
createAccount = { _, _, _, _, _ -> AccountCreatorResult.Success("accountUuid") },
|
||||
accountStateRepository = InMemoryAccountStateRepository(),
|
||||
initialState = initialState,
|
||||
)
|
||||
|
@ -236,7 +237,7 @@ class AccountSetupViewModelTest {
|
|||
isAutomaticConfig = true,
|
||||
)
|
||||
val viewModel = AccountSetupViewModel(
|
||||
createAccount = { _, _, _, _, _ -> "accountUuid" },
|
||||
createAccount = { _, _, _, _, _ -> AccountCreatorResult.Success("accountUuid") },
|
||||
accountStateRepository = InMemoryAccountStateRepository(),
|
||||
initialState = initialState,
|
||||
)
|
||||
|
@ -268,7 +269,7 @@ class AccountSetupViewModelTest {
|
|||
isAutomaticConfig = true,
|
||||
)
|
||||
val viewModel = AccountSetupViewModel(
|
||||
createAccount = { _, _, _, _, _ -> "accountUuid" },
|
||||
createAccount = { _, _, _, _, _ -> AccountCreatorResult.Success("accountUuid") },
|
||||
accountStateRepository = InMemoryAccountStateRepository(),
|
||||
initialState = initialState,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package app.k9mail.feature.account.setup.ui.createaccount
|
||||
|
||||
import app.k9mail.core.ui.compose.testing.ComposeTest
|
||||
import app.k9mail.core.ui.compose.testing.setContent
|
||||
import app.k9mail.core.ui.compose.theme.K9Theme
|
||||
import app.k9mail.feature.account.setup.domain.entity.AccountUuid
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.Effect
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.State
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.containsExactly
|
||||
import assertk.assertions.isEmpty
|
||||
import assertk.assertions.isEqualTo
|
||||
import kotlin.test.Test
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
||||
class CreateAccountScreenTest : ComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `should delegate navigation effects`() = runTest {
|
||||
val accountUuid = AccountUuid("irrelevant")
|
||||
val initialState = State(
|
||||
isLoading = false,
|
||||
error = null,
|
||||
)
|
||||
val viewModel = FakeCreateAccountViewModel(initialState)
|
||||
val navigateNextArguments = mutableListOf<AccountUuid>()
|
||||
var navigateBackCounter = 0
|
||||
|
||||
setContent {
|
||||
K9Theme {
|
||||
CreateAccountScreen(
|
||||
onNext = { accountUuid -> navigateNextArguments.add(accountUuid) },
|
||||
onBack = { navigateBackCounter++ },
|
||||
viewModel = viewModel,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(navigateNextArguments).isEmpty()
|
||||
assertThat(navigateBackCounter).isEqualTo(0)
|
||||
|
||||
viewModel.effect(Effect.NavigateNext(accountUuid))
|
||||
|
||||
assertThat(navigateNextArguments).containsExactly(accountUuid)
|
||||
assertThat(navigateBackCounter).isEqualTo(0)
|
||||
|
||||
viewModel.effect(Effect.NavigateBack)
|
||||
|
||||
assertThat(navigateNextArguments).containsExactly(accountUuid)
|
||||
assertThat(navigateBackCounter).isEqualTo(1)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
package app.k9mail.feature.account.setup.ui.createaccount
|
||||
|
||||
import app.cash.turbine.testIn
|
||||
import app.k9mail.core.ui.compose.testing.MainDispatcherRule
|
||||
import app.k9mail.core.ui.compose.testing.mvi.eventStateTest
|
||||
import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountOptions
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountState
|
||||
import app.k9mail.feature.account.common.domain.entity.AuthorizationState
|
||||
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult
|
||||
import app.k9mail.feature.account.setup.domain.entity.AccountUuid
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.Effect
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.Event
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.State
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.containsExactly
|
||||
import assertk.assertions.isEqualTo
|
||||
import com.fsck.k9.mail.AuthType
|
||||
import com.fsck.k9.mail.ConnectionSecurity
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
import kotlin.test.Test
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
|
||||
class CreateAccountViewModelTest {
|
||||
|
||||
@get:Rule
|
||||
val mainDispatcherRule = MainDispatcherRule()
|
||||
|
||||
private val fakeCreateAccount = FakeCreateAccount()
|
||||
private val accountStateRepository = InMemoryAccountStateRepository().apply {
|
||||
setState(ACCOUNT_STATE)
|
||||
}
|
||||
private val createAccountViewModel = CreateAccountViewModel(
|
||||
createAccount = fakeCreateAccount,
|
||||
accountStateRepository = accountStateRepository,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `initial state should be loading state`() {
|
||||
assertThat(createAccountViewModel.state.value).isEqualTo(State(isLoading = true, error = null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should change state and emit navigate effect after successfully creating account`() = runTest {
|
||||
val accountUuid = "accountUuid"
|
||||
fakeCreateAccount.result = AccountCreatorResult.Success(accountUuid)
|
||||
|
||||
eventStateTest(
|
||||
viewModel = createAccountViewModel,
|
||||
initialState = State(isLoading = true, error = null),
|
||||
event = Event.CreateAccount,
|
||||
expectedState = State(isLoading = false, error = null),
|
||||
coroutineScope = backgroundScope,
|
||||
)
|
||||
|
||||
assertThat(fakeCreateAccount.recordedInvocations).containsExactly(
|
||||
CreateAccountArguments(
|
||||
emailAddress = EMAIL_ADDRESS,
|
||||
incomingServerSettings = INCOMING_SERVER_SETTINGS,
|
||||
outgoingServerSettings = OUTGOING_SERVER_SETTINGS,
|
||||
authorizationState = AUTHORIZATION_STATE.state,
|
||||
options = ACCOUNT_OPTIONS,
|
||||
),
|
||||
)
|
||||
|
||||
val effectTurbine = createAccountViewModel.effect.testIn(backgroundScope)
|
||||
assertThat(effectTurbine.awaitItem()).isEqualTo(Effect.NavigateNext(AccountUuid(accountUuid)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should change state when creating account has failed`() = runTest {
|
||||
val errorResult = AccountCreatorResult.Error("something went wrong")
|
||||
fakeCreateAccount.result = errorResult
|
||||
|
||||
eventStateTest(
|
||||
viewModel = createAccountViewModel,
|
||||
initialState = State(isLoading = true, error = null),
|
||||
event = Event.CreateAccount,
|
||||
expectedState = State(isLoading = false, error = errorResult),
|
||||
coroutineScope = backgroundScope,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should ignore OnBackClicked event when in loading state`() = runTest {
|
||||
val effectTurbine = createAccountViewModel.effect.testIn(scope = backgroundScope)
|
||||
|
||||
createAccountViewModel.event(Event.OnBackClicked)
|
||||
|
||||
effectTurbine.ensureAllEventsConsumed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should emit NavigateBack effect when OnBackClicked event was received while in success state`() = runTest {
|
||||
fakeCreateAccount.result = AccountCreatorResult.Success("accountUuid")
|
||||
createAccountViewModel.event(Event.CreateAccount)
|
||||
val effectTurbine = createAccountViewModel.effect.testIn(backgroundScope)
|
||||
|
||||
createAccountViewModel.event(Event.OnBackClicked)
|
||||
|
||||
assertThat(effectTurbine.awaitItem()).isEqualTo(Effect.NavigateBack)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should emit NavigateBack effect when OnBackClicked event was received while in error state`() = runTest {
|
||||
fakeCreateAccount.result = AccountCreatorResult.Error("something went wrong")
|
||||
createAccountViewModel.event(Event.CreateAccount)
|
||||
val effectTurbine = createAccountViewModel.effect.testIn(backgroundScope)
|
||||
|
||||
createAccountViewModel.event(Event.OnBackClicked)
|
||||
|
||||
assertThat(effectTurbine.awaitItem()).isEqualTo(Effect.NavigateBack)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val EMAIL_ADDRESS = "test@domain.example"
|
||||
|
||||
private val INCOMING_SERVER_SETTINGS = ServerSettings(
|
||||
"imap",
|
||||
"imap.domain.example",
|
||||
993,
|
||||
ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
AuthType.PLAIN,
|
||||
"username",
|
||||
"password",
|
||||
null,
|
||||
)
|
||||
|
||||
private val OUTGOING_SERVER_SETTINGS = ServerSettings(
|
||||
"smtp",
|
||||
"smtp.domain.example",
|
||||
465,
|
||||
ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
AuthType.PLAIN,
|
||||
"username",
|
||||
"password",
|
||||
null,
|
||||
)
|
||||
|
||||
private val AUTHORIZATION_STATE = AuthorizationState("authorization state")
|
||||
|
||||
private val ACCOUNT_OPTIONS = AccountOptions(
|
||||
accountName = "account name",
|
||||
displayName = "display name",
|
||||
emailSignature = null,
|
||||
checkFrequencyInMinutes = 0,
|
||||
messageDisplayCount = 50,
|
||||
showNotification = false,
|
||||
)
|
||||
|
||||
private val ACCOUNT_STATE = AccountState(
|
||||
emailAddress = EMAIL_ADDRESS,
|
||||
incomingServerSettings = INCOMING_SERVER_SETTINGS,
|
||||
outgoingServerSettings = OUTGOING_SERVER_SETTINGS,
|
||||
authorizationState = AUTHORIZATION_STATE,
|
||||
options = ACCOUNT_OPTIONS,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package app.k9mail.feature.account.setup.ui.createaccount
|
||||
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountOptions
|
||||
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult
|
||||
import app.k9mail.feature.account.setup.domain.DomainContract.UseCase.CreateAccount
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
|
||||
class FakeCreateAccount : CreateAccount {
|
||||
val recordedInvocations = mutableListOf<CreateAccountArguments>()
|
||||
|
||||
var result: AccountCreatorResult = AccountCreatorResult.Success("default result")
|
||||
|
||||
override suspend fun execute(
|
||||
emailAddress: String,
|
||||
incomingServerSettings: ServerSettings,
|
||||
outgoingServerSettings: ServerSettings,
|
||||
authorizationState: String?,
|
||||
options: AccountOptions,
|
||||
): AccountCreatorResult {
|
||||
recordedInvocations.add(
|
||||
CreateAccountArguments(
|
||||
emailAddress,
|
||||
incomingServerSettings,
|
||||
outgoingServerSettings,
|
||||
authorizationState,
|
||||
options,
|
||||
),
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
data class CreateAccountArguments(
|
||||
val emailAddress: String,
|
||||
val incomingServerSettings: ServerSettings,
|
||||
val outgoingServerSettings: ServerSettings,
|
||||
val authorizationState: String?,
|
||||
val options: AccountOptions,
|
||||
)
|
|
@ -0,0 +1,21 @@
|
|||
package app.k9mail.feature.account.setup.ui.createaccount
|
||||
|
||||
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.Effect
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.Event
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.State
|
||||
import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract.ViewModel
|
||||
|
||||
class FakeCreateAccountViewModel(initialState: State) :
|
||||
BaseViewModel<State, Event, Effect>(initialState), ViewModel {
|
||||
|
||||
val events = mutableListOf<Event>()
|
||||
|
||||
override fun event(event: Event) {
|
||||
events.add(event)
|
||||
}
|
||||
|
||||
fun effect(effect: Effect) {
|
||||
emitEffect(effect)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue