mirror of
https://github.com/bitwarden/android.git
synced 2024-11-28 06:04:16 +03:00
PM-11176: Update generator to use segmented control (#4075)
This commit is contained in:
parent
2b87cdac9e
commit
efbf84238d
6 changed files with 958 additions and 1505 deletions
|
@ -46,6 +46,7 @@ fun BitwardenSegmentedButton(
|
|||
) {
|
||||
options.forEachIndexed { index, option ->
|
||||
SegmentedButton(
|
||||
enabled = option.isEnabled,
|
||||
selected = option.isChecked,
|
||||
onClick = option.onClick,
|
||||
colors = bitwardenSegmentedButtonColors(),
|
||||
|
@ -74,5 +75,6 @@ data class SegmentedButtonState(
|
|||
val text: String,
|
||||
val onClick: () -> Unit,
|
||||
val isChecked: Boolean,
|
||||
val isEnabled: Boolean = true,
|
||||
val testTag: String? = null,
|
||||
)
|
||||
|
|
|
@ -20,6 +20,6 @@ fun bitwardenSegmentedButtonColors(): SegmentedButtonColors = SegmentedButtonCol
|
|||
disabledActiveContentColor = BitwardenTheme.colorScheme.filledButton.foregroundDisabled,
|
||||
disabledActiveBorderColor = Color.Transparent,
|
||||
disabledInactiveContainerColor = BitwardenTheme.colorScheme.background.primary,
|
||||
disabledInactiveContentColor = BitwardenTheme.colorScheme.filledButton.foregroundDisabled,
|
||||
disabledInactiveContentColor = BitwardenTheme.colorScheme.stroke.divider,
|
||||
disabledInactiveBorderColor = Color.Transparent,
|
||||
)
|
||||
|
|
|
@ -36,6 +36,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.LivecycleEventEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.scrolledContainerBottomDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenMediumTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.action.BitwardenOverflowActionItem
|
||||
|
@ -49,7 +50,10 @@ import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
|||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextFieldWithActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.TooltipData
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.TopAppBarDividerStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.segment.BitwardenSegmentedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.segment.SegmentedButtonState
|
||||
import com.x8bit.bitwarden.ui.platform.components.slider.BitwardenSlider
|
||||
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarHost
|
||||
import com.x8bit.bitwarden.ui.platform.components.stepper.BitwardenStepper
|
||||
|
@ -59,8 +63,8 @@ import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
|||
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Passcode.PasscodeType.Passphrase.Companion.PASSPHRASE_MAX_NUMBER_OF_WORDS
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Passcode.PasscodeType.Passphrase.Companion.PASSPHRASE_MIN_NUMBER_OF_WORDS
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Passphrase.Companion.PASSPHRASE_MAX_NUMBER_OF_WORDS
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Passphrase.Companion.PASSPHRASE_MIN_NUMBER_OF_WORDS
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias.ServiceType
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias.ServiceTypeOption
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
|
@ -129,17 +133,6 @@ fun GeneratorScreen(
|
|||
{ viewModel.trySendAction(GeneratorAction.MainTypeOptionSelect(it)) }
|
||||
}
|
||||
|
||||
val onPasscodeOptionClicked: (GeneratorState.MainType.Passcode.PasscodeTypeOption) -> Unit =
|
||||
remember(viewModel) {
|
||||
{
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeTypeOptionSelect(
|
||||
it,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val onUsernameOptionClicked: (GeneratorState.MainType.Username.UsernameTypeOption) -> Unit =
|
||||
remember(viewModel) {
|
||||
{
|
||||
|
@ -183,9 +176,7 @@ fun GeneratorScreen(
|
|||
BitwardenScaffold(
|
||||
topBar = {
|
||||
when (state.generatorMode) {
|
||||
is GeneratorMode.Modal.Username,
|
||||
GeneratorMode.Modal.Password,
|
||||
-> {
|
||||
is GeneratorMode.Modal -> {
|
||||
ModalAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
onCloseClick = remember(viewModel) {
|
||||
|
@ -212,12 +203,21 @@ fun GeneratorScreen(
|
|||
},
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
) { innerPadding ->
|
||||
Column(modifier = Modifier.padding(innerPadding)) {
|
||||
if (state.generatorMode == GeneratorMode.Default) {
|
||||
MainStateOptionsItem(
|
||||
selectedType = state.selectedType,
|
||||
passcodePolicyOverride = state.passcodePolicyOverride,
|
||||
possibleMainStates = state.typeOptions.toImmutableList(),
|
||||
onMainStateOptionClicked = onMainStateOptionClicked,
|
||||
modifier = Modifier
|
||||
.scrolledContainerBottomDivider(topAppBarScrollBehavior = scrollBehavior),
|
||||
)
|
||||
}
|
||||
ScrollContent(
|
||||
state = state,
|
||||
onRegenerateClick = onRegenerateClick,
|
||||
onCopyClick = onCopyClick,
|
||||
onMainStateOptionClicked = onMainStateOptionClicked,
|
||||
onPasscodeSubStateOptionClicked = onPasscodeOptionClicked,
|
||||
onUsernameSubStateOptionClicked = onUsernameOptionClicked,
|
||||
passwordHandlers = passwordHandlers,
|
||||
passphraseHandlers = passphraseHandlers,
|
||||
|
@ -226,9 +226,9 @@ fun GeneratorScreen(
|
|||
plusAddressedEmailHandlers = plusAddressedEmailHandlers,
|
||||
catchAllEmailHandlers = catchAllEmailHandlers,
|
||||
randomWordHandlers = randomWordHandlers,
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//region Top App Bar Composables
|
||||
|
@ -242,6 +242,7 @@ private fun DefaultAppBar(
|
|||
BitwardenMediumTopAppBar(
|
||||
title = stringResource(id = R.string.generator),
|
||||
scrollBehavior = scrollBehavior,
|
||||
dividerStyle = TopAppBarDividerStyle.NONE,
|
||||
actions = {
|
||||
BitwardenOverflowActionItem(
|
||||
menuItemDataList = persistentListOf(
|
||||
|
@ -288,8 +289,6 @@ private fun ScrollContent(
|
|||
state: GeneratorState,
|
||||
onRegenerateClick: () -> Unit,
|
||||
onCopyClick: () -> Unit,
|
||||
onMainStateOptionClicked: (GeneratorState.MainTypeOption) -> Unit,
|
||||
onPasscodeSubStateOptionClicked: (GeneratorState.MainType.Passcode.PasscodeTypeOption) -> Unit,
|
||||
onUsernameSubStateOptionClicked: (GeneratorState.MainType.Username.UsernameTypeOption) -> Unit,
|
||||
passwordHandlers: PasswordHandlers,
|
||||
passphraseHandlers: PassphraseHandlers,
|
||||
|
@ -305,8 +304,8 @@ private fun ScrollContent(
|
|||
.fillMaxHeight()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
|
||||
if (state.isUnderPolicy) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenInfoCalloutCard(
|
||||
text = stringResource(id = R.string.password_generator_policy_in_effect),
|
||||
modifier = Modifier
|
||||
|
@ -324,15 +323,6 @@ private fun ScrollContent(
|
|||
onRegenerateClick = onRegenerateClick,
|
||||
)
|
||||
|
||||
if (state.generatorMode == GeneratorMode.Default) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
MainStateOptionsItem(
|
||||
selectedType = state.selectedType,
|
||||
possibleMainStates = state.typeOptions.toImmutableList(),
|
||||
onMainStateOptionClicked = onMainStateOptionClicked,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
BitwardenListHeaderText(
|
||||
|
@ -345,13 +335,17 @@ private fun ScrollContent(
|
|||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
when (val selectedType = state.selectedType) {
|
||||
is GeneratorState.MainType.Passcode -> {
|
||||
PasscodeTypeItems(
|
||||
passcodeState = selectedType,
|
||||
onSubStateOptionClicked = onPasscodeSubStateOptionClicked,
|
||||
passwordHandlers = passwordHandlers,
|
||||
is GeneratorState.MainType.Passphrase -> {
|
||||
PassphraseTypeContent(
|
||||
passphraseTypeState = selectedType,
|
||||
passphraseHandlers = passphraseHandlers,
|
||||
overridePasswordPolicyRestriction = state.overridePassword,
|
||||
)
|
||||
}
|
||||
|
||||
is GeneratorState.MainType.Password -> {
|
||||
PasswordTypeContent(
|
||||
passwordTypeState = selectedType,
|
||||
passwordHandlers = passwordHandlers,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -407,92 +401,55 @@ private fun GeneratedStringItem(
|
|||
@Composable
|
||||
private fun MainStateOptionsItem(
|
||||
selectedType: GeneratorState.MainType,
|
||||
passcodePolicyOverride: GeneratorState.PasscodePolicyOverride?,
|
||||
possibleMainStates: ImmutableList<GeneratorState.MainTypeOption>,
|
||||
onMainStateOptionClicked: (GeneratorState.MainTypeOption) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val optionsWithStrings = possibleMainStates.associateWith { stringResource(id = it.labelRes) }
|
||||
BitwardenSegmentedButton(
|
||||
options = possibleMainStates
|
||||
.map { mainOptionType ->
|
||||
SegmentedButtonState(
|
||||
text = stringResource(id = mainOptionType.labelRes),
|
||||
onClick = { onMainStateOptionClicked(mainOptionType) },
|
||||
isChecked = selectedType.mainTypeOption == mainOptionType,
|
||||
isEnabled = when (mainOptionType) {
|
||||
GeneratorState.MainTypeOption.PASSWORD -> {
|
||||
when (passcodePolicyOverride) {
|
||||
GeneratorState.PasscodePolicyOverride.PASSWORD -> true
|
||||
GeneratorState.PasscodePolicyOverride.PASSPHRASE -> false
|
||||
null -> true
|
||||
}
|
||||
}
|
||||
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.what_would_you_like_to_generate),
|
||||
options = optionsWithStrings.values.toImmutableList(),
|
||||
selectedOption = stringResource(id = selectedType.displayStringResId),
|
||||
onOptionSelected = { selectedOption ->
|
||||
val selectedOptionId =
|
||||
optionsWithStrings.entries.first { it.value == selectedOption }.key
|
||||
onMainStateOptionClicked(selectedOptionId)
|
||||
GeneratorState.MainTypeOption.PASSPHRASE -> {
|
||||
when (passcodePolicyOverride) {
|
||||
GeneratorState.PasscodePolicyOverride.PASSWORD -> false
|
||||
GeneratorState.PasscodePolicyOverride.PASSPHRASE -> true
|
||||
null -> true
|
||||
}
|
||||
}
|
||||
|
||||
GeneratorState.MainTypeOption.USERNAME -> true
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
testTag = mainOptionType.testTag,
|
||||
)
|
||||
}
|
||||
.toImmutableList(),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("GeneratorTypePicker"),
|
||||
.testTag(tag = "GeneratorTypePicker"),
|
||||
)
|
||||
}
|
||||
|
||||
//endregion ScrollContent and Static Items
|
||||
|
||||
//region PasscodeType Composables
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.PasscodeTypeItems(
|
||||
passcodeState: GeneratorState.MainType.Passcode,
|
||||
onSubStateOptionClicked: (GeneratorState.MainType.Passcode.PasscodeTypeOption) -> Unit,
|
||||
passwordHandlers: PasswordHandlers,
|
||||
passphraseHandlers: PassphraseHandlers,
|
||||
overridePasswordPolicyRestriction: Boolean,
|
||||
) {
|
||||
PasscodeOptionsItem(passcodeState, onSubStateOptionClicked, overridePasswordPolicyRestriction)
|
||||
|
||||
when (val selectedType = passcodeState.selectedType) {
|
||||
is GeneratorState.MainType.Passcode.PasscodeType.Password -> {
|
||||
PasswordTypeContent(
|
||||
passwordTypeState = selectedType,
|
||||
passwordHandlers = passwordHandlers,
|
||||
)
|
||||
}
|
||||
|
||||
is GeneratorState.MainType.Passcode.PasscodeType.Passphrase -> {
|
||||
PassphraseTypeContent(
|
||||
passphraseTypeState = selectedType,
|
||||
passphraseHandlers = passphraseHandlers,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PasscodeOptionsItem(
|
||||
currentSubState: GeneratorState.MainType.Passcode,
|
||||
onSubStateOptionClicked: (GeneratorState.MainType.Passcode.PasscodeTypeOption) -> Unit,
|
||||
overridePasswordPolicyRestriction: Boolean,
|
||||
) {
|
||||
val possibleSubStates = GeneratorState.MainType.Passcode.PasscodeTypeOption.entries
|
||||
val optionsWithStrings = possibleSubStates.associateWith { stringResource(id = it.labelRes) }
|
||||
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.password_type),
|
||||
options = optionsWithStrings.values.toImmutableList(),
|
||||
selectedOption = stringResource(id = currentSubState.selectedType.displayStringResId),
|
||||
onOptionSelected = { selectedOption ->
|
||||
val selectedOptionId =
|
||||
optionsWithStrings.entries.first { it.value == selectedOption }.key
|
||||
onSubStateOptionClicked(selectedOptionId)
|
||||
},
|
||||
isEnabled = !overridePasswordPolicyRestriction,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth()
|
||||
.testTag("PasswordTypePicker"),
|
||||
)
|
||||
}
|
||||
|
||||
//endregion PasscodeType Composables
|
||||
|
||||
//region PasswordType Composables
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun ColumnScope.PasswordTypeContent(
|
||||
passwordTypeState: GeneratorState.MainType.Passcode.PasscodeType.Password,
|
||||
passwordTypeState: GeneratorState.MainType.Password,
|
||||
passwordHandlers: PasswordHandlers,
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
@ -707,7 +664,7 @@ private fun PasswordAvoidAmbiguousCharsToggleItem(
|
|||
|
||||
@Composable
|
||||
private fun ColumnScope.PassphraseTypeContent(
|
||||
passphraseTypeState: GeneratorState.MainType.Passcode.PasscodeType.Passphrase,
|
||||
passphraseTypeState: GeneratorState.MainType.Passphrase,
|
||||
passphraseHandlers: PassphraseHandlers,
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
@ -1232,8 +1189,7 @@ private data class PasswordHandlers(
|
|||
return PasswordHandlers(
|
||||
onPasswordSliderLengthChange = { newLength, isUserInteracting ->
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password
|
||||
.SliderLengthChange(
|
||||
GeneratorAction.MainType.Password.SliderLengthChange(
|
||||
length = newLength,
|
||||
isUserInteracting = isUserInteracting,
|
||||
),
|
||||
|
@ -1241,56 +1197,49 @@ private data class PasswordHandlers(
|
|||
},
|
||||
onPasswordToggleCapitalLettersChange = { shouldUseCapitals ->
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password
|
||||
.ToggleCapitalLettersChange(
|
||||
GeneratorAction.MainType.Password.ToggleCapitalLettersChange(
|
||||
useCapitals = shouldUseCapitals,
|
||||
),
|
||||
)
|
||||
},
|
||||
onPasswordToggleLowercaseLettersChange = { shouldUseLowercase ->
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password
|
||||
.ToggleLowercaseLettersChange(
|
||||
GeneratorAction.MainType.Password.ToggleLowercaseLettersChange(
|
||||
useLowercase = shouldUseLowercase,
|
||||
),
|
||||
)
|
||||
},
|
||||
onPasswordToggleNumbersChange = { shouldUseNumbers ->
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password
|
||||
.ToggleNumbersChange(
|
||||
GeneratorAction.MainType.Password.ToggleNumbersChange(
|
||||
useNumbers = shouldUseNumbers,
|
||||
),
|
||||
)
|
||||
},
|
||||
onPasswordToggleSpecialCharactersChange = { shouldUseSpecialChars ->
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password
|
||||
.ToggleSpecialCharactersChange(
|
||||
GeneratorAction.MainType.Password.ToggleSpecialCharactersChange(
|
||||
useSpecialChars = shouldUseSpecialChars,
|
||||
),
|
||||
)
|
||||
},
|
||||
onPasswordMinNumbersCounterChange = { newMinNumbers ->
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password
|
||||
.MinNumbersCounterChange(
|
||||
GeneratorAction.MainType.Password.MinNumbersCounterChange(
|
||||
minNumbers = newMinNumbers,
|
||||
),
|
||||
)
|
||||
},
|
||||
onPasswordMinSpecialCharactersChange = { newMinSpecial ->
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password
|
||||
.MinSpecialCharactersChange(
|
||||
GeneratorAction.MainType.Password.MinSpecialCharactersChange(
|
||||
minSpecial = newMinSpecial,
|
||||
),
|
||||
)
|
||||
},
|
||||
onPasswordToggleAvoidAmbiguousCharsChange = { shouldAvoidAmbiguousChars ->
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password
|
||||
.ToggleAvoidAmbigousCharactersChange(
|
||||
GeneratorAction.MainType.Password.ToggleAvoidAmbigousCharactersChange(
|
||||
avoidAmbiguousChars = shouldAvoidAmbiguousChars,
|
||||
),
|
||||
)
|
||||
|
@ -1317,32 +1266,28 @@ private data class PassphraseHandlers(
|
|||
return PassphraseHandlers(
|
||||
onPassphraseNumWordsCounterChange = { changeInCounter ->
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase
|
||||
.NumWordsCounterChange(
|
||||
GeneratorAction.MainType.Passphrase.NumWordsCounterChange(
|
||||
numWords = changeInCounter,
|
||||
),
|
||||
)
|
||||
},
|
||||
onPassphraseWordSeparatorChange = { newSeparator ->
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase
|
||||
.WordSeparatorTextChange(
|
||||
GeneratorAction.MainType.Passphrase.WordSeparatorTextChange(
|
||||
wordSeparator = newSeparator,
|
||||
),
|
||||
)
|
||||
},
|
||||
onPassphraseCapitalizeToggleChange = { shouldCapitalize ->
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase
|
||||
.ToggleCapitalizeChange(
|
||||
GeneratorAction.MainType.Passphrase.ToggleCapitalizeChange(
|
||||
capitalize = shouldCapitalize,
|
||||
),
|
||||
)
|
||||
},
|
||||
onPassphraseIncludeNumberToggleChange = { shouldIncludeNumber ->
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase
|
||||
.ToggleIncludeNumberChange(
|
||||
GeneratorAction.MainType.Passphrase.ToggleIncludeNumberChange(
|
||||
includeNumber = shouldIncludeNumber,
|
||||
),
|
||||
)
|
||||
|
|
|
@ -30,11 +30,6 @@ import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
|||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.orNullIfBlank
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Passcode
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Passcode.PasscodeType.Passphrase
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Passcode.PasscodeType.Password
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Passcode.PasscodeTypeOption
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.CatchAllEmail
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias.ServiceType.AddyIo
|
||||
|
@ -87,16 +82,18 @@ class GeneratorViewModel @Inject constructor(
|
|||
generatedText = NO_GENERATED_TEXT,
|
||||
selectedType = when (generatorMode) {
|
||||
is GeneratorMode.Modal.Username -> {
|
||||
val type = generatorRepository.getUsernameGenerationOptions().usernameType
|
||||
Username(selectedType = type)
|
||||
GeneratorState.MainType.Username(
|
||||
selectedType = generatorRepository
|
||||
.getUsernameGenerationOptions()
|
||||
.usernameType,
|
||||
)
|
||||
}
|
||||
|
||||
GeneratorMode.Modal.Password -> {
|
||||
val type = generatorRepository.getPasscodeGenerationOptions().passcodeType
|
||||
Passcode(selectedType = type)
|
||||
generatorRepository.getPasscodeGenerationOptions().passcodeType
|
||||
}
|
||||
|
||||
GeneratorMode.Default -> Passcode(selectedType = Password())
|
||||
GeneratorMode.Default -> GeneratorState.MainType.Password()
|
||||
},
|
||||
generatorMode = generatorMode,
|
||||
currentEmailAddress = requireNotNull(
|
||||
|
@ -147,15 +144,11 @@ class GeneratorViewModel @Inject constructor(
|
|||
@Suppress("MaxLineLength")
|
||||
private fun handleMainTypeAction(action: GeneratorAction.MainType) {
|
||||
when (action) {
|
||||
is GeneratorAction.MainType.Passcode.PasscodeTypeOptionSelect -> {
|
||||
handlePasscodeTypeOptionSelect(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Password -> {
|
||||
is GeneratorAction.MainType.Password -> {
|
||||
handlePasswordSpecificAction(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Passphrase -> {
|
||||
is GeneratorAction.MainType.Passphrase -> {
|
||||
handlePassphraseSpecificAction(action)
|
||||
}
|
||||
|
||||
|
@ -255,13 +248,15 @@ class GeneratorViewModel @Inject constructor(
|
|||
|
||||
private fun handleSelectClick() {
|
||||
when (state.selectedType) {
|
||||
is Passcode -> {
|
||||
is GeneratorState.MainType.Passphrase,
|
||||
is GeneratorState.MainType.Password,
|
||||
-> {
|
||||
generatorRepository.emitGeneratorResult(
|
||||
GeneratorResult.Password(state.generatedText),
|
||||
)
|
||||
}
|
||||
|
||||
is Username -> {
|
||||
is GeneratorState.MainType.Username -> {
|
||||
generatorRepository.emitGeneratorResult(
|
||||
GeneratorResult.Username(state.generatedText),
|
||||
)
|
||||
|
@ -276,12 +271,13 @@ class GeneratorViewModel @Inject constructor(
|
|||
|
||||
private fun loadOptions(shouldUseStorageOptions: Boolean = false) {
|
||||
when (val selectedType = state.selectedType) {
|
||||
is Passcode -> {
|
||||
is GeneratorState.MainType.Passphrase,
|
||||
is GeneratorState.MainType.Password,
|
||||
-> {
|
||||
val mainType = if (shouldUseStorageOptions) {
|
||||
generatorRepository
|
||||
.getPasscodeGenerationOptions()
|
||||
?.passcodeType
|
||||
?.let { Passcode(it) }
|
||||
?: selectedType
|
||||
} else {
|
||||
selectedType
|
||||
|
@ -289,12 +285,12 @@ class GeneratorViewModel @Inject constructor(
|
|||
loadPasscodeOptions(selectedType = mainType)
|
||||
}
|
||||
|
||||
is Username -> {
|
||||
is GeneratorState.MainType.Username -> {
|
||||
val mainType = if (shouldUseStorageOptions) {
|
||||
generatorRepository
|
||||
.getUsernameGenerationOptions()
|
||||
?.usernameType
|
||||
?.let { Username(it) }
|
||||
?.let { GeneratorState.MainType.Username(it) }
|
||||
?: selectedType
|
||||
} else {
|
||||
selectedType
|
||||
|
@ -308,32 +304,51 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod", "LongMethod")
|
||||
private fun loadPasscodeOptions(selectedType: Passcode) {
|
||||
val options = generatorRepository.getPasscodeGenerationOptions()
|
||||
private fun loadPasscodeOptions(selectedType: GeneratorState.MainType) {
|
||||
if (selectedType is GeneratorState.MainType.Username) {
|
||||
// This can only handle passwords and passphrases
|
||||
return
|
||||
}
|
||||
val options = generatorRepository
|
||||
.getPasscodeGenerationOptions()
|
||||
?: generatePasscodeDefaultOptions()
|
||||
|
||||
val policy = policyManager
|
||||
.getActivePolicies<PolicyInformation.PasswordGenerator>()
|
||||
.toStrictestPolicy()
|
||||
|
||||
val passwordType = if (!policy.overridePasswordType.isNullOrBlank()) {
|
||||
val passwordType = when (policy.overridePasswordType.toPasscodePolicyOverride()) {
|
||||
GeneratorState.PasscodePolicyOverride.PASSWORD -> {
|
||||
mutableStateFlow.update {
|
||||
it.copy(overridePassword = true)
|
||||
}
|
||||
Passcode(
|
||||
selectedType = policy.overridePasswordType.toSelectedType(),
|
||||
it.copy(
|
||||
passcodePolicyOverride = GeneratorState.PasscodePolicyOverride.PASSWORD,
|
||||
)
|
||||
} else {
|
||||
mutableStateFlow.update {
|
||||
it.copy(overridePassword = false)
|
||||
}
|
||||
selectedType
|
||||
GeneratorState.MainType.Password()
|
||||
}
|
||||
|
||||
when (passwordType.selectedType) {
|
||||
is Passphrase -> {
|
||||
val minNumWords = policy.minNumberWords ?: Passphrase.PASSPHRASE_MIN_NUMBER_OF_WORDS
|
||||
val passphrase = Passphrase(
|
||||
GeneratorState.PasscodePolicyOverride.PASSPHRASE -> {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
passcodePolicyOverride = GeneratorState.PasscodePolicyOverride.PASSPHRASE,
|
||||
)
|
||||
}
|
||||
GeneratorState.MainType.Passphrase()
|
||||
}
|
||||
|
||||
null -> {
|
||||
mutableStateFlow.update { it.copy(passcodePolicyOverride = null) }
|
||||
selectedType
|
||||
}
|
||||
}
|
||||
|
||||
when (passwordType) {
|
||||
is GeneratorState.MainType.Passphrase -> {
|
||||
val minNumWords = policy
|
||||
.minNumberWords
|
||||
?: GeneratorState.MainType.Passphrase.PASSPHRASE_MIN_NUMBER_OF_WORDS
|
||||
updateGeneratorMainType {
|
||||
GeneratorState.MainType.Passphrase(
|
||||
numWords = max(options.numWords, minNumWords),
|
||||
minNumWords = minNumWords,
|
||||
wordSeparator = options.wordSeparator.toCharArray().first(),
|
||||
|
@ -342,14 +357,15 @@ class GeneratorViewModel @Inject constructor(
|
|||
includeNumber = options.allowIncludeNumber || policy.includeNumber == true,
|
||||
includeNumberEnabled = policy.includeNumber != true,
|
||||
)
|
||||
updateGeneratorMainType {
|
||||
Passcode(selectedType = passphrase)
|
||||
}
|
||||
}
|
||||
|
||||
is Password -> {
|
||||
val minLength = policy.minLength ?: Password.PASSWORD_LENGTH_SLIDER_MIN
|
||||
val password = Password(
|
||||
is GeneratorState.MainType.Password -> {
|
||||
val minLength = policy
|
||||
.minLength
|
||||
?: GeneratorState.MainType.Password.PASSWORD_LENGTH_SLIDER_MIN
|
||||
updateGeneratorMainType {
|
||||
GeneratorState.MainType.Password(
|
||||
length = max(options.length, minLength),
|
||||
minLength = minLength,
|
||||
useCapitals = options.hasUppercase || policy.useUpper == true,
|
||||
|
@ -366,14 +382,17 @@ class GeneratorViewModel @Inject constructor(
|
|||
minSpecialAllowed = policy.minSpecial ?: 0,
|
||||
avoidAmbiguousChars = options.allowAmbiguousChar,
|
||||
)
|
||||
updateGeneratorMainType {
|
||||
Passcode(selectedType = password)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadUsernameOptions(selectedType: Username, forceRegeneration: Boolean = false) {
|
||||
is GeneratorState.MainType.Username -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadUsernameOptions(
|
||||
selectedType: GeneratorState.MainType.Username,
|
||||
forceRegeneration: Boolean = false,
|
||||
) {
|
||||
val options = generatorRepository.getUsernameGenerationOptions()
|
||||
val updatedSelectedType = when (val type = selectedType.selectedType) {
|
||||
is PlusAddressedEmail -> {
|
||||
|
@ -382,14 +401,16 @@ class GeneratorViewModel @Inject constructor(
|
|||
?.orNullIfBlank()
|
||||
?: state.currentEmailAddress
|
||||
|
||||
Username(selectedType = PlusAddressedEmail(email = emailToUse))
|
||||
GeneratorState.MainType.Username(
|
||||
selectedType = PlusAddressedEmail(email = emailToUse),
|
||||
)
|
||||
}
|
||||
|
||||
is CatchAllEmail -> {
|
||||
val catchAllEmail = CatchAllEmail(
|
||||
domainName = options?.catchAllEmailDomain ?: type.domainName,
|
||||
)
|
||||
Username(selectedType = catchAllEmail)
|
||||
GeneratorState.MainType.Username(selectedType = catchAllEmail)
|
||||
}
|
||||
|
||||
is RandomWord -> {
|
||||
|
@ -397,7 +418,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
capitalize = options?.capitalizeRandomWordUsername ?: type.capitalize,
|
||||
includeNumber = options?.includeNumberRandomWordUsername ?: type.includeNumber,
|
||||
)
|
||||
Username(selectedType = randomWord)
|
||||
GeneratorState.MainType.Username(selectedType = randomWord)
|
||||
}
|
||||
|
||||
is ForwardedEmailAlias -> {
|
||||
|
@ -406,7 +427,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
?.toServiceType(options)
|
||||
?: type.selectedServiceType
|
||||
|
||||
Username(
|
||||
GeneratorState.MainType.Username(
|
||||
selectedType = ForwardedEmailAlias(
|
||||
selectedServiceType = mappedServiceType,
|
||||
obfuscatedText = "",
|
||||
|
@ -418,7 +439,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
updateGeneratorMainType(forceRegeneration = forceRegeneration) { updatedSelectedType }
|
||||
}
|
||||
|
||||
private fun savePasswordOptionsToDisk(password: Password) {
|
||||
private fun savePasswordOptionsToDisk(password: GeneratorState.MainType.Password) {
|
||||
val options = generatorRepository
|
||||
.getPasscodeGenerationOptions() ?: generatePasscodeDefaultOptions()
|
||||
val newOptions = options.copy(
|
||||
|
@ -435,7 +456,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
generatorRepository.savePasscodeGenerationOptions(newOptions)
|
||||
}
|
||||
|
||||
private fun savePassphraseOptionsToDisk(passphrase: Passphrase) {
|
||||
private fun savePassphraseOptionsToDisk(passphrase: GeneratorState.MainType.Passphrase) {
|
||||
val options = generatorRepository
|
||||
.getPasscodeGenerationOptions() ?: generatePasscodeDefaultOptions()
|
||||
val newOptions = options.copy(
|
||||
|
@ -532,8 +553,8 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun generatePasscodeDefaultOptions(): PasscodeGenerationOptions {
|
||||
val defaultPassword = Password()
|
||||
val defaultPassphrase = Passphrase()
|
||||
val defaultPassword = GeneratorState.MainType.Password()
|
||||
val defaultPassphrase = GeneratorState.MainType.Passphrase()
|
||||
|
||||
return PasscodeGenerationOptions(
|
||||
type = PasscodeGenerationOptions.PasscodeType.PASSWORD,
|
||||
|
@ -572,7 +593,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun generatePassword(password: Password) {
|
||||
private suspend fun generatePassword(password: GeneratorState.MainType.Password) {
|
||||
val request = PasswordGeneratorRequest(
|
||||
lowercase = password.useLowercase,
|
||||
uppercase = password.useCapitals,
|
||||
|
@ -591,7 +612,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
sendAction(GeneratorAction.Internal.UpdateGeneratedPasswordResult(result))
|
||||
}
|
||||
|
||||
private suspend fun generatePassphrase(passphrase: Passphrase) {
|
||||
private suspend fun generatePassphrase(passphrase: GeneratorState.MainType.Passphrase) {
|
||||
val request = PassphraseGeneratorRequest(
|
||||
numWords = passphrase.numWords.toUByte(),
|
||||
wordSeparator = passphrase.wordSeparator?.toString() ?: " ",
|
||||
|
@ -736,16 +757,17 @@ class GeneratorViewModel @Inject constructor(
|
|||
private fun handleMainTypeOptionSelect(action: GeneratorAction.MainTypeOptionSelect) {
|
||||
when (action.mainTypeOption) {
|
||||
GeneratorState.MainTypeOption.PASSWORD -> {
|
||||
val type = generatorRepository.getPasscodeGenerationOptions().passcodeType
|
||||
loadPasscodeOptions(
|
||||
selectedType = Passcode(selectedType = type),
|
||||
)
|
||||
loadPasscodeOptions(selectedType = GeneratorState.MainType.Password())
|
||||
}
|
||||
|
||||
GeneratorState.MainTypeOption.PASSPHRASE -> {
|
||||
loadPasscodeOptions(selectedType = GeneratorState.MainType.Passphrase())
|
||||
}
|
||||
|
||||
GeneratorState.MainTypeOption.USERNAME -> {
|
||||
val type = generatorRepository.getUsernameGenerationOptions().usernameType
|
||||
loadUsernameOptions(
|
||||
selectedType = Username(selectedType = type),
|
||||
selectedType = GeneratorState.MainType.Username(selectedType = type),
|
||||
forceRegeneration = true,
|
||||
)
|
||||
}
|
||||
|
@ -754,76 +776,46 @@ class GeneratorViewModel @Inject constructor(
|
|||
|
||||
//endregion Main Type Option Handlers
|
||||
|
||||
//region Passcode Type Handlers
|
||||
|
||||
private fun handlePasscodeTypeOptionSelect(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeTypeOptionSelect,
|
||||
) {
|
||||
when (action.passcodeTypeOption) {
|
||||
PasscodeTypeOption.PASSWORD -> loadPasscodeOptions(
|
||||
selectedType = Passcode(selectedType = Password()),
|
||||
)
|
||||
|
||||
PasscodeTypeOption.PASSPHRASE -> loadPasscodeOptions(
|
||||
selectedType = Passcode(selectedType = Passphrase()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//endregion Passcode Type Handlers
|
||||
|
||||
//region Password Specific Handlers
|
||||
|
||||
private fun handlePasswordSpecificAction(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Password,
|
||||
) {
|
||||
private fun handlePasswordSpecificAction(action: GeneratorAction.MainType.Password) {
|
||||
when (action) {
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Password.SliderLengthChange,
|
||||
-> {
|
||||
is GeneratorAction.MainType.Password.SliderLengthChange -> {
|
||||
handlePasswordLengthSliderChange(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Password.ToggleCapitalLettersChange,
|
||||
-> {
|
||||
is GeneratorAction.MainType.Password.ToggleCapitalLettersChange -> {
|
||||
handleToggleCapitalLetters(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Password.ToggleLowercaseLettersChange,
|
||||
-> {
|
||||
is GeneratorAction.MainType.Password.ToggleLowercaseLettersChange -> {
|
||||
handleToggleLowercaseLetters(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Password.ToggleNumbersChange,
|
||||
-> {
|
||||
is GeneratorAction.MainType.Password.ToggleNumbersChange -> {
|
||||
handleToggleNumbers(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Password
|
||||
.ToggleSpecialCharactersChange,
|
||||
-> {
|
||||
is GeneratorAction.MainType.Password.ToggleSpecialCharactersChange -> {
|
||||
handleToggleSpecialChars(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Password.MinNumbersCounterChange,
|
||||
-> {
|
||||
is GeneratorAction.MainType.Password.MinNumbersCounterChange -> {
|
||||
handleMinNumbersChange(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Password.MinSpecialCharactersChange,
|
||||
-> {
|
||||
is GeneratorAction.MainType.Password.MinSpecialCharactersChange -> {
|
||||
handleMinSpecialChange(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Password
|
||||
.ToggleAvoidAmbigousCharactersChange,
|
||||
-> {
|
||||
is GeneratorAction.MainType.Password.ToggleAvoidAmbigousCharactersChange -> {
|
||||
handleToggleAmbiguousChars(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePasswordLengthSliderChange(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Password.SliderLengthChange,
|
||||
action: GeneratorAction.MainType.Password.SliderLengthChange,
|
||||
) {
|
||||
val adjustedLength = action.length
|
||||
|
||||
|
@ -836,7 +828,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleToggleCapitalLetters(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Password.ToggleCapitalLettersChange,
|
||||
action: GeneratorAction.MainType.Password.ToggleCapitalLettersChange,
|
||||
) {
|
||||
updatePasswordType { currentPasswordType ->
|
||||
currentPasswordType.copy(
|
||||
|
@ -847,8 +839,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleToggleLowercaseLetters(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Password
|
||||
.ToggleLowercaseLettersChange,
|
||||
action: GeneratorAction.MainType.Password.ToggleLowercaseLettersChange,
|
||||
) {
|
||||
updatePasswordType { currentPasswordType ->
|
||||
currentPasswordType.copy(
|
||||
|
@ -859,7 +850,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleToggleNumbers(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Password.ToggleNumbersChange,
|
||||
action: GeneratorAction.MainType.Password.ToggleNumbersChange,
|
||||
) {
|
||||
updatePasswordType { currentPasswordType ->
|
||||
currentPasswordType.copy(
|
||||
|
@ -870,8 +861,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleToggleSpecialChars(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Password
|
||||
.ToggleSpecialCharactersChange,
|
||||
action: GeneratorAction.MainType.Password.ToggleSpecialCharactersChange,
|
||||
) {
|
||||
updatePasswordType { currentPasswordType ->
|
||||
currentPasswordType.copy(
|
||||
|
@ -882,7 +872,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleMinNumbersChange(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Password.MinNumbersCounterChange,
|
||||
action: GeneratorAction.MainType.Password.MinNumbersCounterChange,
|
||||
) {
|
||||
updatePasswordType { currentPasswordType ->
|
||||
currentPasswordType.copy(
|
||||
|
@ -893,7 +883,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleMinSpecialChange(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Password.MinSpecialCharactersChange,
|
||||
action: GeneratorAction.MainType.Password.MinSpecialCharactersChange,
|
||||
) {
|
||||
updatePasswordType { currentPasswordType ->
|
||||
currentPasswordType.copy(
|
||||
|
@ -904,8 +894,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleToggleAmbiguousChars(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Password
|
||||
.ToggleAvoidAmbigousCharactersChange,
|
||||
action: GeneratorAction.MainType.Password.ToggleAvoidAmbigousCharactersChange,
|
||||
) {
|
||||
updatePasswordType { currentPasswordType ->
|
||||
currentPasswordType.copy(
|
||||
|
@ -919,33 +908,29 @@ class GeneratorViewModel @Inject constructor(
|
|||
//region Passphrase Specific Handlers
|
||||
|
||||
private fun handlePassphraseSpecificAction(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Passphrase,
|
||||
action: GeneratorAction.MainType.Passphrase,
|
||||
) {
|
||||
when (action) {
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.NumWordsCounterChange,
|
||||
-> {
|
||||
is GeneratorAction.MainType.Passphrase.NumWordsCounterChange -> {
|
||||
handleNumWordsCounterChange(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.ToggleCapitalizeChange,
|
||||
-> {
|
||||
is GeneratorAction.MainType.Passphrase.ToggleCapitalizeChange -> {
|
||||
handlePassphraseToggleCapitalizeChange(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.ToggleIncludeNumberChange,
|
||||
-> {
|
||||
is GeneratorAction.MainType.Passphrase.ToggleIncludeNumberChange -> {
|
||||
handlePassphraseToggleIncludeNumberChange(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.WordSeparatorTextChange,
|
||||
-> {
|
||||
is GeneratorAction.MainType.Passphrase.WordSeparatorTextChange -> {
|
||||
handleWordSeparatorTextInputChange(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePassphraseToggleCapitalizeChange(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.ToggleCapitalizeChange,
|
||||
action: GeneratorAction.MainType.Passphrase.ToggleCapitalizeChange,
|
||||
) {
|
||||
updatePassphraseType { currentPassphraseType ->
|
||||
currentPassphraseType.copy(
|
||||
|
@ -955,7 +940,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handlePassphraseToggleIncludeNumberChange(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.ToggleIncludeNumberChange,
|
||||
action: GeneratorAction.MainType.Passphrase.ToggleIncludeNumberChange,
|
||||
) {
|
||||
updatePassphraseType { currentPassphraseType ->
|
||||
currentPassphraseType.copy(
|
||||
|
@ -965,7 +950,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleNumWordsCounterChange(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.NumWordsCounterChange,
|
||||
action: GeneratorAction.MainType.Passphrase.NumWordsCounterChange,
|
||||
) {
|
||||
updatePassphraseType { passphraseType ->
|
||||
passphraseType.copy(numWords = action.numWords)
|
||||
|
@ -973,7 +958,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleWordSeparatorTextInputChange(
|
||||
action: GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.WordSeparatorTextChange,
|
||||
action: GeneratorAction.MainType.Passphrase.WordSeparatorTextChange,
|
||||
) {
|
||||
updatePassphraseType { passphraseType ->
|
||||
val newWordSeparator =
|
||||
|
@ -990,25 +975,37 @@ class GeneratorViewModel @Inject constructor(
|
|||
action: GeneratorAction.MainType.Username.UsernameTypeOptionSelect,
|
||||
) {
|
||||
when (action.usernameTypeOption) {
|
||||
Username.UsernameTypeOption.PLUS_ADDRESSED_EMAIL -> loadUsernameOptions(
|
||||
selectedType = Username(selectedType = PlusAddressedEmail()),
|
||||
GeneratorState.MainType.Username.UsernameTypeOption.PLUS_ADDRESSED_EMAIL -> {
|
||||
loadUsernameOptions(
|
||||
selectedType = GeneratorState.MainType.Username(
|
||||
selectedType = PlusAddressedEmail(),
|
||||
),
|
||||
forceRegeneration = true,
|
||||
)
|
||||
}
|
||||
|
||||
Username.UsernameTypeOption.CATCH_ALL_EMAIL -> loadUsernameOptions(
|
||||
selectedType = Username(selectedType = CatchAllEmail()),
|
||||
GeneratorState.MainType.Username.UsernameTypeOption.CATCH_ALL_EMAIL -> {
|
||||
loadUsernameOptions(
|
||||
selectedType = GeneratorState.MainType.Username(
|
||||
selectedType = CatchAllEmail(),
|
||||
),
|
||||
forceRegeneration = true,
|
||||
)
|
||||
}
|
||||
|
||||
// We do not force regeneration here since the API can fail if the data is entered
|
||||
// incorrectly. This will only be generated when the user clicks the regenerate button.
|
||||
Username.UsernameTypeOption.FORWARDED_EMAIL_ALIAS -> loadUsernameOptions(
|
||||
selectedType = Username(selectedType = ForwardedEmailAlias()),
|
||||
GeneratorState.MainType.Username.UsernameTypeOption.FORWARDED_EMAIL_ALIAS -> {
|
||||
loadUsernameOptions(
|
||||
selectedType = GeneratorState.MainType.Username(
|
||||
selectedType = ForwardedEmailAlias(),
|
||||
),
|
||||
forceRegeneration = false,
|
||||
)
|
||||
}
|
||||
|
||||
Username.UsernameTypeOption.RANDOM_WORD -> loadUsernameOptions(
|
||||
selectedType = Username(selectedType = RandomWord()),
|
||||
GeneratorState.MainType.Username.UsernameTypeOption.RANDOM_WORD -> loadUsernameOptions(
|
||||
selectedType = GeneratorState.MainType.Username(selectedType = RandomWord()),
|
||||
forceRegeneration = true,
|
||||
)
|
||||
}
|
||||
|
@ -1371,25 +1368,26 @@ class GeneratorViewModel @Inject constructor(
|
|||
generateTextJob.cancel()
|
||||
generateTextJob = viewModelScope.launch {
|
||||
when (updatedMainType) {
|
||||
is Passcode -> when (val selectedType = updatedMainType.selectedType) {
|
||||
is Passphrase -> {
|
||||
savePassphraseOptionsToDisk(selectedType)
|
||||
generatePassphrase(selectedType)
|
||||
is GeneratorState.MainType.Passphrase -> {
|
||||
savePassphraseOptionsToDisk(updatedMainType)
|
||||
generatePassphrase(updatedMainType)
|
||||
}
|
||||
|
||||
is Password -> {
|
||||
savePasswordOptionsToDisk(selectedType)
|
||||
generatePassword(selectedType)
|
||||
}
|
||||
is GeneratorState.MainType.Password -> {
|
||||
savePasswordOptionsToDisk(updatedMainType)
|
||||
generatePassword(updatedMainType)
|
||||
}
|
||||
|
||||
is Username -> when (val selectedType = updatedMainType.selectedType) {
|
||||
is GeneratorState.MainType.Username -> {
|
||||
when (val selectedType = updatedMainType.selectedType) {
|
||||
is ForwardedEmailAlias -> {
|
||||
saveForwardedEmailAliasServiceTypeToDisk(selectedType)
|
||||
if (forceRegeneration) {
|
||||
generateForwardedEmailAlias(selectedType)
|
||||
} else {
|
||||
mutableStateFlow.update { it.copy(generatedText = NO_GENERATED_TEXT) }
|
||||
mutableStateFlow.update {
|
||||
it.copy(generatedText = NO_GENERATED_TEXT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1398,7 +1396,9 @@ class GeneratorViewModel @Inject constructor(
|
|||
if (forceRegeneration) {
|
||||
generateCatchAllEmail(selectedType)
|
||||
} else {
|
||||
mutableStateFlow.update { it.copy(generatedText = NO_GENERATED_TEXT) }
|
||||
mutableStateFlow.update {
|
||||
it.copy(generatedText = NO_GENERATED_TEXT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1407,7 +1407,9 @@ class GeneratorViewModel @Inject constructor(
|
|||
if (forceRegeneration) {
|
||||
generatePlusAddressedEmail(selectedType)
|
||||
} else {
|
||||
mutableStateFlow.update { it.copy(generatedText = NO_GENERATED_TEXT) }
|
||||
mutableStateFlow.update {
|
||||
it.copy(generatedText = NO_GENERATED_TEXT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1419,6 +1421,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun generateForwardedEmailAlias(alias: ForwardedEmailAlias) {
|
||||
val request = alias.selectedServiceType?.toUsernameGeneratorRequest(state.website) ?: run {
|
||||
|
@ -1463,16 +1466,9 @@ class GeneratorViewModel @Inject constructor(
|
|||
sendAction(GeneratorAction.Internal.UpdateGeneratedRandomWordUsernameResult(result))
|
||||
}
|
||||
|
||||
private inline fun updateGeneratorMainTypePasscode(
|
||||
crossinline block: (Passcode) -> Passcode,
|
||||
) {
|
||||
updateGeneratorMainType {
|
||||
if (it !is Passcode) null else block(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the length property of the [Password] to reflect the new minimum.
|
||||
* Updates the length property of the [GeneratorState.MainType.Password] to reflect the new
|
||||
* minimum.
|
||||
*/
|
||||
private fun updatePasswordLength() {
|
||||
updatePasswordType { currentPasswordType ->
|
||||
|
@ -1486,39 +1482,34 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private inline fun updatePasswordType(
|
||||
crossinline block: (Password) -> Password,
|
||||
crossinline block: (GeneratorState.MainType.Password) -> GeneratorState.MainType.Password,
|
||||
) {
|
||||
updateGeneratorMainTypePasscode { currentSelectedType ->
|
||||
val currentPasswordType = currentSelectedType.selectedType
|
||||
if (currentPasswordType !is Password) {
|
||||
return@updateGeneratorMainTypePasscode currentSelectedType
|
||||
updateGeneratorMainType { currentSelectedType ->
|
||||
if (currentSelectedType !is GeneratorState.MainType.Password) {
|
||||
return@updateGeneratorMainType currentSelectedType
|
||||
}
|
||||
|
||||
val updatedPasswordType = currentPasswordType
|
||||
.let(block)
|
||||
.enforceAtLeastOneToggleOn()
|
||||
|
||||
currentSelectedType.copy(selectedType = updatedPasswordType)
|
||||
currentSelectedType.let(block).enforceAtLeastOneToggleOn()
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun updatePassphraseType(
|
||||
crossinline block: (Passphrase) -> Passphrase,
|
||||
crossinline block: (
|
||||
GeneratorState.MainType.Passphrase,
|
||||
) -> GeneratorState.MainType.Passphrase,
|
||||
) {
|
||||
updateGeneratorMainTypePasscode { currentSelectedType ->
|
||||
val currentPasswordType = currentSelectedType.selectedType
|
||||
if (currentPasswordType !is Passphrase) {
|
||||
return@updateGeneratorMainTypePasscode currentSelectedType
|
||||
updateGeneratorMainType { currentSelectedType ->
|
||||
if (currentSelectedType !is GeneratorState.MainType.Passphrase) {
|
||||
return@updateGeneratorMainType currentSelectedType
|
||||
}
|
||||
currentSelectedType.copy(selectedType = block(currentPasswordType))
|
||||
block(currentSelectedType)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun updateGeneratorMainTypeUsername(
|
||||
crossinline block: (Username) -> Username,
|
||||
crossinline block: (GeneratorState.MainType.Username) -> GeneratorState.MainType.Username,
|
||||
) {
|
||||
updateGeneratorMainType {
|
||||
if (it !is Username) null else block(it)
|
||||
if (it !is GeneratorState.MainType.Username) null else block(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1731,7 +1722,7 @@ data class GeneratorState(
|
|||
val currentEmailAddress: String,
|
||||
val isUnderPolicy: Boolean = false,
|
||||
val website: String? = null,
|
||||
var overridePassword: Boolean = false,
|
||||
var passcodePolicyOverride: PasscodePolicyOverride? = null,
|
||||
) : Parcelable {
|
||||
|
||||
/**
|
||||
|
@ -1741,13 +1732,27 @@ data class GeneratorState(
|
|||
get() = MainTypeOption.entries.toList()
|
||||
|
||||
/**
|
||||
* Enum representing the main type options for the generator, such as PASSWORD and USERNAME.
|
||||
* Enum representing the main type options for the generator, such as PASSWORD PASSPHRASE, and
|
||||
* USERNAME.
|
||||
*
|
||||
* @property labelRes The resource ID of the string that represents the label of each type.
|
||||
* @property testTag The string used as the test tag for the option.
|
||||
*/
|
||||
enum class MainTypeOption(val labelRes: Int) {
|
||||
PASSWORD(R.string.password),
|
||||
USERNAME(R.string.username),
|
||||
enum class MainTypeOption(
|
||||
val labelRes: Int,
|
||||
val testTag: String,
|
||||
) {
|
||||
PASSWORD(labelRes = R.string.password, testTag = "password_option"),
|
||||
PASSPHRASE(labelRes = R.string.passphrase, testTag = "passphrase_option"),
|
||||
USERNAME(labelRes = R.string.username, testTag = "username_option"),
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum representing the passcode types for the generator, such as PASSWORD and PASSPHRASE.
|
||||
*/
|
||||
enum class PasscodePolicyOverride {
|
||||
PASSWORD,
|
||||
PASSPHRASE,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1758,50 +1763,9 @@ data class GeneratorState(
|
|||
sealed class MainType : Parcelable {
|
||||
|
||||
/**
|
||||
* Represents the resource ID for the display string. This is an abstract property
|
||||
* that must be overridden by each subclass to provide the appropriate string resource ID
|
||||
* for display purposes.
|
||||
* Indicates the type this is.
|
||||
*/
|
||||
abstract val displayStringResId: Int
|
||||
|
||||
/**
|
||||
* Represents the Passcode main type, allowing the user to specify the kind of passcode,
|
||||
* such as a Password or a Passphrase, and configure their respective properties.
|
||||
*
|
||||
* @property selectedType The currently selected PasscodeType
|
||||
*/
|
||||
@Parcelize
|
||||
data class Passcode(
|
||||
val selectedType: PasscodeType = Password(),
|
||||
) : MainType(), Parcelable {
|
||||
override val displayStringResId: Int
|
||||
get() = MainTypeOption.PASSWORD.labelRes
|
||||
|
||||
/**
|
||||
* Enum representing the types of passcodes,
|
||||
* allowing for different passcode configurations.
|
||||
*
|
||||
* @property labelRes The ID of the string that represents the label for each type.
|
||||
*/
|
||||
enum class PasscodeTypeOption(val labelRes: Int) {
|
||||
PASSWORD(R.string.password),
|
||||
PASSPHRASE(R.string.passphrase),
|
||||
}
|
||||
|
||||
/**
|
||||
* A sealed class representing the different types of PASSWORD,
|
||||
* such as standard Password and Passphrase, each with its own properties.
|
||||
*/
|
||||
@Parcelize
|
||||
sealed class PasscodeType : Parcelable {
|
||||
|
||||
/**
|
||||
* Represents the resource ID for the display string specific to each
|
||||
* PasscodeType subclass. Every subclass of PasscodeType must override
|
||||
* this property to provide the appropriate string resource ID for
|
||||
* its display string.
|
||||
*/
|
||||
abstract val displayStringResId: Int
|
||||
abstract val mainTypeOption: MainTypeOption
|
||||
|
||||
/**
|
||||
* Represents a standard PASSWORD type, with configurable options for
|
||||
|
@ -1852,9 +1816,8 @@ data class GeneratorState(
|
|||
val avoidAmbiguousChars: Boolean = false,
|
||||
val ambiguousCharsEnabled: Boolean = true,
|
||||
val isUserInteracting: Boolean = false,
|
||||
) : PasscodeType(), Parcelable {
|
||||
override val displayStringResId: Int
|
||||
get() = PasscodeTypeOption.PASSWORD.labelRes
|
||||
) : MainType() {
|
||||
override val mainTypeOption: MainTypeOption get() = MainTypeOption.PASSWORD
|
||||
|
||||
/**
|
||||
* The computed minimum length for the generated Password
|
||||
|
@ -1904,9 +1867,8 @@ data class GeneratorState(
|
|||
val capitalizeEnabled: Boolean = true,
|
||||
val includeNumber: Boolean = false,
|
||||
val includeNumberEnabled: Boolean = true,
|
||||
) : PasscodeType(), Parcelable {
|
||||
override val displayStringResId: Int
|
||||
get() = PasscodeTypeOption.PASSPHRASE.labelRes
|
||||
) : MainType() {
|
||||
override val mainTypeOption: MainTypeOption get() = MainTypeOption.PASSPHRASE
|
||||
|
||||
@Suppress("UndocumentedPublicClass")
|
||||
companion object {
|
||||
|
@ -1917,8 +1879,6 @@ data class GeneratorState(
|
|||
const val PASSPHRASE_MAX_NUMBER_OF_WORDS: Int = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the USERNAME main type, holding the configuration
|
||||
|
@ -1927,9 +1887,8 @@ data class GeneratorState(
|
|||
@Parcelize
|
||||
data class Username(
|
||||
val selectedType: UsernameType = PlusAddressedEmail(),
|
||||
) : MainType(), Parcelable {
|
||||
override val displayStringResId: Int
|
||||
get() = MainTypeOption.USERNAME.labelRes
|
||||
) : MainType() {
|
||||
override val mainTypeOption: MainTypeOption get() = MainTypeOption.USERNAME
|
||||
|
||||
/**
|
||||
* Enum representing the types of usernames,
|
||||
|
@ -2209,29 +2168,10 @@ sealed class GeneratorAction {
|
|||
*/
|
||||
sealed class MainType : GeneratorAction() {
|
||||
|
||||
/**
|
||||
* Represents actions specifically related to [GeneratorState.MainType.Passcode].
|
||||
*/
|
||||
sealed class Passcode : MainType() {
|
||||
|
||||
/**
|
||||
* Represents the action of selecting a passcode type option.
|
||||
*
|
||||
* @property passcodeTypeOption The selected passcode type option.
|
||||
*/
|
||||
data class PasscodeTypeOptionSelect(
|
||||
val passcodeTypeOption: PasscodeTypeOption,
|
||||
) : Passcode()
|
||||
|
||||
/**
|
||||
* Represents actions related to the different types of passcodes.
|
||||
*/
|
||||
sealed class PasscodeType : Passcode() {
|
||||
|
||||
/**
|
||||
* Represents actions specifically related to passwords, a subtype of passcode.
|
||||
*/
|
||||
sealed class Password : PasscodeType() {
|
||||
sealed class Password : MainType() {
|
||||
/**
|
||||
* Represents a change action for the length of the password,
|
||||
* adjusted using a slider.
|
||||
|
@ -2324,7 +2264,7 @@ sealed class GeneratorAction {
|
|||
/**
|
||||
* Represents actions specifically related to passphrases, a subtype of passcode.
|
||||
*/
|
||||
sealed class Passphrase : PasscodeType() {
|
||||
sealed class Passphrase : MainType() {
|
||||
|
||||
/**
|
||||
* Fired when the number of words counter is changed.
|
||||
|
@ -2354,8 +2294,6 @@ sealed class GeneratorAction {
|
|||
*/
|
||||
data class ToggleIncludeNumberChange(val includeNumber: Boolean) : Passphrase()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents actions related to the [GeneratorState.MainType.Username] in the generator.
|
||||
|
@ -2631,8 +2569,8 @@ sealed class GeneratorEvent {
|
|||
) : GeneratorEvent()
|
||||
}
|
||||
|
||||
@Suppress("ComplexCondition")
|
||||
private fun Password.enforceAtLeastOneToggleOn(): Password =
|
||||
@Suppress("ComplexCondition", "MaxLineLength")
|
||||
private fun GeneratorState.MainType.Password.enforceAtLeastOneToggleOn(): GeneratorState.MainType.Password =
|
||||
// If all toggles are off, turn on useLowercase
|
||||
if (!this.useCapitals &&
|
||||
!this.useLowercase &&
|
||||
|
@ -2644,14 +2582,14 @@ private fun Password.enforceAtLeastOneToggleOn(): Password =
|
|||
this
|
||||
}
|
||||
|
||||
private val PasscodeGenerationOptions?.passcodeType: Passcode.PasscodeType
|
||||
private val PasscodeGenerationOptions?.passcodeType: GeneratorState.MainType
|
||||
get() = when (this?.type) {
|
||||
PasscodeGenerationOptions.PasscodeType.PASSWORD -> Password()
|
||||
PasscodeGenerationOptions.PasscodeType.PASSPHRASE -> Passphrase()
|
||||
else -> Password()
|
||||
PasscodeGenerationOptions.PasscodeType.PASSWORD -> GeneratorState.MainType.Password()
|
||||
PasscodeGenerationOptions.PasscodeType.PASSPHRASE -> GeneratorState.MainType.Passphrase()
|
||||
else -> GeneratorState.MainType.Password()
|
||||
}
|
||||
|
||||
private val UsernameGenerationOptions?.usernameType: Username.UsernameType
|
||||
private val UsernameGenerationOptions?.usernameType: GeneratorState.MainType.Username.UsernameType
|
||||
get() = when (this?.type) {
|
||||
UsernameGenerationOptions.UsernameType.PLUS_ADDRESSED_EMAIL -> PlusAddressedEmail()
|
||||
UsernameGenerationOptions.UsernameType.CATCH_ALL_EMAIL -> CatchAllEmail()
|
||||
|
@ -2660,8 +2598,15 @@ private val UsernameGenerationOptions?.usernameType: Username.UsernameType
|
|||
else -> PlusAddressedEmail()
|
||||
}
|
||||
|
||||
private fun String?.toSelectedType(): Passcode.PasscodeType =
|
||||
private fun String?.toPasscodePolicyOverride(): GeneratorState.PasscodePolicyOverride? =
|
||||
if (this.isNullOrBlank()) {
|
||||
null
|
||||
} else {
|
||||
when (this) {
|
||||
PolicyInformation.PasswordGenerator.TYPE_PASSPHRASE -> Passphrase()
|
||||
else -> Password()
|
||||
PolicyInformation.PasswordGenerator.TYPE_PASSPHRASE -> {
|
||||
GeneratorState.PasscodePolicyOverride.PASSPHRASE
|
||||
}
|
||||
|
||||
else -> GeneratorState.PasscodePolicyOverride.PASSWORD
|
||||
}
|
||||
}
|
||||
|
|
|
@ -205,15 +205,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
fun `clicking a MainStateOption should send MainTypeOptionSelect action`() {
|
||||
// Opens the menu
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Password. What would you like to generate?")
|
||||
.performClick()
|
||||
|
||||
// Choose the option from the menu
|
||||
composeTestRule
|
||||
.onAllNodesWithText(text = "Password")
|
||||
.onLast()
|
||||
.performScrollTo()
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.onNodeWithText(text = "Password")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
|
@ -228,45 +220,13 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
.assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking a PasscodeOption should send PasscodeTypeOption action`() {
|
||||
// Opens the menu
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Password. Password type")
|
||||
.performClick()
|
||||
|
||||
// Choose the option from the menu
|
||||
composeTestRule
|
||||
.onAllNodesWithText(text = "Passphrase")
|
||||
.onLast()
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeTypeOptionSelect(
|
||||
GeneratorState.MainType.Passcode.PasscodeTypeOption.PASSPHRASE,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Make sure dialog is hidden:
|
||||
composeTestRule
|
||||
.onNode(isDialog())
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `clicking a UsernameOption should send UsernameTypeOption action`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Username(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Username
|
||||
.UsernameType
|
||||
.PlusAddressedEmail(),
|
||||
GeneratorState.MainType.Username.UsernameType.PlusAddressedEmail(),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -302,15 +262,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
//region Passcode Password Tests
|
||||
|
||||
@Test
|
||||
fun `in Passcode_Password state, the ViewModel state should update the UI correctly`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Password. What would you like to generate?")
|
||||
.assertIsDisplayed()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Password. Password type")
|
||||
.assertIsDisplayed()
|
||||
|
||||
fun `in Password state, the ViewModel state should update the UI correctly`() {
|
||||
composeTestRule
|
||||
.onNode(
|
||||
expectValue(
|
||||
|
@ -363,7 +315,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, adjusting the slider should send SliderLengthChange action with length not equal to default`() {
|
||||
fun `in Password state, adjusting the slider should send SliderLengthChange action with length not equal to default`() {
|
||||
composeTestRule
|
||||
.onNodeWithText("Length")
|
||||
.onSiblings()
|
||||
|
@ -383,7 +335,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.SliderLengthChange(
|
||||
GeneratorAction.MainType.Password.SliderLengthChange(
|
||||
length = 128,
|
||||
isUserInteracting = true,
|
||||
),
|
||||
|
@ -396,7 +348,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
// actually get updated from its original value, and thus will be its original value of 14.
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.SliderLengthChange(
|
||||
GeneratorAction.MainType.Password.SliderLengthChange(
|
||||
length = 14,
|
||||
isUserInteracting = false,
|
||||
),
|
||||
|
@ -406,14 +358,14 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, toggling the capital letters toggle should send ToggleCapitalLettersChange action`() {
|
||||
fun `in Password state, toggling the capital letters toggle should send ToggleCapitalLettersChange action`() {
|
||||
composeTestRule.onNodeWithText("A—Z")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.ToggleCapitalLettersChange(
|
||||
GeneratorAction.MainType.Password.ToggleCapitalLettersChange(
|
||||
useCapitals = false,
|
||||
),
|
||||
)
|
||||
|
@ -422,19 +374,14 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, toggling the use lowercase toggle should send ToggleLowercaseLettersChange action`() {
|
||||
fun `in Password state, toggling the use lowercase toggle should send ToggleLowercaseLettersChange action`() {
|
||||
composeTestRule.onNodeWithText("a—z")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleLowercaseLettersChange(
|
||||
GeneratorAction.MainType.Password.ToggleLowercaseLettersChange(
|
||||
useLowercase = false,
|
||||
),
|
||||
)
|
||||
|
@ -443,35 +390,28 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, toggling the use numbers toggle should send ToggleNumbersChange action`() {
|
||||
fun `in Password state, toggling the use numbers toggle should send ToggleNumbersChange action`() {
|
||||
composeTestRule.onNodeWithText("0-9")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.ToggleNumbersChange(
|
||||
useNumbers = false,
|
||||
),
|
||||
GeneratorAction.MainType.Password.ToggleNumbersChange(useNumbers = false),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, toggling the use special characters toggle should send ToggleSpecialCharactersChange action`() {
|
||||
fun `in Password state, toggling the use special characters toggle should send ToggleSpecialCharactersChange action`() {
|
||||
composeTestRule.onNodeWithText("!@#$%^&*")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleSpecialCharactersChange(
|
||||
GeneratorAction.MainType.Password.ToggleSpecialCharactersChange(
|
||||
useSpecialChars = true,
|
||||
),
|
||||
)
|
||||
|
@ -480,7 +420,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, decrementing the minimum numbers counter should send MinNumbersCounterChange action`() {
|
||||
fun `in Password state, decrementing the minimum numbers counter should send MinNumbersCounterChange action`() {
|
||||
val initialMinNumbers = 1
|
||||
|
||||
composeTestRule.onNodeWithText("Minimum numbers")
|
||||
|
@ -492,7 +432,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.MinNumbersCounterChange(
|
||||
GeneratorAction.MainType.Password.MinNumbersCounterChange(
|
||||
minNumbers = initialMinNumbers - 1,
|
||||
),
|
||||
)
|
||||
|
@ -501,7 +441,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, incrementing the minimum numbers counter should send MinNumbersCounterChange action`() {
|
||||
fun `in Password state, incrementing the minimum numbers counter should send MinNumbersCounterChange action`() {
|
||||
val initialMinNumbers = 1
|
||||
|
||||
composeTestRule.onNodeWithText("Minimum numbers")
|
||||
|
@ -513,28 +453,19 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.MinNumbersCounterChange(
|
||||
GeneratorAction.MainType.Password.MinNumbersCounterChange(
|
||||
minNumbers = initialMinNumbers + 1,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, decrementing the minimum numbers counter below 0 should do nothing`() {
|
||||
fun `in Password state, decrementing the minimum numbers counter below 0 should do nothing`() {
|
||||
val initialMinNumbers = 0
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password(
|
||||
minNumbers = initialMinNumbers,
|
||||
),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Password(minNumbers = initialMinNumbers),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -549,21 +480,12 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
verify(exactly = 1) { viewModel.trySendAction(any()) }
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, incrementing the minimum numbers counter above 9 should do nothing`() {
|
||||
fun `in Password state, incrementing the minimum numbers counter above 9 should do nothing`() {
|
||||
val initialMinNumbers = 9
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password(
|
||||
minNumbers = initialMinNumbers,
|
||||
),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Password(minNumbers = initialMinNumbers),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -580,7 +502,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, decrementing the minimum special characters counter should send MinSpecialCharactersChange action`() {
|
||||
fun `in Password state, decrementing the minimum special characters counter should send MinSpecialCharactersChange action`() {
|
||||
val initialSpecialChars = 1
|
||||
|
||||
composeTestRule.onNodeWithText("Minimum special")
|
||||
|
@ -592,7 +514,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.MinSpecialCharactersChange(
|
||||
GeneratorAction.MainType.Password.MinSpecialCharactersChange(
|
||||
minSpecial = initialSpecialChars - 1,
|
||||
),
|
||||
)
|
||||
|
@ -601,7 +523,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, incrementing the minimum special characters counter should send MinSpecialCharactersChange action`() {
|
||||
fun `in Password state, incrementing the minimum special characters counter should send MinSpecialCharactersChange action`() {
|
||||
val initialSpecialChars = 1
|
||||
|
||||
composeTestRule.onNodeWithText("Minimum special")
|
||||
|
@ -613,7 +535,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.MinSpecialCharactersChange(
|
||||
GeneratorAction.MainType.Password.MinSpecialCharactersChange(
|
||||
minSpecial = initialSpecialChars + 1,
|
||||
),
|
||||
)
|
||||
|
@ -622,19 +544,11 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, decrementing the minimum special characters below 0 should do nothing`() {
|
||||
fun `in Password state, decrementing the minimum special characters below 0 should do nothing`() {
|
||||
val initialSpecialChars = 0
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password(
|
||||
minSpecial = initialSpecialChars,
|
||||
),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Password(minSpecial = initialSpecialChars),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -650,19 +564,11 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, decrementing the minimum special characters above 9 should do nothing`() {
|
||||
fun `in Password state, decrementing the minimum special characters above 9 should do nothing`() {
|
||||
val initialSpecialChars = 9
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password(
|
||||
minSpecial = initialSpecialChars,
|
||||
),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Password(minSpecial = initialSpecialChars),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -678,19 +584,14 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, toggling the use avoid ambiguous characters toggle should send ToggleSpecialCharactersChange action`() {
|
||||
fun `in Password state, toggling the use avoid ambiguous characters toggle should send ToggleSpecialCharactersChange action`() {
|
||||
composeTestRule.onNodeWithText("Avoid ambiguous characters")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleAvoidAmbigousCharactersChange(
|
||||
GeneratorAction.MainType.Password.ToggleAvoidAmbigousCharactersChange(
|
||||
avoidAmbiguousChars = true,
|
||||
),
|
||||
)
|
||||
|
@ -698,15 +599,10 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `in Passcode_Password state, disabled elements should not send events`() {
|
||||
fun `in Password state, disabled elements should not send events`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
capitalsEnabled = false,
|
||||
lowercaseEnabled = false,
|
||||
numbersEnabled = false,
|
||||
|
@ -714,7 +610,6 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
ambiguousCharsEnabled = false,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule.onNodeWithText("A—Z")
|
||||
|
@ -735,52 +630,23 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify(exactly = 0) {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleCapitalLettersChange(
|
||||
useCapitals = false,
|
||||
),
|
||||
GeneratorAction.MainType.Password.ToggleCapitalLettersChange(useCapitals = false),
|
||||
)
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleLowercaseLettersChange(
|
||||
GeneratorAction.MainType.Password.ToggleLowercaseLettersChange(
|
||||
useLowercase = false,
|
||||
),
|
||||
)
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleNumbersChange(
|
||||
useNumbers = false,
|
||||
),
|
||||
GeneratorAction.MainType.Password.ToggleNumbersChange(useNumbers = false),
|
||||
)
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleSpecialCharactersChange(
|
||||
GeneratorAction.MainType.Password.ToggleSpecialCharactersChange(
|
||||
useSpecialChars = true,
|
||||
),
|
||||
)
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleAvoidAmbigousCharactersChange(
|
||||
GeneratorAction.MainType.Password.ToggleAvoidAmbigousCharactersChange(
|
||||
avoidAmbiguousChars = true,
|
||||
),
|
||||
)
|
||||
|
@ -788,21 +654,15 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `in Passcode_Password state, minimum numbers cannot go below minimum threshold`() {
|
||||
fun `in Password state, minimum numbers cannot go below minimum threshold`() {
|
||||
val initialMinNumbers = 5
|
||||
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
minNumbersAllowed = initialMinNumbers,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule.onNodeWithText("Minimum numbers")
|
||||
|
@ -814,7 +674,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify(exactly = 0) {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.MinNumbersCounterChange(
|
||||
GeneratorAction.MainType.Password.MinNumbersCounterChange(
|
||||
minNumbers = 4,
|
||||
),
|
||||
)
|
||||
|
@ -822,23 +682,17 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `in Passcode_Password state, maximum numbers should match minimum if lower`() {
|
||||
fun `in Password state, maximum numbers should match minimum if lower`() {
|
||||
val initialMinNumbers = 7
|
||||
val initialMaxNumbers = 5
|
||||
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
minNumbersAllowed = initialMinNumbers,
|
||||
maxNumbersAllowed = initialMaxNumbers,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
|
@ -850,23 +704,16 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Password state, minimum special characters cannot go below minimum threshold`() {
|
||||
fun `in Password state, minimum special characters cannot go below minimum threshold`() {
|
||||
val initialMinSpecials = 5
|
||||
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
minSpecialAllowed = initialMinSpecials,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule.onNodeWithText("Minimum special")
|
||||
|
@ -878,7 +725,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify(exactly = 0) {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.MinSpecialCharactersChange(
|
||||
GeneratorAction.MainType.Password.MinSpecialCharactersChange(
|
||||
minSpecial = 4,
|
||||
),
|
||||
)
|
||||
|
@ -886,23 +733,17 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `in Passcode_Password state, maximum special should match minimum if lower `() {
|
||||
fun `in Password state, maximum special should match minimum if lower `() {
|
||||
val initialMinSpecials = 7
|
||||
val initialMaxSpecials = 5
|
||||
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
minSpecialAllowed = initialMinSpecials,
|
||||
maxSpecialAllowed = initialMaxSpecials,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule.onNodeWithText("Minimum special")
|
||||
|
@ -914,20 +755,14 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `in Passcode_Passphrase state, disabled elements should not send events`() {
|
||||
fun `in Passphrase state, disabled elements should not send events`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase(
|
||||
selectedType = GeneratorState.MainType.Passphrase(
|
||||
capitalizeEnabled = false,
|
||||
includeNumberEnabled = false,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule.onNodeWithText("Capitalize")
|
||||
|
@ -939,44 +774,25 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify(exactly = 0) {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase
|
||||
.ToggleCapitalizeChange(
|
||||
GeneratorAction.MainType.Passphrase.ToggleCapitalizeChange(
|
||||
capitalize = true,
|
||||
),
|
||||
)
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase
|
||||
.ToggleIncludeNumberChange(
|
||||
GeneratorAction.MainType.Passphrase.ToggleIncludeNumberChange(
|
||||
includeNumber = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_passphrase state, minimum number of words cannot go below minimum threshold`() {
|
||||
fun `in Passphrase state, minimum number of words cannot go below minimum threshold`() {
|
||||
val initialMinWords = 5
|
||||
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase(
|
||||
minNumWords = initialMinWords,
|
||||
),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Passphrase(minNumWords = initialMinWords),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -989,9 +805,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify(exactly = 0) {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.NumWordsCounterChange(
|
||||
numWords = 4,
|
||||
),
|
||||
GeneratorAction.MainType.Passphrase.NumWordsCounterChange(numWords = 4),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1002,19 +816,11 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Passphrase state, decrementing number of words should send NumWordsCounterChange action with decremented value`() {
|
||||
fun `in Passphrase state, decrementing number of words should send NumWordsCounterChange action with decremented value`() {
|
||||
val initialNumWords = 4
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase(
|
||||
numWords = initialNumWords,
|
||||
),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Passphrase(numWords = initialNumWords),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -1029,7 +835,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.NumWordsCounterChange(
|
||||
GeneratorAction.MainType.Passphrase.NumWordsCounterChange(
|
||||
numWords = initialNumWords - 1,
|
||||
),
|
||||
)
|
||||
|
@ -1037,19 +843,11 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `in Passcode_Passphrase state, decrementing number of words under 3 should do nothing`() {
|
||||
fun `in Passphrase state, decrementing number of words under 3 should do nothing`() {
|
||||
val initialNumWords = 3
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase(
|
||||
numWords = initialNumWords,
|
||||
),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Passphrase(numWords = initialNumWords),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -1066,19 +864,11 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `in Passcode_Passphrase state, incrementing number of words over 20 should do nothing`() {
|
||||
fun `in Passphrase state, incrementing number of words over 20 should do nothing`() {
|
||||
val initialNumWords = 20
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase(
|
||||
numWords = initialNumWords,
|
||||
),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Passphrase(numWords = initialNumWords),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -1096,17 +886,11 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Passphrase state, incrementing number of words should send NumWordsCounterChange action with incremented value`() {
|
||||
fun `in Passphrase state, incrementing number of words should send NumWordsCounterChange action with incremented value`() {
|
||||
val initialNumWords = 3
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase(),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Passphrase(),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -1120,25 +904,18 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.NumWordsCounterChange(
|
||||
GeneratorAction.MainType.Passphrase.NumWordsCounterChange(
|
||||
numWords = initialNumWords + 1,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Passphrase state, toggling capitalize should send ToggleCapitalizeChange action`() {
|
||||
fun `in Passphrase state, toggling capitalize should send ToggleCapitalizeChange action`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase(),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Passphrase(),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -1149,7 +926,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.ToggleCapitalizeChange(
|
||||
GeneratorAction.MainType.Passphrase.ToggleCapitalizeChange(
|
||||
capitalize = true,
|
||||
),
|
||||
)
|
||||
|
@ -1158,16 +935,10 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Passphrase state, toggling the include number toggle should send ToggleIncludeNumberChange action`() {
|
||||
fun `in Passphrase state, toggling the include number toggle should send ToggleIncludeNumberChange action`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase(),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Passphrase(),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -1177,7 +948,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.ToggleIncludeNumberChange(
|
||||
GeneratorAction.MainType.Passphrase.ToggleIncludeNumberChange(
|
||||
includeNumber = true,
|
||||
),
|
||||
)
|
||||
|
@ -1186,16 +957,10 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in Passcode_Passphrase state, updating text in word separator should send WordSeparatorTextChange action`() {
|
||||
fun `in Passphrase state, updating text in word separator should send WordSeparatorTextChange action`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase(),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Passphrase(),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -1206,7 +971,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Passphrase.WordSeparatorTextChange(
|
||||
GeneratorAction.MainType.Passphrase.WordSeparatorTextChange(
|
||||
wordSeparator = 'a',
|
||||
),
|
||||
)
|
||||
|
@ -1765,8 +1530,6 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
|
||||
private val DEFAULT_STATE = GeneratorState(
|
||||
generatedText = "",
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Password(),
|
||||
currentEmailAddress = "currentEmail",
|
||||
)
|
||||
|
|
|
@ -163,9 +163,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
fakeGeneratorRepository.savePasscodeGenerationOptions(passcodeGenerationOptions)
|
||||
val expected = GeneratorState(
|
||||
generatedText = "updatedPassphrase",
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
selectedType = GeneratorState.MainType.Passcode.PasscodeType.Passphrase(),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Passphrase(),
|
||||
generatorMode = GeneratorMode.Modal.Password,
|
||||
currentEmailAddress = "currentEmail",
|
||||
isUnderPolicy = false,
|
||||
|
@ -213,8 +211,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
mutablePolicyFlow.tryEmit(value = policies)
|
||||
assertEquals(
|
||||
initialPasscodeState.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
selectedType = GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = 14,
|
||||
minLength = 10,
|
||||
maxLength = 128,
|
||||
|
@ -236,7 +233,6 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
ambiguousCharsEnabled = true,
|
||||
isUserInteracting = false,
|
||||
),
|
||||
),
|
||||
isUnderPolicy = true,
|
||||
),
|
||||
awaitItem(),
|
||||
|
@ -247,8 +243,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
mutablePolicyFlow.tryEmit(value = emptyList())
|
||||
assertEquals(
|
||||
initialPasscodeState.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
selectedType = GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = 14,
|
||||
minLength = 5,
|
||||
maxLength = 128,
|
||||
|
@ -270,7 +265,6 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
ambiguousCharsEnabled = true,
|
||||
isUserInteracting = false,
|
||||
),
|
||||
),
|
||||
isUnderPolicy = false,
|
||||
),
|
||||
awaitItem(),
|
||||
|
@ -562,8 +556,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
val viewModel = createViewModel()
|
||||
assertEquals(
|
||||
initialPasscodeState.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = 14,
|
||||
minLength = 10,
|
||||
useCapitals = true,
|
||||
|
@ -581,7 +574,6 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
avoidAmbiguousChars = false,
|
||||
),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
@ -613,8 +605,8 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
} returns listOf(policy)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
val action = GeneratorAction.MainType.Passcode.PasscodeTypeOptionSelect(
|
||||
passcodeTypeOption = GeneratorState.MainType.Passcode.PasscodeTypeOption.PASSPHRASE,
|
||||
val action = GeneratorAction.MainTypeOptionSelect(
|
||||
mainTypeOption = GeneratorState.MainTypeOption.PASSPHRASE,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(action)
|
||||
|
@ -622,8 +614,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
assertEquals(
|
||||
initialPasscodeState.copy(
|
||||
generatedText = "updatedPassphrase",
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Passphrase(
|
||||
selectedType = GeneratorState.MainType.Passphrase(
|
||||
numWords = 5,
|
||||
minNumWords = 5,
|
||||
maxNumWords = 20,
|
||||
|
@ -633,7 +624,6 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
includeNumberEnabled = false,
|
||||
),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
@ -659,10 +649,8 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
assertEquals(
|
||||
initialPasscodeState.copy(
|
||||
generatedText = "updatedPassphrase",
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Passphrase(),
|
||||
),
|
||||
overridePassword = true,
|
||||
selectedType = GeneratorState.MainType.Passphrase(),
|
||||
passcodePolicyOverride = GeneratorState.PasscodePolicyOverride.PASSPHRASE,
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
|
@ -701,10 +689,8 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
assertEquals(
|
||||
initialPasscodeState.copy(
|
||||
generatedText = "defaultPassword",
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(),
|
||||
),
|
||||
overridePassword = true,
|
||||
selectedType = GeneratorState.MainType.Password(),
|
||||
passcodePolicyOverride = GeneratorState.PasscodePolicyOverride.PASSWORD,
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
|
@ -721,9 +707,8 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
viewModel.trySendAction(action)
|
||||
|
||||
val expectedState =
|
||||
initialPasscodeState.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(),
|
||||
val expectedState = initialPasscodeState.copy(
|
||||
selectedType = GeneratorState.MainType.Password(),
|
||||
generatedText = "updatedText",
|
||||
)
|
||||
|
||||
|
@ -754,54 +739,6 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `PasscodeTypeOptionSelect PASSWORD should switch to PasswordType`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
fakeGeneratorRepository.setMockGeneratePasswordResult(
|
||||
GeneratedPasswordResult.Success("updatedText"),
|
||||
)
|
||||
|
||||
val action = GeneratorAction.MainType.Passcode.PasscodeTypeOptionSelect(
|
||||
passcodeTypeOption = GeneratorState.MainType.Passcode.PasscodeTypeOption.PASSWORD,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(action)
|
||||
|
||||
val expectedState = initialPasscodeState.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
selectedType = GeneratorState.MainType.Passcode.PasscodeType.Password(),
|
||||
),
|
||||
generatedText = "updatedText",
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `PasscodeTypeOptionSelect PASSPHRASE should switch to PassphraseType`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
val updatedText = "updatedPassphrase"
|
||||
|
||||
fakeGeneratorRepository.setMockGeneratePasswordResult(
|
||||
GeneratedPasswordResult.Success(updatedText),
|
||||
)
|
||||
|
||||
val action = GeneratorAction.MainType.Passcode.PasscodeTypeOptionSelect(
|
||||
passcodeTypeOption = GeneratorState.MainType.Passcode.PasscodeTypeOption.PASSPHRASE,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(action)
|
||||
|
||||
val expectedState = initialPasscodeState.copy(
|
||||
generatedText = updatedText,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
selectedType = GeneratorState.MainType.Passcode.PasscodeType.Passphrase(),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `UsernameTypeOptionSelect PLUS_ADDRESSED_EMAIL should switch to PlusAddressedEmail type`() =
|
||||
runTest {
|
||||
|
@ -975,8 +912,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
),
|
||||
)
|
||||
val expectedState = initialState.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
selectedType = GeneratorState.MainType.Passcode.PasscodeType.Passphrase(
|
||||
selectedType = GeneratorState.MainType.Passphrase(
|
||||
numWords = 3,
|
||||
minNumWords = 3,
|
||||
maxNumWords = 20,
|
||||
|
@ -986,7 +922,6 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
includeNumber = false,
|
||||
includeNumberEnabled = true,
|
||||
),
|
||||
),
|
||||
generatedText = "updatedPassphrase",
|
||||
)
|
||||
viewModel.trySendAction(GeneratorAction.LifecycleResume)
|
||||
|
@ -1055,7 +990,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
val newLength = 16
|
||||
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.SliderLengthChange(
|
||||
GeneratorAction.MainType.Password.SliderLengthChange(
|
||||
length = newLength,
|
||||
isUserInteracting = false,
|
||||
),
|
||||
|
@ -1063,11 +998,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
val expectedState = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
length = newLength,
|
||||
),
|
||||
),
|
||||
selectedType = GeneratorState.MainType.Password(length = newLength),
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
|
@ -1084,7 +1015,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
// Update the length to something small enough that it updates on capitals toggle.
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.SliderLengthChange(
|
||||
GeneratorAction.MainType.Password.SliderLengthChange(
|
||||
length = 5,
|
||||
isUserInteracting = false,
|
||||
),
|
||||
|
@ -1092,12 +1023,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
// Set useCapitals to false initially to ensure length change is reflected.
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleCapitalLettersChange(
|
||||
GeneratorAction.MainType.Password.ToggleCapitalLettersChange(
|
||||
useCapitals = false,
|
||||
),
|
||||
)
|
||||
|
@ -1105,18 +1031,16 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
// Verify useCapitals is reflected and initial length is correct.
|
||||
val expectedState1 = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = 5,
|
||||
useCapitals = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
assertEquals(expectedState1, viewModel.stateFlow.value)
|
||||
|
||||
// Update minNumbers so that length will exceed 5 when useCapitals is enabled.
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.MinNumbersCounterChange(
|
||||
GeneratorAction.MainType.Password.MinNumbersCounterChange(
|
||||
minNumbers = 4,
|
||||
),
|
||||
)
|
||||
|
@ -1124,25 +1048,18 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
// Verify length is still 5, minNumbers is updated, and useCapital is still false.
|
||||
val expectedState2 = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = 5, // 0 uppercase + 1 lowercase + 4 numbers + 0 special chars
|
||||
minNumbers = 4,
|
||||
useCapitals = false,
|
||||
useNumbers = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
assertEquals(expectedState2, viewModel.stateFlow.value)
|
||||
|
||||
// Enable useCapitals.
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleCapitalLettersChange(
|
||||
GeneratorAction.MainType.Password.ToggleCapitalLettersChange(
|
||||
useCapitals = true,
|
||||
),
|
||||
)
|
||||
|
@ -1150,13 +1067,11 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
// Verify this has caused length to increase.
|
||||
val expectedState3 = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = 6, // 1 uppercase + 1 lowercase + 4 numbers + 0 special chars
|
||||
minNumbers = 4,
|
||||
useCapitals = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
assertEquals(expectedState3, viewModel.stateFlow.value)
|
||||
}
|
||||
|
@ -1172,7 +1087,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
// Update the length to something small enough that it updates on lowercase toggle.
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.SliderLengthChange(
|
||||
GeneratorAction.MainType.Password.SliderLengthChange(
|
||||
length = 5,
|
||||
isUserInteracting = false,
|
||||
),
|
||||
|
@ -1180,12 +1095,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
// Set useLowercase to false initially to ensure length change is reflected.
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleLowercaseLettersChange(
|
||||
GeneratorAction.MainType.Password.ToggleLowercaseLettersChange(
|
||||
useLowercase = false,
|
||||
),
|
||||
)
|
||||
|
@ -1193,18 +1103,16 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
// Verify useLowercase is reflected and initial length is correct.
|
||||
val expectedState1 = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = 5,
|
||||
useLowercase = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
assertEquals(expectedState1, viewModel.stateFlow.value)
|
||||
|
||||
// Update minNumbers so that length will exceed 5 when useLowercase is enabled.
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.MinNumbersCounterChange(
|
||||
GeneratorAction.MainType.Password.MinNumbersCounterChange(
|
||||
minNumbers = 4,
|
||||
),
|
||||
)
|
||||
|
@ -1212,25 +1120,18 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
// Verify length is still 5, minNumbers is updated, and useLowercase is still false.
|
||||
val expectedState2 = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = 5, // 1 uppercase + 0 lowercase + 4 numbers + 0 special chars
|
||||
minNumbers = 4,
|
||||
useLowercase = false,
|
||||
useNumbers = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
assertEquals(expectedState2, viewModel.stateFlow.value)
|
||||
|
||||
// Enable useLowercase.
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleLowercaseLettersChange(
|
||||
GeneratorAction.MainType.Password.ToggleLowercaseLettersChange(
|
||||
useLowercase = true,
|
||||
),
|
||||
)
|
||||
|
@ -1238,13 +1139,11 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
// Verify this has caused length to increase.
|
||||
val expectedState3 = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = 6, // 1 uppercase + 1 lowercase + 4 numbers + 0 special chars
|
||||
minNumbers = 4,
|
||||
useLowercase = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
assertEquals(expectedState3, viewModel.stateFlow.value)
|
||||
}
|
||||
|
@ -1259,18 +1158,16 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
val useNumbers = true
|
||||
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.ToggleNumbersChange(
|
||||
GeneratorAction.MainType.Password.ToggleNumbersChange(
|
||||
useNumbers = useNumbers,
|
||||
),
|
||||
)
|
||||
|
||||
val expectedState = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
useNumbers = useNumbers,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
|
@ -1288,23 +1185,16 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
val useSpecialChars = true
|
||||
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleSpecialCharactersChange(
|
||||
GeneratorAction.MainType.Password.ToggleSpecialCharactersChange(
|
||||
useSpecialChars = useSpecialChars,
|
||||
),
|
||||
)
|
||||
|
||||
val expectedState = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
useSpecialChars = useSpecialChars,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
|
@ -1320,19 +1210,14 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
// Toggle numbers to false initially so we can verify length changes appropriately.
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleNumbersChange(
|
||||
GeneratorAction.MainType.Password.ToggleNumbersChange(
|
||||
useNumbers = false,
|
||||
),
|
||||
)
|
||||
|
||||
// Update the length to something small enough that it's updated on numbers toggle.
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.SliderLengthChange(
|
||||
GeneratorAction.MainType.Password.SliderLengthChange(
|
||||
length = 5,
|
||||
isUserInteracting = false,
|
||||
),
|
||||
|
@ -1340,44 +1225,35 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
val minNumbers = 5
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.MinNumbersCounterChange(
|
||||
GeneratorAction.MainType.Password.MinNumbersCounterChange(
|
||||
minNumbers = minNumbers,
|
||||
),
|
||||
)
|
||||
|
||||
val expectedState1 = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = 5, // the current slider value, which the min doesn't exceed
|
||||
minNumbers = minNumbers,
|
||||
useNumbers = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
assertEquals(expectedState1, viewModel.stateFlow.value)
|
||||
|
||||
// Toggle numbers to true so we can verify length changes appropriately.
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleNumbersChange(
|
||||
GeneratorAction.MainType.Password.ToggleNumbersChange(
|
||||
useNumbers = true,
|
||||
),
|
||||
)
|
||||
|
||||
val expectedState2 = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = 7, // 1 uppercase + 1 lowercase + 5 numbers + 0 special
|
||||
minNumbers = minNumbers,
|
||||
useNumbers = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
assertEquals(expectedState2, viewModel.stateFlow.value)
|
||||
}
|
||||
|
@ -1393,7 +1269,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
// Update the length to something small enough that it's updated on toggle.
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.SliderLengthChange(
|
||||
GeneratorAction.MainType.Password.SliderLengthChange(
|
||||
length = 5,
|
||||
isUserInteracting = false,
|
||||
),
|
||||
|
@ -1402,12 +1278,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
val minSpecial = 5
|
||||
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.MinSpecialCharactersChange(
|
||||
GeneratorAction.MainType.Password.MinSpecialCharactersChange(
|
||||
minSpecial = minSpecial,
|
||||
),
|
||||
)
|
||||
|
@ -1415,23 +1286,16 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
// Length should still be 5 because special characters are not enabled.
|
||||
val expectedState1 = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = 5,
|
||||
minSpecial = minSpecial,
|
||||
useSpecialChars = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
assertEquals(expectedState1, viewModel.stateFlow.value)
|
||||
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleSpecialCharactersChange(
|
||||
GeneratorAction.MainType.Password.ToggleSpecialCharactersChange(
|
||||
useSpecialChars = true,
|
||||
),
|
||||
)
|
||||
|
@ -1439,13 +1303,11 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
// Length should update to 7 because special characters are now enabled.
|
||||
val expectedState2 = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = 8, // 1 uppercase + 1 lowercase + 1 number + 5 special chars
|
||||
minSpecial = minSpecial,
|
||||
useSpecialChars = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
assertEquals(expectedState2, viewModel.stateFlow.value)
|
||||
}
|
||||
|
@ -1462,23 +1324,16 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
val avoidAmbiguousChars = true
|
||||
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleAvoidAmbigousCharactersChange(
|
||||
GeneratorAction.MainType.Password.ToggleAvoidAmbigousCharactersChange(
|
||||
avoidAmbiguousChars = avoidAmbiguousChars,
|
||||
),
|
||||
)
|
||||
|
||||
val expectedState = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
avoidAmbiguousChars = avoidAmbiguousChars,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
|
@ -1493,60 +1348,45 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
// Initially turn on all toggles
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.ToggleCapitalLettersChange(
|
||||
GeneratorAction.MainType.Password.ToggleCapitalLettersChange(
|
||||
useCapitals = true,
|
||||
),
|
||||
)
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleLowercaseLettersChange(
|
||||
GeneratorAction.MainType.Password.ToggleLowercaseLettersChange(
|
||||
useLowercase = true,
|
||||
),
|
||||
)
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.ToggleNumbersChange(
|
||||
GeneratorAction.MainType.Password.ToggleNumbersChange(
|
||||
useNumbers = true,
|
||||
),
|
||||
)
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleSpecialCharactersChange(
|
||||
GeneratorAction.MainType.Password.ToggleSpecialCharactersChange(
|
||||
useSpecialChars = true,
|
||||
),
|
||||
)
|
||||
|
||||
// Attempt to turn off all toggles
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.ToggleCapitalLettersChange(
|
||||
GeneratorAction.MainType.Password.ToggleCapitalLettersChange(
|
||||
useCapitals = false,
|
||||
),
|
||||
)
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleLowercaseLettersChange(
|
||||
GeneratorAction.MainType.Password.ToggleLowercaseLettersChange(
|
||||
useLowercase = false,
|
||||
),
|
||||
)
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction.MainType.Passcode.PasscodeType.Password.ToggleNumbersChange(
|
||||
GeneratorAction.MainType.Password.ToggleNumbersChange(
|
||||
useNumbers = false,
|
||||
),
|
||||
)
|
||||
|
||||
// Check the state with only one toggle (useSpecialChars) left on
|
||||
val intermediatePasswordState = GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
val intermediatePasswordState = GeneratorState.MainType.Password(
|
||||
useCapitals = false,
|
||||
useLowercase = false,
|
||||
useNumbers = false,
|
||||
|
@ -1555,32 +1395,23 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
val intermediateState = defaultPasswordState.copy(
|
||||
generatedText = updatedGeneratedPassword,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
selectedType = intermediatePasswordState,
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(intermediateState, viewModel.stateFlow.value)
|
||||
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Password
|
||||
.ToggleSpecialCharactersChange(
|
||||
GeneratorAction.MainType.Password.ToggleSpecialCharactersChange(
|
||||
useSpecialChars = false,
|
||||
),
|
||||
)
|
||||
|
||||
// Check if useLowercase is turned on automatically
|
||||
val expectedState = intermediateState.copy(
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
selectedType = intermediatePasswordState.copy(
|
||||
useLowercase = true,
|
||||
useSpecialChars = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
|
@ -1611,23 +1442,16 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
val newNumWords = 4
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase
|
||||
.NumWordsCounterChange(
|
||||
GeneratorAction.MainType.Passphrase.NumWordsCounterChange(
|
||||
numWords = newNumWords,
|
||||
),
|
||||
)
|
||||
|
||||
val expectedState = defaultPassphraseState.copy(
|
||||
generatedText = updatedGeneratedPassphrase,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Passphrase(
|
||||
selectedType = GeneratorState.MainType.Passphrase(
|
||||
numWords = newNumWords,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
|
@ -1644,22 +1468,16 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
val newWordSeparatorChar = '_'
|
||||
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType.Passphrase
|
||||
.WordSeparatorTextChange(
|
||||
GeneratorAction.MainType.Passphrase.WordSeparatorTextChange(
|
||||
wordSeparator = newWordSeparatorChar,
|
||||
),
|
||||
)
|
||||
|
||||
val expectedState = defaultPassphraseState.copy(
|
||||
generatedText = updatedGeneratedPassphrase,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Passphrase(
|
||||
selectedType = GeneratorState.MainType.Passphrase(
|
||||
wordSeparator = newWordSeparatorChar,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
|
@ -1674,23 +1492,16 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
)
|
||||
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase
|
||||
.ToggleIncludeNumberChange(
|
||||
GeneratorAction.MainType.Passphrase.ToggleIncludeNumberChange(
|
||||
includeNumber = true,
|
||||
),
|
||||
)
|
||||
|
||||
val expectedState = defaultPassphraseState.copy(
|
||||
generatedText = updatedGeneratedPassphrase,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
selectedType = GeneratorState.MainType.Passcode.PasscodeType.Passphrase(
|
||||
selectedType = GeneratorState.MainType.Passphrase(
|
||||
includeNumber = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
|
@ -1705,23 +1516,16 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
)
|
||||
|
||||
viewModel.trySendAction(
|
||||
GeneratorAction
|
||||
.MainType
|
||||
.Passcode
|
||||
.PasscodeType
|
||||
.Passphrase
|
||||
.ToggleCapitalizeChange(
|
||||
GeneratorAction.MainType.Passphrase.ToggleCapitalizeChange(
|
||||
capitalize = true,
|
||||
),
|
||||
)
|
||||
|
||||
val expectedState = defaultPassphraseState.copy(
|
||||
generatedText = updatedGeneratedPassphrase,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Passphrase(
|
||||
selectedType = GeneratorState.MainType.Passphrase(
|
||||
capitalize = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
|
@ -2307,8 +2111,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
@Test
|
||||
fun `Password minimumLength should be at least as long as the sum of the minimums`() {
|
||||
val password =
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
val password = GeneratorState.MainType.Password(
|
||||
length = 14,
|
||||
minLength = 10,
|
||||
useCapitals = true,
|
||||
|
@ -2331,8 +2134,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
@Test
|
||||
fun `Password minimumLength should use minLength if higher than sum of the minimums`() {
|
||||
val password =
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
val password = GeneratorState.MainType.Password(
|
||||
length = 14,
|
||||
minLength = 10,
|
||||
useCapitals = true,
|
||||
|
@ -2367,8 +2169,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
): GeneratorState =
|
||||
GeneratorState(
|
||||
generatedText = generatedText,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||
selectedType = GeneratorState.MainType.Password(
|
||||
length = length,
|
||||
useCapitals = useCapitals,
|
||||
useLowercase = useLowercase,
|
||||
|
@ -2378,7 +2179,6 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
minSpecial = minSpecial,
|
||||
avoidAmbiguousChars = avoidAmbiguousChars,
|
||||
),
|
||||
),
|
||||
currentEmailAddress = "currentEmail",
|
||||
)
|
||||
|
||||
|
@ -2391,14 +2191,12 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
): GeneratorState =
|
||||
GeneratorState(
|
||||
generatedText = generatedText,
|
||||
selectedType = GeneratorState.MainType.Passcode(
|
||||
GeneratorState.MainType.Passcode.PasscodeType.Passphrase(
|
||||
selectedType = GeneratorState.MainType.Passphrase(
|
||||
numWords = numWords,
|
||||
wordSeparator = wordSeparator,
|
||||
capitalize = capitalize,
|
||||
includeNumber = includeNumber,
|
||||
),
|
||||
),
|
||||
currentEmailAddress = "currentEmail",
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in a new issue