Merge pull request #6954 from thundernest/add_account_setup_incoming_config_validation
Add account setup incoming config validation
This commit is contained in:
commit
8015392d70
16 changed files with 316 additions and 84 deletions
|
@ -1,6 +1,8 @@
|
|||
package app.k9mail.feature.account.setup
|
||||
|
||||
import app.k9mail.feature.account.setup.ui.AccountSetupViewModel
|
||||
import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract
|
||||
import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigValidator
|
||||
import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigViewModel
|
||||
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract
|
||||
import app.k9mail.feature.account.setup.ui.options.AccountOptionsValidator
|
||||
|
@ -13,11 +15,16 @@ import org.koin.core.module.Module
|
|||
import org.koin.dsl.module
|
||||
|
||||
val featureAccountSetupModule: Module = module {
|
||||
factory<AccountIncomingConfigContract.Validator> { AccountIncomingConfigValidator() }
|
||||
factory<AccountOutgoingConfigContract.Validator> { AccountOutgoingConfigValidator() }
|
||||
factory<AccountOptionsContract.Validator> { AccountOptionsValidator() }
|
||||
|
||||
viewModel { AccountSetupViewModel() }
|
||||
viewModel { AccountIncomingConfigViewModel() }
|
||||
viewModel {
|
||||
AccountIncomingConfigViewModel(
|
||||
validator = get(),
|
||||
)
|
||||
}
|
||||
viewModel {
|
||||
AccountOutgoingConfigViewModel(
|
||||
validator = get(),
|
||||
|
|
|
@ -68,4 +68,8 @@ class NumberInputField(
|
|||
result = 31 * result + isValid.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "NumberInputField(value=$value, error=$error, isValid=$isValid)"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,4 +68,8 @@ class StringInputField(
|
|||
result = 31 * result + isValid.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "StringInputField(value='$value', error=$error, isValid=$isValid)"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package app.k9mail.feature.account.setup.domain.usecase
|
||||
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationError
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationUseCase
|
||||
|
||||
class ValidateImapPrefix : ValidationUseCase<String> {
|
||||
|
||||
override fun execute(input: String): ValidationResult {
|
||||
return when {
|
||||
input.isEmpty() -> ValidationResult.Success
|
||||
input.isBlank() -> ValidationResult.Failure(ValidateImapPrefixError.BlankImapPrefix)
|
||||
|
||||
else -> ValidationResult.Success
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface ValidateImapPrefixError : ValidationError {
|
||||
object BlankImapPrefix : ValidateImapPrefixError
|
||||
}
|
||||
}
|
|
@ -1,8 +1,14 @@
|
|||
package app.k9mail.feature.account.setup.ui
|
||||
|
||||
import android.content.res.Resources
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationError
|
||||
import app.k9mail.feature.account.setup.R
|
||||
import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidateImapPrefix
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidatePassword
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidatePort
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidateServer
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidateUsername
|
||||
|
||||
internal fun ConnectionSecurity.toResourceString(resources: Resources): String {
|
||||
return when (this) {
|
||||
|
@ -11,3 +17,58 @@ internal fun ConnectionSecurity.toResourceString(resources: Resources): String {
|
|||
ConnectionSecurity.TLS -> resources.getString(R.string.account_setup_connection_security_ssl)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ValidationError.toResourceString(resources: Resources): String {
|
||||
return when (this) {
|
||||
is ValidateServer.ValidateServerError -> toServerErrorString(resources)
|
||||
is ValidatePort.ValidatePortError -> toPortErrorString(resources)
|
||||
is ValidateUsername.ValidateUsernameError -> toUsernameErrorString(resources)
|
||||
is ValidatePassword.ValidatePasswordError -> toPasswordErrorString(resources)
|
||||
is ValidateImapPrefix.ValidateImapPrefixError -> toImapPrefixErrorString(resources)
|
||||
else -> throw IllegalArgumentException("Unknown error: $this")
|
||||
}
|
||||
}
|
||||
|
||||
private fun ValidateServer.ValidateServerError.toServerErrorString(resources: Resources): String {
|
||||
return when (this) {
|
||||
is ValidateServer.ValidateServerError.EmptyServer -> resources.getString(
|
||||
R.string.account_setup_validation_error_server_required,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ValidatePort.ValidatePortError.toPortErrorString(resources: Resources): String {
|
||||
return when (this) {
|
||||
is ValidatePort.ValidatePortError.EmptyPort -> resources.getString(
|
||||
R.string.account_setup_validation_error_port_required,
|
||||
)
|
||||
|
||||
is ValidatePort.ValidatePortError.InvalidPort -> resources.getString(
|
||||
R.string.account_setup_validation_error_port_invalid,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ValidateUsername.ValidateUsernameError.toUsernameErrorString(resources: Resources): String {
|
||||
return when (this) {
|
||||
ValidateUsername.ValidateUsernameError.EmptyUsername -> resources.getString(
|
||||
R.string.account_setup_validation_error_username_required,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ValidatePassword.ValidatePasswordError.toPasswordErrorString(resources: Resources): String {
|
||||
return when (this) {
|
||||
ValidatePassword.ValidatePasswordError.EmptyPassword -> resources.getString(
|
||||
R.string.account_setup_validation_error_password_required,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ValidateImapPrefix.ValidateImapPrefixError.toImapPrefixErrorString(resources: Resources): String {
|
||||
return when (this) {
|
||||
ValidateImapPrefix.ValidateImapPrefixError.BlankImapPrefix -> resources.getString(
|
||||
R.string.account_setup_validation_error_imap_prefix_blank,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ internal fun AccountIncomingConfigContent(
|
|||
item {
|
||||
TextInput(
|
||||
text = state.server.value,
|
||||
errorMessage = state.server.error?.toResourceString(resources),
|
||||
onTextChange = { onEvent(Event.ServerChanged(it)) },
|
||||
label = stringResource(id = R.string.account_setup_incoming_config_server_label),
|
||||
contentPadding = defaultItemPadding(),
|
||||
|
@ -95,6 +96,7 @@ internal fun AccountIncomingConfigContent(
|
|||
item {
|
||||
NumberInput(
|
||||
value = state.port.value,
|
||||
errorMessage = state.port.error?.toResourceString(resources),
|
||||
onValueChange = { onEvent(Event.PortChanged(it)) },
|
||||
label = stringResource(id = R.string.account_setup_outgoing_config_port_label),
|
||||
contentPadding = defaultItemPadding(),
|
||||
|
@ -104,6 +106,7 @@ internal fun AccountIncomingConfigContent(
|
|||
item {
|
||||
TextInput(
|
||||
text = state.username.value,
|
||||
errorMessage = state.username.error?.toResourceString(resources),
|
||||
onTextChange = { onEvent(Event.UsernameChanged(it)) },
|
||||
label = stringResource(id = R.string.account_setup_outgoing_config_username_label),
|
||||
contentPadding = defaultItemPadding(),
|
||||
|
@ -113,6 +116,7 @@ internal fun AccountIncomingConfigContent(
|
|||
item {
|
||||
PasswordInput(
|
||||
password = state.password.value,
|
||||
errorMessage = state.password.error?.toResourceString(resources),
|
||||
onPasswordChange = { onEvent(Event.PasswordChanged(it)) },
|
||||
contentPadding = defaultItemPadding(),
|
||||
)
|
||||
|
@ -149,6 +153,7 @@ internal fun AccountIncomingConfigContent(
|
|||
item {
|
||||
TextInput(
|
||||
text = state.imapPrefix.value,
|
||||
errorMessage = state.imapPrefix.error?.toResourceString(resources),
|
||||
onTextChange = { onEvent(Event.ImapPrefixChanged(it)) },
|
||||
label = stringResource(id = R.string.account_setup_incoming_config_imap_prefix_label),
|
||||
contentPadding = defaultItemPadding(),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package app.k9mail.feature.account.setup.ui.incoming
|
||||
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
|
||||
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
|
||||
import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
|
||||
import app.k9mail.feature.account.setup.domain.entity.IncomingProtocolType
|
||||
|
@ -48,4 +49,12 @@ interface AccountIncomingConfigContract {
|
|||
object NavigateNext : Effect()
|
||||
object NavigateBack : Effect()
|
||||
}
|
||||
|
||||
interface Validator {
|
||||
fun validateServer(server: String): ValidationResult
|
||||
fun validatePort(port: Long?): ValidationResult
|
||||
fun validateUsername(username: String): ValidationResult
|
||||
fun validatePassword(password: String): ValidationResult
|
||||
fun validateImapPrefix(imapPrefix: String): ValidationResult
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,9 @@ internal fun AccountIncomingConfigScreenK9Preview() {
|
|||
AccountIncomingConfigScreen(
|
||||
onNext = {},
|
||||
onBack = {},
|
||||
viewModel = AccountIncomingConfigViewModel(),
|
||||
viewModel = AccountIncomingConfigViewModel(
|
||||
validator = AccountIncomingConfigValidator(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +74,9 @@ internal fun AccountIncomingConfigScreenThunderbirdPreview() {
|
|||
AccountIncomingConfigScreen(
|
||||
onNext = {},
|
||||
onBack = {},
|
||||
viewModel = AccountIncomingConfigViewModel(),
|
||||
viewModel = AccountIncomingConfigViewModel(
|
||||
validator = AccountIncomingConfigValidator(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package app.k9mail.feature.account.setup.ui.incoming
|
||||
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidateImapPrefix
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidatePassword
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidatePort
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidateServer
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidateUsername
|
||||
|
||||
class AccountIncomingConfigValidator(
|
||||
private val serverValidator: ValidateServer = ValidateServer(),
|
||||
private val portValidator: ValidatePort = ValidatePort(),
|
||||
private val usernameValidator: ValidateUsername = ValidateUsername(),
|
||||
private val passwordValidator: ValidatePassword = ValidatePassword(),
|
||||
private val imapPrefixValidator: ValidateImapPrefix = ValidateImapPrefix(),
|
||||
) : AccountIncomingConfigContract.Validator {
|
||||
override fun validateServer(server: String): ValidationResult {
|
||||
return serverValidator.execute(server)
|
||||
}
|
||||
|
||||
override fun validatePort(port: Long?): ValidationResult {
|
||||
return portValidator.execute(port)
|
||||
}
|
||||
|
||||
override fun validateUsername(username: String): ValidationResult {
|
||||
return usernameValidator.execute(username)
|
||||
}
|
||||
|
||||
override fun validatePassword(password: String): ValidationResult {
|
||||
return passwordValidator.execute(password)
|
||||
}
|
||||
|
||||
override fun validateImapPrefix(imapPrefix: String): ValidationResult {
|
||||
return imapPrefixValidator.execute(imapPrefix)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package app.k9mail.feature.account.setup.ui.incoming
|
||||
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
|
||||
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
|
||||
import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
|
||||
import app.k9mail.feature.account.setup.domain.entity.IncomingProtocolType
|
||||
|
@ -20,10 +21,12 @@ import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContrac
|
|||
import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Event.UseCompressionChanged
|
||||
import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Event.UsernameChanged
|
||||
import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.State
|
||||
import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Validator
|
||||
import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.ViewModel
|
||||
|
||||
class AccountIncomingConfigViewModel(
|
||||
initialState: State = State(),
|
||||
private val validator: Validator,
|
||||
) : BaseViewModel<State, Event, Effect>(initialState), ViewModel {
|
||||
|
||||
override fun initState(state: State) {
|
||||
|
@ -70,8 +73,29 @@ class AccountIncomingConfigViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
private fun submit() {
|
||||
navigateNext()
|
||||
private fun submit() = with(state.value) {
|
||||
val serverResult = validator.validateServer(server.value)
|
||||
val portResult = validator.validatePort(port.value)
|
||||
val usernameResult = validator.validateUsername(username.value)
|
||||
val passwordResult = validator.validatePassword(password.value)
|
||||
val imapPrefixResult = validator.validateImapPrefix(imapPrefix.value)
|
||||
|
||||
val hasError = listOf(serverResult, portResult, usernameResult, passwordResult, imapPrefixResult)
|
||||
.any { it is ValidationResult.Failure }
|
||||
|
||||
updateState {
|
||||
it.copy(
|
||||
server = it.server.updateFromValidationResult(serverResult),
|
||||
port = it.port.updateFromValidationResult(portResult),
|
||||
username = it.username.updateFromValidationResult(usernameResult),
|
||||
password = it.password.updateFromValidationResult(passwordResult),
|
||||
imapPrefix = it.imapPrefix.updateFromValidationResult(imapPrefixResult),
|
||||
)
|
||||
}
|
||||
|
||||
if (!hasError) {
|
||||
navigateNext()
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateBack() = emitEffect(Effect.NavigateBack)
|
||||
|
|
|
@ -44,7 +44,6 @@ interface AccountOutgoingConfigContract {
|
|||
|
||||
interface Validator {
|
||||
fun validateServer(server: String): ValidationResult
|
||||
|
||||
fun validatePort(port: Long?): ValidationResult
|
||||
fun validateUsername(username: String): ValidationResult
|
||||
fun validatePassword(password: String): ValidationResult
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
package app.k9mail.feature.account.setup.ui.outgoing
|
||||
|
||||
import android.content.res.Resources
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationError
|
||||
import app.k9mail.feature.account.setup.R
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidatePassword.ValidatePasswordError
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidatePort.ValidatePortError
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidateServer.ValidateServerError
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidateUsername.ValidateUsernameError
|
||||
|
||||
internal fun ValidationError.toResourceString(resources: Resources): String {
|
||||
return when (this) {
|
||||
is ValidateServerError -> toServerErrorString(resources)
|
||||
is ValidatePortError -> toPortErrorString(resources)
|
||||
is ValidateUsernameError -> toUsernameErrorString(resources)
|
||||
is ValidatePasswordError -> toPasswordErrorString(resources)
|
||||
else -> throw IllegalArgumentException("Unknown error: $this")
|
||||
}
|
||||
}
|
||||
|
||||
private fun ValidateServerError.toServerErrorString(resources: Resources): String {
|
||||
return when (this) {
|
||||
is ValidateServerError.EmptyServer -> resources.getString(
|
||||
R.string.account_setup_outgoing_config_server_error_required,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ValidatePortError.toPortErrorString(resources: Resources): String {
|
||||
return when (this) {
|
||||
is ValidatePortError.EmptyPort -> resources.getString(
|
||||
R.string.account_setup_outgoing_config_port_error_required,
|
||||
)
|
||||
|
||||
is ValidatePortError.InvalidPort -> resources.getString(
|
||||
R.string.account_setup_outgoing_config_port_error_invalid,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ValidateUsernameError.toUsernameErrorString(resources: Resources): String {
|
||||
return when (this) {
|
||||
ValidateUsernameError.EmptyUsername -> resources.getString(
|
||||
R.string.account_setup_outgoing_config_username_error_required,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ValidatePasswordError.toPasswordErrorString(resources: Resources): String {
|
||||
return when (this) {
|
||||
ValidatePasswordError.EmptyPassword -> resources.getString(
|
||||
R.string.account_setup_outgoing_config_password_error_required,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -9,6 +9,13 @@
|
|||
<string name="account_setup_connection_security_start_tls">StartTLS</string>
|
||||
<string name="account_setup_client_certificate_none_available">None available</string>
|
||||
|
||||
<string name="account_setup_validation_error_server_required">Server name is required.</string>
|
||||
<string name="account_setup_validation_error_port_required">Port is required.</string>
|
||||
<string name="account_setup_validation_error_port_invalid">Port is invalid (must be 1–65535).</string>
|
||||
<string name="account_setup_validation_error_username_required">Username is required.</string>
|
||||
<string name="account_setup_validation_error_password_required">Password is required.</string>
|
||||
<string name="account_setup_validation_error_imap_prefix_blank">Imap prefix can\'t be blank.</string>
|
||||
|
||||
<string name="account_setup_auto_config_title">K-9 Mail</string>
|
||||
|
||||
<string name="account_setup_incoming_config_top_bar_title">Incoming server settings</string>
|
||||
|
@ -21,14 +28,9 @@
|
|||
|
||||
<string name="account_setup_outgoing_config_top_bar_title">Outgoing server settings</string>
|
||||
<string name="account_setup_outgoing_config_server_label">Server</string>
|
||||
<string name="account_setup_outgoing_config_server_error_required">Server name is required.</string>
|
||||
<string name="account_setup_outgoing_config_security_label">Security</string>
|
||||
<string name="account_setup_outgoing_config_port_label">Port</string>
|
||||
<string name="account_setup_outgoing_config_port_error_required">Port is required.</string>
|
||||
<string name="account_setup_outgoing_config_port_error_invalid">Port is invalid (must be 1–65535).</string>
|
||||
<string name="account_setup_outgoing_config_username_label">Username</string>
|
||||
<string name="account_setup_outgoing_config_username_error_required">Username is required.</string>
|
||||
<string name="account_setup_outgoing_config_password_error_required">Password is required.</string>
|
||||
<string name="account_setup_outgoing_config_client_certificate_label">Client certificate</string>
|
||||
<string name="account_setup_outgoing_config_imap_namespace_label">Auto-detect IMAP namespace</string>
|
||||
<string name="account_setup_outgoing_config_compression_label">Use compression</string>
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package app.k9mail.feature.account.setup.domain.usecase
|
||||
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isInstanceOf
|
||||
import assertk.assertions.prop
|
||||
import org.junit.Test
|
||||
|
||||
class ValidateImapPrefixTest {
|
||||
|
||||
@Test
|
||||
fun `should success when imap prefix is set`() {
|
||||
val useCase = ValidateImapPrefix()
|
||||
|
||||
val result = useCase.execute("imap")
|
||||
|
||||
assertThat(result).isInstanceOf(ValidationResult.Success::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should succeed when imap prefix is empty`() {
|
||||
val useCase = ValidateImapPrefix()
|
||||
|
||||
val result = useCase.execute("")
|
||||
|
||||
assertThat(result).isInstanceOf(ValidationResult.Success::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail when imap prefix is blank`() {
|
||||
val useCase = ValidateImapPrefix()
|
||||
|
||||
val result = useCase.execute(" ")
|
||||
|
||||
assertThat(result).isInstanceOf(ValidationResult.Failure::class)
|
||||
.prop(ValidationResult.Failure::error)
|
||||
.isInstanceOf(ValidateImapPrefix.ValidateImapPrefixError.BlankImapPrefix::class)
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@ package app.k9mail.feature.account.setup.ui.incoming
|
|||
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import app.cash.turbine.testIn
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationError
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
|
||||
import app.k9mail.core.ui.compose.testing.MainDispatcherRule
|
||||
import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
|
||||
import app.k9mail.feature.account.setup.domain.entity.IncomingProtocolType
|
||||
|
@ -13,6 +15,7 @@ import app.k9mail.feature.account.setup.testing.eventStateTest
|
|||
import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Effect
|
||||
import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Event
|
||||
import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.State
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.assertThatAndTurbinesConsumed
|
||||
import assertk.assertions.isEqualTo
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
@ -26,7 +29,9 @@ class AccountIncomingConfigViewModelTest {
|
|||
@get:Rule
|
||||
val mainDispatcherRule = MainDispatcherRule()
|
||||
|
||||
private val testSubject = AccountIncomingConfigViewModel()
|
||||
private val testSubject = AccountIncomingConfigViewModel(
|
||||
validator = FakeAccountIncomingConfigValidator(),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `should change protocol, security and port when ProtocolTypeChanged event is received`() = runTest {
|
||||
|
@ -152,28 +157,76 @@ class AccountIncomingConfigViewModelTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `should emit NavigateNext effect when OnNextClicked event received`() = runTest {
|
||||
val viewModel = testSubject
|
||||
val stateTurbine = viewModel.state.testIn(backgroundScope)
|
||||
val effectTurbine = viewModel.effect.testIn(backgroundScope)
|
||||
val turbines = listOf(stateTurbine, effectTurbine)
|
||||
fun `should change state and emit NavigateNext effect when OnNextClicked event is received and input is valid`() =
|
||||
runTest {
|
||||
val viewModel = testSubject
|
||||
val stateTurbine = viewModel.state.testIn(backgroundScope)
|
||||
val effectTurbine = viewModel.effect.testIn(backgroundScope)
|
||||
val turbines = listOf(stateTurbine, effectTurbine)
|
||||
|
||||
assertThatAndTurbinesConsumed(
|
||||
actual = stateTurbine.awaitItem(),
|
||||
turbines = turbines,
|
||||
) {
|
||||
isEqualTo(State())
|
||||
assertThatAndTurbinesConsumed(
|
||||
actual = stateTurbine.awaitItem(),
|
||||
turbines = turbines,
|
||||
) {
|
||||
isEqualTo(State())
|
||||
}
|
||||
|
||||
viewModel.event(Event.OnNextClicked)
|
||||
|
||||
assertThat(stateTurbine.awaitItem()).isEqualTo(
|
||||
State(
|
||||
server = StringInputField(value = "", isValid = true),
|
||||
port = NumberInputField(value = 993L, isValid = true),
|
||||
username = StringInputField(value = "", isValid = true),
|
||||
password = StringInputField(value = "", isValid = true),
|
||||
imapPrefix = StringInputField(value = "", isValid = true),
|
||||
),
|
||||
)
|
||||
|
||||
assertThatAndTurbinesConsumed(
|
||||
actual = effectTurbine.awaitItem(),
|
||||
turbines = turbines,
|
||||
) {
|
||||
isEqualTo(Effect.NavigateNext)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.event(Event.OnNextClicked)
|
||||
@Test
|
||||
fun `should change state and not emit NavigateNext effect when OnNextClicked event received and input invalid`() =
|
||||
runTest {
|
||||
val viewModel = AccountIncomingConfigViewModel(
|
||||
validator = FakeAccountIncomingConfigValidator(
|
||||
serverAnswer = ValidationResult.Failure(TestError),
|
||||
),
|
||||
)
|
||||
val stateTurbine = viewModel.state.testIn(backgroundScope)
|
||||
val effectTurbine = viewModel.effect.testIn(backgroundScope)
|
||||
val turbines = listOf(stateTurbine, effectTurbine)
|
||||
|
||||
assertThatAndTurbinesConsumed(
|
||||
actual = effectTurbine.awaitItem(),
|
||||
turbines = turbines,
|
||||
) {
|
||||
isEqualTo(Effect.NavigateNext)
|
||||
assertThatAndTurbinesConsumed(
|
||||
actual = stateTurbine.awaitItem(),
|
||||
turbines = turbines,
|
||||
) {
|
||||
isEqualTo(State())
|
||||
}
|
||||
|
||||
viewModel.event(Event.OnNextClicked)
|
||||
|
||||
assertThatAndTurbinesConsumed(
|
||||
actual = stateTurbine.awaitItem(),
|
||||
turbines = turbines,
|
||||
) {
|
||||
isEqualTo(
|
||||
State(
|
||||
server = StringInputField(value = "", error = TestError, isValid = false),
|
||||
port = NumberInputField(value = 993L, isValid = true),
|
||||
username = StringInputField(value = "", isValid = true),
|
||||
password = StringInputField(value = "", isValid = true),
|
||||
imapPrefix = StringInputField(value = "", isValid = true),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should emit NavigateBack effect when OnBackClicked event received`() = runTest {
|
||||
|
@ -198,4 +251,6 @@ class AccountIncomingConfigViewModelTest {
|
|||
isEqualTo(Effect.NavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
private object TestError : ValidationError
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package app.k9mail.feature.account.setup.ui.incoming
|
||||
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
|
||||
|
||||
class FakeAccountIncomingConfigValidator(
|
||||
private val serverAnswer: ValidationResult = ValidationResult.Success,
|
||||
private val portAnswer: ValidationResult = ValidationResult.Success,
|
||||
private val usernameAnswer: ValidationResult = ValidationResult.Success,
|
||||
private val passwordAnswer: ValidationResult = ValidationResult.Success,
|
||||
private val imapPrefixAnswer: ValidationResult = ValidationResult.Success,
|
||||
) : AccountIncomingConfigContract.Validator {
|
||||
override fun validateServer(server: String): ValidationResult = serverAnswer
|
||||
override fun validatePort(port: Long?): ValidationResult = portAnswer
|
||||
override fun validateUsername(username: String): ValidationResult = usernameAnswer
|
||||
override fun validatePassword(password: String): ValidationResult = passwordAnswer
|
||||
override fun validateImapPrefix(imapPrefix: String): ValidationResult = imapPrefixAnswer
|
||||
}
|
Loading…
Reference in a new issue