mirror of
https://github.com/bitwarden/android.git
synced 2024-11-25 02:46:00 +03:00
BIT-634: Generator UI fixes (#126)
This commit is contained in:
parent
cabde64d61
commit
4f9f0ce8a7
4 changed files with 67 additions and 27 deletions
|
@ -8,6 +8,7 @@ import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.LargeTopAppBar
|
import androidx.compose.material3.LargeTopAppBar
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.MediumTopAppBar
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
|
@ -37,14 +38,14 @@ import com.x8bit.bitwarden.R
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun BitwardenLargeTopAppBar(
|
fun BitwardenMediumTopAppBar(
|
||||||
title: String,
|
title: String,
|
||||||
dropdownMenuItemContent: @Composable ColumnScope.() -> Unit = {},
|
dropdownMenuItemContent: @Composable ColumnScope.() -> Unit = {},
|
||||||
scrollBehavior: TopAppBarScrollBehavior,
|
scrollBehavior: TopAppBarScrollBehavior,
|
||||||
) {
|
) {
|
||||||
var isOverflowMenuVisible by remember { mutableStateOf(false) }
|
var isOverflowMenuVisible by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
LargeTopAppBar(
|
MediumTopAppBar(
|
||||||
colors = TopAppBarDefaults.largeTopAppBarColors(
|
colors = TopAppBarDefaults.largeTopAppBarColors(
|
||||||
scrolledContainerColor = MaterialTheme.colorScheme.surface,
|
scrolledContainerColor = MaterialTheme.colorScheme.surface,
|
||||||
),
|
),
|
|
@ -15,6 +15,7 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
import androidx.compose.ui.semantics.semantics
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
@ -27,6 +28,7 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||||
* @param isChecked The current state of the switch (either checked or unchecked).
|
* @param isChecked The current state of the switch (either checked or unchecked).
|
||||||
* @param onCheckedChange A lambda that is invoked when the switch's state changes.
|
* @param onCheckedChange A lambda that is invoked when the switch's state changes.
|
||||||
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
|
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
|
||||||
|
* @param contentDescription A description of the switch's UI for accessibility purposes.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun BitwardenWideSwitch(
|
fun BitwardenWideSwitch(
|
||||||
|
@ -34,6 +36,7 @@ fun BitwardenWideSwitch(
|
||||||
isChecked: Boolean,
|
isChecked: Boolean,
|
||||||
onCheckedChange: ((Boolean) -> Unit)?,
|
onCheckedChange: ((Boolean) -> Unit)?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
contentDescription: String? = null,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
@ -46,7 +49,11 @@ fun BitwardenWideSwitch(
|
||||||
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
|
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
|
||||||
onClick = { onCheckedChange?.invoke(!isChecked) },
|
onClick = { onCheckedChange?.invoke(!isChecked) },
|
||||||
)
|
)
|
||||||
.semantics(mergeDescendants = true) { }
|
.semantics(mergeDescendants = true) {
|
||||||
|
if (contentDescription != null) {
|
||||||
|
this.contentDescription = contentDescription
|
||||||
|
}
|
||||||
|
}
|
||||||
.then(modifier),
|
.then(modifier),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
|
|
|
@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.widthIn
|
|
||||||
import androidx.compose.foundation.layout.wrapContentWidth
|
import androidx.compose.foundation.layout.wrapContentWidth
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
@ -27,22 +26,28 @@ import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.semantics
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.x8bit.bitwarden.R
|
import com.x8bit.bitwarden.R
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenLargeTopAppBar
|
import com.x8bit.bitwarden.ui.platform.base.util.toDp
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenMediumTopAppBar
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenMultiSelectButton
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenMultiSelectButton
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextField
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextField
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextFieldWithTwoIcons
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextFieldWithTwoIcons
|
||||||
|
@ -102,7 +107,7 @@ fun GeneratorScreen(viewModel: GeneratorViewModel = hiltViewModel()) {
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
BitwardenLargeTopAppBar(
|
BitwardenMediumTopAppBar(
|
||||||
title = stringResource(id = R.string.generator),
|
title = stringResource(id = R.string.generator),
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
)
|
)
|
||||||
|
@ -272,7 +277,7 @@ private fun PasscodeOptionsItem(
|
||||||
possibleSubStates.associateBy({ it }, { stringResource(id = it.labelRes) })
|
possibleSubStates.associateBy({ it }, { stringResource(id = it.labelRes) })
|
||||||
|
|
||||||
BitwardenMultiSelectButton(
|
BitwardenMultiSelectButton(
|
||||||
label = stringResource(id = currentSubState.selectedType.displayStringResId),
|
label = stringResource(id = R.string.password_type),
|
||||||
options = optionsWithStrings.values.toList(),
|
options = optionsWithStrings.values.toList(),
|
||||||
selectedOption = stringResource(id = currentSubState.selectedType.displayStringResId),
|
selectedOption = stringResource(id = currentSubState.selectedType.displayStringResId),
|
||||||
onOptionSelected = { selectedOption ->
|
onOptionSelected = { selectedOption ->
|
||||||
|
@ -347,6 +352,9 @@ private fun PasswordLengthSliderItem(
|
||||||
length: Int,
|
length: Int,
|
||||||
onPasswordSliderLengthChange: (Int) -> Unit,
|
onPasswordSliderLengthChange: (Int) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
var labelTextWidth by remember { mutableStateOf(Dp.Unspecified) }
|
||||||
|
|
||||||
|
val density = LocalDensity.current
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -362,12 +370,21 @@ private fun PasswordLengthSliderItem(
|
||||||
onPasswordSliderLengthChange(newValue)
|
onPasswordSliderLengthChange(newValue)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
label = { Text(stringResource(id = R.string.length)) },
|
label = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.length),
|
||||||
|
modifier = Modifier
|
||||||
|
.onGloballyPositioned {
|
||||||
|
labelTextWidth = it.size.width.toDp(density)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.wrapContentWidth()
|
.wrapContentWidth()
|
||||||
.widthIn(max = 71.dp),
|
// We want the width to be no wider than the label + 16dp on either side
|
||||||
|
.width(labelTextWidth + 16.dp + 16.dp),
|
||||||
)
|
)
|
||||||
|
|
||||||
Slider(
|
Slider(
|
||||||
|
@ -378,6 +395,7 @@ private fun PasswordLengthSliderItem(
|
||||||
valueRange =
|
valueRange =
|
||||||
PASSWORD_LENGTH_SLIDER_MIN.toFloat()..PASSWORD_LENGTH_SLIDER_MAX.toFloat(),
|
PASSWORD_LENGTH_SLIDER_MIN.toFloat()..PASSWORD_LENGTH_SLIDER_MAX.toFloat(),
|
||||||
steps = PASSWORD_LENGTH_SLIDER_MAX - 1,
|
steps = PASSWORD_LENGTH_SLIDER_MAX - 1,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -388,10 +406,11 @@ private fun PasswordCapitalLettersToggleItem(
|
||||||
onPasswordToggleCapitalLettersChange: (Boolean) -> Unit,
|
onPasswordToggleCapitalLettersChange: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
BitwardenWideSwitch(
|
BitwardenWideSwitch(
|
||||||
label = stringResource(id = R.string.uppercase_ato_z),
|
label = "A—Z",
|
||||||
isChecked = useCapitals,
|
isChecked = useCapitals,
|
||||||
onCheckedChange = onPasswordToggleCapitalLettersChange,
|
onCheckedChange = onPasswordToggleCapitalLettersChange,
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
contentDescription = stringResource(id = R.string.uppercase_ato_z),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,10 +420,11 @@ private fun PasswordLowercaseLettersToggleItem(
|
||||||
onPasswordToggleLowercaseLettersChange: (Boolean) -> Unit,
|
onPasswordToggleLowercaseLettersChange: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
BitwardenWideSwitch(
|
BitwardenWideSwitch(
|
||||||
label = stringResource(id = R.string.lowercase_ato_z),
|
label = "a—z",
|
||||||
isChecked = useLowercase,
|
isChecked = useLowercase,
|
||||||
onCheckedChange = onPasswordToggleLowercaseLettersChange,
|
onCheckedChange = onPasswordToggleLowercaseLettersChange,
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
contentDescription = stringResource(id = R.string.lowercase_ato_z),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,10 +434,11 @@ private fun PasswordNumbersToggleItem(
|
||||||
onPasswordToggleNumbersChange: (Boolean) -> Unit,
|
onPasswordToggleNumbersChange: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
BitwardenWideSwitch(
|
BitwardenWideSwitch(
|
||||||
label = stringResource(id = R.string.numbers_zero_to_nine),
|
label = "0-9",
|
||||||
isChecked = useNumbers,
|
isChecked = useNumbers,
|
||||||
onCheckedChange = onPasswordToggleNumbersChange,
|
onCheckedChange = onPasswordToggleNumbersChange,
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
contentDescription = stringResource(id = R.string.numbers_zero_to_nine),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,10 +448,11 @@ private fun PasswordSpecialCharactersToggleItem(
|
||||||
onPasswordToggleSpecialCharactersChange: (Boolean) -> Unit,
|
onPasswordToggleSpecialCharactersChange: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
BitwardenWideSwitch(
|
BitwardenWideSwitch(
|
||||||
label = stringResource(id = R.string.special_characters),
|
label = "!@#$%^&*",
|
||||||
isChecked = useSpecialChars,
|
isChecked = useSpecialChars,
|
||||||
onCheckedChange = onPasswordToggleSpecialCharactersChange,
|
onCheckedChange = onPasswordToggleSpecialCharactersChange,
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
contentDescription = stringResource(id = R.string.special_characters),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,9 @@ class GeneratorScreenTest : BaseComposeTest() {
|
||||||
GeneratorScreen(viewModel = viewModel)
|
GeneratorScreen(viewModel = viewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
composeTestRule.onNodeWithContentDescription(label = "Generate password").performClick()
|
composeTestRule
|
||||||
|
.onNodeWithContentDescription(label = "Generate password")
|
||||||
|
.performClick()
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
viewModel.trySendAction(GeneratorAction.RegenerateClick)
|
viewModel.trySendAction(GeneratorAction.RegenerateClick)
|
||||||
|
@ -61,7 +63,9 @@ class GeneratorScreenTest : BaseComposeTest() {
|
||||||
GeneratorScreen(viewModel = viewModel)
|
GeneratorScreen(viewModel = viewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
composeTestRule.onNodeWithContentDescription(label = "Copy").performClick()
|
composeTestRule
|
||||||
|
.onNodeWithContentDescription(label = "Copy")
|
||||||
|
.performClick()
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
viewModel.trySendAction(GeneratorAction.CopyClick)
|
viewModel.trySendAction(GeneratorAction.CopyClick)
|
||||||
|
@ -80,7 +84,8 @@ class GeneratorScreenTest : BaseComposeTest() {
|
||||||
.performClick()
|
.performClick()
|
||||||
|
|
||||||
// Choose the option from the menu
|
// Choose the option from the menu
|
||||||
composeTestRule.onAllNodesWithText(text = "Password")
|
composeTestRule
|
||||||
|
.onAllNodesWithText(text = "Password")
|
||||||
.onLast()
|
.onLast()
|
||||||
.performScrollTo()
|
.performScrollTo()
|
||||||
.performClick()
|
.performClick()
|
||||||
|
@ -97,10 +102,15 @@ class GeneratorScreenTest : BaseComposeTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opens the menu
|
// Opens the menu
|
||||||
composeTestRule.onNodeWithContentDescription(label = "Password, Password").performClick()
|
composeTestRule
|
||||||
|
.onNodeWithContentDescription(label = "Password type, Password")
|
||||||
|
.performClick()
|
||||||
|
|
||||||
// Choose the option from the menu
|
// Choose the option from the menu
|
||||||
composeTestRule.onAllNodesWithText(text = "Passphrase").onLast().performClick()
|
composeTestRule
|
||||||
|
.onAllNodesWithText(text = "Passphrase")
|
||||||
|
.onLast()
|
||||||
|
.performClick()
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
viewModel.trySendAction(
|
viewModel.trySendAction(
|
||||||
|
@ -124,7 +134,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
||||||
.assertIsDisplayed()
|
.assertIsDisplayed()
|
||||||
|
|
||||||
composeTestRule
|
composeTestRule
|
||||||
.onNodeWithContentDescription(label = "Password, Password")
|
.onNodeWithContentDescription(label = "Password type, Password")
|
||||||
.assertIsDisplayed()
|
.assertIsDisplayed()
|
||||||
|
|
||||||
composeTestRule
|
composeTestRule
|
||||||
|
@ -136,22 +146,22 @@ class GeneratorScreenTest : BaseComposeTest() {
|
||||||
.assertExists()
|
.assertExists()
|
||||||
|
|
||||||
composeTestRule
|
composeTestRule
|
||||||
.onNodeWithText("Uppercase (A to Z)")
|
.onNodeWithText("A—Z")
|
||||||
.performScrollTo()
|
.performScrollTo()
|
||||||
.assertIsDisplayed()
|
.assertIsDisplayed()
|
||||||
|
|
||||||
composeTestRule
|
composeTestRule
|
||||||
.onNodeWithText("Lowercase (A to Z)")
|
.onNodeWithText("a—z")
|
||||||
.performScrollTo()
|
.performScrollTo()
|
||||||
.assertIsDisplayed()
|
.assertIsDisplayed()
|
||||||
|
|
||||||
composeTestRule
|
composeTestRule
|
||||||
.onNodeWithText("Numbers (0 to 9)")
|
.onNodeWithText("0-9")
|
||||||
.performScrollTo()
|
.performScrollTo()
|
||||||
.assertIsDisplayed()
|
.assertIsDisplayed()
|
||||||
|
|
||||||
composeTestRule
|
composeTestRule
|
||||||
.onNodeWithText("Special characters (!@#$%^*)")
|
.onNodeWithText("!@#$%^&*")
|
||||||
.performScrollTo()
|
.performScrollTo()
|
||||||
.assertIsDisplayed()
|
.assertIsDisplayed()
|
||||||
|
|
||||||
|
@ -213,7 +223,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
||||||
GeneratorScreen(viewModel = viewModel)
|
GeneratorScreen(viewModel = viewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
composeTestRule.onNodeWithText("Uppercase (A to Z)")
|
composeTestRule.onNodeWithText("A—Z")
|
||||||
.performScrollTo()
|
.performScrollTo()
|
||||||
.performClick()
|
.performClick()
|
||||||
|
|
||||||
|
@ -232,7 +242,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
||||||
GeneratorScreen(viewModel = viewModel)
|
GeneratorScreen(viewModel = viewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
composeTestRule.onNodeWithText("Lowercase (A to Z)")
|
composeTestRule.onNodeWithText("a—z")
|
||||||
.performScrollTo()
|
.performScrollTo()
|
||||||
.performClick()
|
.performClick()
|
||||||
|
|
||||||
|
@ -251,7 +261,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
||||||
GeneratorScreen(viewModel = viewModel)
|
GeneratorScreen(viewModel = viewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
composeTestRule.onNodeWithText("Numbers (0 to 9)")
|
composeTestRule.onNodeWithText("0-9")
|
||||||
.performScrollTo()
|
.performScrollTo()
|
||||||
.performClick()
|
.performClick()
|
||||||
|
|
||||||
|
@ -270,7 +280,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
||||||
GeneratorScreen(viewModel = viewModel)
|
GeneratorScreen(viewModel = viewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
composeTestRule.onNodeWithText("Special characters (!@#$%^*)")
|
composeTestRule.onNodeWithText("!@#$%^&*")
|
||||||
.performScrollTo()
|
.performScrollTo()
|
||||||
.performClick()
|
.performClick()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue