BIT-193 Implement password length validation on create account (#96)

This commit is contained in:
Andrew Haisting
2023-10-05 17:23:04 -05:00
committed by GitHub
parent 1d777f77ce
commit 9a3420894b
5 changed files with 189 additions and 14 deletions

View File

@@ -1,5 +1,9 @@
package com.x8bit.bitwarden.ui.auth.feature.createaccount
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.filterToOne
import androidx.compose.ui.test.hasAnyAncestor
import androidx.compose.ui.test.isDialog
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
@@ -12,6 +16,8 @@ import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.Pas
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.PasswordInputChange
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.SubmitClick
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.components.BasicDialogState
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
@@ -25,7 +31,7 @@ class CreateAccountScreenTest : BaseComposeTest() {
@Test
fun `app bar submit click should send SubmitClick action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE.copy(isSubmitEnabled = true))
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns emptyFlow()
every { trySendAction(SubmitClick) } returns Unit
}
@@ -39,7 +45,7 @@ class CreateAccountScreenTest : BaseComposeTest() {
@Test
fun `bottom button submit click should send SubmitClick action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE.copy(isSubmitEnabled = true))
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns emptyFlow()
every { trySendAction(SubmitClick) } returns Unit
}
@@ -136,6 +142,50 @@ class CreateAccountScreenTest : BaseComposeTest() {
verify { viewModel.trySendAction(PasswordHintChange(TEST_INPUT)) }
}
@Test
fun `clicking OK on the error dialog should send ErrorDialogDismiss action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(
DEFAULT_STATE.copy(
errorDialogState = BasicDialogState.Shown(
title = "title".asText(),
message = "message".asText(),
),
),
)
every { eventFlow } returns emptyFlow()
every { trySendAction(CreateAccountAction.ErrorDialogDismiss) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(onNavigateBack = {}, viewModel = viewModel)
}
composeTestRule
.onAllNodesWithText("Ok")
.filterToOne(hasAnyAncestor(isDialog()))
.performClick()
verify { viewModel.trySendAction(CreateAccountAction.ErrorDialogDismiss) }
}
@Test
fun `when BasicDialogState is Shown should show dialog`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(
DEFAULT_STATE.copy(
errorDialogState = BasicDialogState.Shown(
title = "title".asText(),
message = "message".asText(),
),
),
)
every { eventFlow } returns emptyFlow()
every { trySendAction(CreateAccountAction.ErrorDialogDismiss) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(onNavigateBack = {}, viewModel = viewModel)
}
composeTestRule.onNode(isDialog()).assertIsDisplayed()
}
companion object {
private const val TEST_INPUT = "input"
private val DEFAULT_STATE = CreateAccountState(
@@ -143,7 +193,7 @@ class CreateAccountScreenTest : BaseComposeTest() {
passwordInput = "",
confirmPasswordInput = "",
passwordHintInput = "",
isSubmitEnabled = false,
errorDialogState = BasicDialogState.Hidden,
)
}
}

View File

@@ -2,13 +2,15 @@ package com.x8bit.bitwarden.ui.auth.feature.createaccount
import androidx.lifecycle.SavedStateHandle
import app.cash.turbine.test
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.CloseClick
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.ConfirmPasswordInputChange
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.EmailInputChange
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.PasswordHintChange
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.PasswordInputChange
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.SubmitClick
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.components.BasicDialogState
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
@@ -28,7 +30,7 @@ class CreateAccountViewModelTest : BaseViewModelTest() {
passwordInput = "password",
confirmPasswordInput = "confirmPassword",
passwordHintInput = "hint",
isSubmitEnabled = false,
errorDialogState = BasicDialogState.Hidden,
)
val handle = SavedStateHandle(mapOf("state" to savedState))
val viewModel = CreateAccountViewModel(handle)
@@ -36,10 +38,29 @@ class CreateAccountViewModelTest : BaseViewModelTest() {
}
@Test
fun `SubmitClick should emit ShowToast`() = runTest {
fun `SubmitClick with password below 12 chars should show password length dialog`() = runTest {
val viewModel = CreateAccountViewModel(SavedStateHandle())
val input = "abcdefghikl"
viewModel.trySendAction(PasswordInputChange("abcdefghikl"))
val expectedState = DEFAULT_STATE.copy(
passwordInput = input,
errorDialogState = BasicDialogState.Shown(
title = R.string.an_error_has_occurred.asText(),
message = R.string.master_password_length_val_message_x.asText(12),
),
)
viewModel.actionChannel.trySend(CreateAccountAction.SubmitClick)
viewModel.stateFlow.test {
assertEquals(expectedState, awaitItem())
}
}
@Test
fun `SubmitClick with long enough password emit ShowToast`() = runTest {
val viewModel = CreateAccountViewModel(SavedStateHandle())
viewModel.trySendAction(PasswordInputChange("longenoughpassword"))
viewModel.eventFlow.test {
viewModel.actionChannel.trySend(SubmitClick)
viewModel.actionChannel.trySend(CreateAccountAction.SubmitClick)
assert(awaitItem() is CreateAccountEvent.ShowToast)
}
}
@@ -95,7 +116,7 @@ class CreateAccountViewModelTest : BaseViewModelTest() {
emailInput = "",
confirmPasswordInput = "",
passwordHintInput = "",
isSubmitEnabled = false,
errorDialogState = BasicDialogState.Hidden,
)
}
}