mirror of
https://github.com/bitwarden/android.git
synced 2025-02-16 11:59:57 +03:00
BIT-333: Adding UI tests for the Generator screen (#70)
This commit is contained in:
parent
1dccee6c45
commit
a1096d21ec
2 changed files with 235 additions and 4 deletions
|
@ -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,
|
||||
) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue