BIT-333: Adding UI tests for the Generator screen (#70)

This commit is contained in:
joshua-livefront 2023-09-28 10:08:46 -04:00 committed by Álison Fernandes
parent 1dccee6c45
commit a1096d21ec
2 changed files with 235 additions and 4 deletions

View file

@ -43,6 +43,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
@ -499,7 +500,9 @@ private fun CounterItem(
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth(),
modifier = Modifier
.semantics(mergeDescendants = true) {}
.fillMaxWidth(),
) {
Text(label)
Row(
@ -510,7 +513,8 @@ private fun CounterItem(
) {
Icon(
Icons.Default.ArrowBack,
contentDescription = null,
// Unicode for "minus"
contentDescription = "\u2212",
tint = MaterialTheme.colorScheme.primary,
)
}
@ -522,7 +526,7 @@ private fun CounterItem(
) {
Icon(
Icons.Default.ArrowForward,
contentDescription = null,
contentDescription = "+",
tint = MaterialTheme.colorScheme.primary,
)
}
@ -553,6 +557,7 @@ private fun TextInputItem(
CommonPadding {
Column(
modifier = Modifier
.semantics(mergeDescendants = true) {}
.fillMaxHeight()
.padding(top = 4.dp, bottom = 4.dp),
verticalArrangement = Arrangement.Center,
@ -596,7 +601,9 @@ private fun SwitchItem(
) {
CommonPadding {
Row(
modifier = Modifier.fillMaxWidth(),
modifier = Modifier
.semantics(mergeDescendants = true) {}
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {

View file

@ -0,0 +1,224 @@
@file:Suppress("MaxLineLength")
package com.x8bit.bitwarden.ui.tools.feature.generator
import androidx.compose.ui.semantics.Role.Companion.Switch
import androidx.compose.ui.semantics.SemanticsProperties.Role
import androidx.compose.ui.test.SemanticsMatcher.Companion.expectValue
import androidx.compose.ui.test.filterToOne
import androidx.compose.ui.test.hasContentDescription
import androidx.compose.ui.test.hasSetTextAction
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onChildren
import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.onLast
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performTextInput
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
import org.junit.Test
class GeneratorScreenTest : BaseComposeTest() {
private val mutableStateFlow = MutableStateFlow(
GeneratorState(
generatedText = "Placeholder",
selectedType = GeneratorState.MainType.Passcode(GeneratorState.MainType.Passcode.PasscodeType.Password()),
),
)
private val viewModel = mockk<GeneratorViewModel>(relaxed = true) {
every { eventFlow } returns emptyFlow()
every { stateFlow } returns mutableStateFlow
}
@Test
fun `clicking a MainStateOption should send MainTypeOptionSelect action`() {
composeTestRule.setContent {
GeneratorScreen(viewModel = viewModel)
}
// Opens the menu
composeTestRule.onAllNodesWithText(text = "Password").onFirst().performClick()
// Choose the option from the menu
composeTestRule.onAllNodesWithText(text = "Password").onLast().performClick()
verify {
viewModel.trySendAction(GeneratorAction.MainTypeOptionSelect(GeneratorState.MainTypeOption.PASSWORD))
}
}
@Test
fun `clicking a PasscodeOption should send PasscodeTypeOption action`() {
composeTestRule.setContent {
GeneratorScreen(viewModel = viewModel)
}
// Opens the menu
composeTestRule.onAllNodesWithText(text = "Password").onLast().performClick()
// Choose the option from the menu
composeTestRule.onAllNodesWithText(text = "Passphrase").onLast().performClick()
verify {
viewModel.trySendAction(
GeneratorAction.MainType.Passcode.PasscodeTypeOptionSelect(
GeneratorState.MainType.Passcode.PasscodeTypeOption.PASSPHRASE,
),
)
}
}
@Test
fun `in Passcode_Passphrase state, decrementing number of words should send NumWordsCounterChange action with decremented value`() {
val initialNumWords = 3
updateState(
GeneratorState(
generatedText = "Placeholder",
selectedType = GeneratorState.MainType.Passcode(GeneratorState.MainType.Passcode.PasscodeType.Passphrase()),
),
)
composeTestRule.setContent {
GeneratorScreen(viewModel = viewModel)
}
// Unicode for "minus" used for content description
composeTestRule
.onNodeWithText("Number of words")
.onChildren()
.filterToOne(hasContentDescription("\u2212"))
.performClick()
verify {
viewModel.trySendAction(
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.NumWordsCounterChange(
numWords = initialNumWords - 1,
),
)
}
}
@Test
fun `in Passcode_Passphrase state, incrementing number of words should send NumWordsCounterChange action with incremented value`() {
val initialNumWords = 3
updateState(
GeneratorState(
generatedText = "Placeholder",
selectedType = GeneratorState.MainType.Passcode(GeneratorState.MainType.Passcode.PasscodeType.Passphrase()),
),
)
composeTestRule.setContent {
GeneratorScreen(viewModel = viewModel)
}
composeTestRule
.onNodeWithText("Number of words")
.onChildren()
.filterToOne(hasContentDescription("+"))
.performClick()
verify {
viewModel.trySendAction(
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.NumWordsCounterChange(
numWords = initialNumWords + 1,
),
)
}
}
@Test
fun `in Passcode_Passphrase state, toggling capitalize should send ToggleCapitalizeChange action`() {
updateState(
GeneratorState(
generatedText = "Placeholder",
selectedType = GeneratorState.MainType.Passcode(GeneratorState.MainType.Passcode.PasscodeType.Passphrase()),
),
)
composeTestRule.setContent {
GeneratorScreen(viewModel = viewModel)
}
composeTestRule
.onNodeWithText("Capitalize")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.performScrollTo()
.performClick()
verify {
viewModel.trySendAction(
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.ToggleCapitalizeChange(
capitalize = true,
),
)
}
}
@Test
fun `in Passcode_Passphrase state, toggling the include number toggle should send ToggleIncludeNumberChange action`() {
updateState(
GeneratorState(
generatedText = "Placeholder",
selectedType = GeneratorState.MainType.Passcode(GeneratorState.MainType.Passcode.PasscodeType.Passphrase()),
),
)
composeTestRule.setContent {
GeneratorScreen(viewModel = viewModel)
}
composeTestRule.onNodeWithText("Include number")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.performScrollTo()
.performClick()
verify {
viewModel.trySendAction(
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.ToggleIncludeNumberChange(
includeNumber = true,
),
)
}
}
@Test
fun `in Passcode_Passphrase state, updating text in word separator should send WordSeparatorTextChange action`() {
updateState(
GeneratorState(
generatedText = "Placeholder",
selectedType = GeneratorState.MainType.Passcode(GeneratorState.MainType.Passcode.PasscodeType.Passphrase()),
),
)
composeTestRule.setContent {
GeneratorScreen(viewModel = viewModel)
}
composeTestRule.onNodeWithText("Word separator")
.onChildren()
.filterToOne(hasSetTextAction())
.performTextInput("a")
verify {
viewModel.trySendAction(
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.WordSeparatorTextChange(
wordSeparator = 'a',
),
)
}
}
private fun updateState(state: GeneratorState) {
mutableStateFlow.value = state
}
}