mirror of
https://github.com/bitwarden/android.git
synced 2024-11-23 09:56:11 +03:00
BIT-660: Adding ui for catch all email (#303)
This commit is contained in:
parent
641b5c35bf
commit
85e750cc08
5 changed files with 245 additions and 16 deletions
|
@ -38,6 +38,7 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||||
* @param onOptionSelected A lambda that is invoked when an option
|
* @param onOptionSelected A lambda that is invoked when an option
|
||||||
* is selected from the dropdown menu.
|
* is selected from the dropdown menu.
|
||||||
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
|
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
|
||||||
|
* @param supportingText A optional supporting text that will appear below the text field.
|
||||||
*/
|
*/
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -47,6 +48,7 @@ fun BitwardenMultiSelectButton(
|
||||||
selectedOption: String,
|
selectedOption: String,
|
||||||
onOptionSelected: (String) -> Unit,
|
onOptionSelected: (String) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
supportingText: String? = null,
|
||||||
) {
|
) {
|
||||||
var shouldShowDialog by remember { mutableStateOf(false) }
|
var shouldShowDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
@ -93,7 +95,16 @@ fun BitwardenMultiSelectButton(
|
||||||
disabledTrailingIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
disabledTrailingIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
disabledPlaceholderColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
disabledPlaceholderColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
disabledSupportingTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
|
supportingText = supportingText?.let {
|
||||||
|
{
|
||||||
|
Text(
|
||||||
|
text = supportingText,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
if (shouldShowDialog) {
|
if (shouldShowDialog) {
|
||||||
BitwardenSelectionDialog(
|
BitwardenSelectionDialog(
|
||||||
|
|
|
@ -16,7 +16,6 @@ import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
import androidx.compose.material3.SnackbarDuration
|
import androidx.compose.material3.SnackbarDuration
|
||||||
|
@ -146,6 +145,10 @@ fun GeneratorScreen(
|
||||||
PlusAddressedEmailHandlers.create(viewModel = viewModel)
|
PlusAddressedEmailHandlers.create(viewModel = viewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val catchAllEmailHandlers = remember(viewModel) {
|
||||||
|
CatchAllEmailHandlers.create(viewModel = viewModel)
|
||||||
|
}
|
||||||
|
|
||||||
val scrollBehavior =
|
val scrollBehavior =
|
||||||
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState())
|
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
|
@ -174,6 +177,7 @@ fun GeneratorScreen(
|
||||||
passwordHandlers = passwordHandlers,
|
passwordHandlers = passwordHandlers,
|
||||||
passphraseHandlers = passphraseHandlers,
|
passphraseHandlers = passphraseHandlers,
|
||||||
plusAddressedEmailHandlers = plusAddressedEmailHandlers,
|
plusAddressedEmailHandlers = plusAddressedEmailHandlers,
|
||||||
|
catchAllEmailHandlers = catchAllEmailHandlers,
|
||||||
modifier = Modifier.padding(innerPadding),
|
modifier = Modifier.padding(innerPadding),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -193,6 +197,7 @@ private fun ScrollContent(
|
||||||
passwordHandlers: PasswordHandlers,
|
passwordHandlers: PasswordHandlers,
|
||||||
passphraseHandlers: PassphraseHandlers,
|
passphraseHandlers: PassphraseHandlers,
|
||||||
plusAddressedEmailHandlers: PlusAddressedEmailHandlers,
|
plusAddressedEmailHandlers: PlusAddressedEmailHandlers,
|
||||||
|
catchAllEmailHandlers: CatchAllEmailHandlers,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
|
@ -241,6 +246,7 @@ private fun ScrollContent(
|
||||||
usernameState = selectedType,
|
usernameState = selectedType,
|
||||||
onSubStateOptionClicked = onUsernameSubStateOptionClicked,
|
onSubStateOptionClicked = onUsernameSubStateOptionClicked,
|
||||||
plusAddressedEmailHandlers = plusAddressedEmailHandlers,
|
plusAddressedEmailHandlers = plusAddressedEmailHandlers,
|
||||||
|
catchAllEmailHandlers = catchAllEmailHandlers,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -695,6 +701,7 @@ private fun ColumnScope.UsernameTypeItems(
|
||||||
usernameState: GeneratorState.MainType.Username,
|
usernameState: GeneratorState.MainType.Username,
|
||||||
onSubStateOptionClicked: (GeneratorState.MainType.Username.UsernameTypeOption) -> Unit,
|
onSubStateOptionClicked: (GeneratorState.MainType.Username.UsernameTypeOption) -> Unit,
|
||||||
plusAddressedEmailHandlers: PlusAddressedEmailHandlers,
|
plusAddressedEmailHandlers: PlusAddressedEmailHandlers,
|
||||||
|
catchAllEmailHandlers: CatchAllEmailHandlers,
|
||||||
) {
|
) {
|
||||||
UsernameOptionsItem(usernameState, onSubStateOptionClicked)
|
UsernameOptionsItem(usernameState, onSubStateOptionClicked)
|
||||||
|
|
||||||
|
@ -711,7 +718,10 @@ private fun ColumnScope.UsernameTypeItems(
|
||||||
}
|
}
|
||||||
|
|
||||||
is GeneratorState.MainType.Username.UsernameType.CatchAllEmail -> {
|
is GeneratorState.MainType.Username.UsernameType.CatchAllEmail -> {
|
||||||
// TODO: Implement CatchAllEmail BIT-656
|
CatchAllEmailTypeContent(
|
||||||
|
usernameTypeState = selectedType,
|
||||||
|
catchAllEmailHandlers = catchAllEmailHandlers,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is GeneratorState.MainType.Username.UsernameType.RandomWord -> {
|
is GeneratorState.MainType.Username.UsernameType.RandomWord -> {
|
||||||
|
@ -740,6 +750,9 @@ private fun UsernameOptionsItem(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
|
supportingText = currentSubState.selectedType.supportingStringResId?.let {
|
||||||
|
stringResource(id = it)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -752,17 +765,6 @@ private fun ColumnScope.PlusAddressedEmailTypeContent(
|
||||||
usernameTypeState: GeneratorState.MainType.Username.UsernameType.PlusAddressedEmail,
|
usernameTypeState: GeneratorState.MainType.Username.UsernameType.PlusAddressedEmail,
|
||||||
plusAddressedEmailHandlers: PlusAddressedEmailHandlers,
|
plusAddressedEmailHandlers: PlusAddressedEmailHandlers,
|
||||||
) {
|
) {
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = stringResource(id = R.string.plus_addressed_email_description),
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 32.dp),
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
PlusAddressedEmailTextInputItem(
|
PlusAddressedEmailTextInputItem(
|
||||||
|
@ -790,6 +792,40 @@ private fun PlusAddressedEmailTextInputItem(
|
||||||
|
|
||||||
//endregion PlusAddressedEmailType Composables
|
//endregion PlusAddressedEmailType Composables
|
||||||
|
|
||||||
|
//region CatchAllEmailType Composables
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ColumnScope.CatchAllEmailTypeContent(
|
||||||
|
usernameTypeState: GeneratorState.MainType.Username.UsernameType.CatchAllEmail,
|
||||||
|
catchAllEmailHandlers: CatchAllEmailHandlers,
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
CatchAllEmailTextInputItem(
|
||||||
|
domain = usernameTypeState.domainName,
|
||||||
|
onDomainTextChange = catchAllEmailHandlers.onDomainChange,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CatchAllEmailTextInputItem(
|
||||||
|
domain: String,
|
||||||
|
onDomainTextChange: (domain: String) -> Unit,
|
||||||
|
) {
|
||||||
|
BitwardenTextField(
|
||||||
|
label = stringResource(id = R.string.domain_name_required_parenthesis),
|
||||||
|
value = domain,
|
||||||
|
onValueChange = {
|
||||||
|
onDomainTextChange(it)
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion CatchAllEmailType Composables
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun GeneratorPreview() {
|
private fun GeneratorPreview() {
|
||||||
|
@ -966,3 +1002,32 @@ private class PlusAddressedEmailHandlers(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class dedicated to handling user interactions related to plus addressed email
|
||||||
|
* configuration.
|
||||||
|
* Each lambda corresponds to a specific user action, allowing for easy delegation of
|
||||||
|
* logic when user input is detected.
|
||||||
|
*/
|
||||||
|
private class CatchAllEmailHandlers(
|
||||||
|
val onDomainChange: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun create(viewModel: GeneratorViewModel): CatchAllEmailHandlers {
|
||||||
|
return CatchAllEmailHandlers(
|
||||||
|
onDomainChange = { newDomain ->
|
||||||
|
viewModel.trySendAction(
|
||||||
|
GeneratorAction
|
||||||
|
.MainType
|
||||||
|
.Username
|
||||||
|
.UsernameType
|
||||||
|
.CatchAllEmail
|
||||||
|
.DomainTextChange(
|
||||||
|
domain = newDomain,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Pa
|
||||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username
|
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username
|
||||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias.ServiceType.AnonAddy
|
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias.ServiceType.AnonAddy
|
||||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.PlusAddressedEmail
|
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.PlusAddressedEmail
|
||||||
|
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.CatchAllEmail
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
@ -104,6 +105,10 @@ class GeneratorViewModel @Inject constructor(
|
||||||
{
|
{
|
||||||
handlePlusAddressedEmailTextInputChange(action)
|
handlePlusAddressedEmailTextInputChange(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is GeneratorAction.MainType.Username.UsernameType.CatchAllEmail.DomainTextChange -> {
|
||||||
|
handleCatchAllEmailTextInputChange(action)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,6 +545,19 @@ class GeneratorViewModel @Inject constructor(
|
||||||
|
|
||||||
//endregion Plus Addressed Email Specific Handlers
|
//endregion Plus Addressed Email Specific Handlers
|
||||||
|
|
||||||
|
//region Catch-All Email Specific Handlers
|
||||||
|
|
||||||
|
private fun handleCatchAllEmailTextInputChange(
|
||||||
|
action: GeneratorAction.MainType.Username.UsernameType.CatchAllEmail.DomainTextChange,
|
||||||
|
) {
|
||||||
|
updateCatchAllEmailType { catchAllEmailType ->
|
||||||
|
val newDomain = action.domain
|
||||||
|
catchAllEmailType.copy(domainName = newDomain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion Catch-All Email Specific Handlers
|
||||||
|
|
||||||
//region Utility Functions
|
//region Utility Functions
|
||||||
|
|
||||||
private inline fun updateGeneratorMainType(
|
private inline fun updateGeneratorMainType(
|
||||||
|
@ -623,6 +641,18 @@ class GeneratorViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline fun updateCatchAllEmailType(
|
||||||
|
crossinline block: (CatchAllEmail) -> CatchAllEmail,
|
||||||
|
) {
|
||||||
|
updateGeneratorMainTypeUsername { currentSelectedType ->
|
||||||
|
val currentUsernameType = currentSelectedType.selectedType
|
||||||
|
if (currentUsernameType !is CatchAllEmail) {
|
||||||
|
return@updateGeneratorMainTypeUsername currentSelectedType
|
||||||
|
}
|
||||||
|
currentSelectedType.copy(selectedType = block(currentUsernameType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//endregion Utility Functions
|
//endregion Utility Functions
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -821,12 +851,16 @@ data class GeneratorState(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the resource ID for the display string specific to each
|
* Represents the resource ID for the display string specific to each
|
||||||
* PasscodeType subclass. Every subclass of UsernameType must override
|
* UsernameType subclass.
|
||||||
* this property to provide the appropriate string resource ID for
|
|
||||||
* its display string.
|
|
||||||
*/
|
*/
|
||||||
abstract val displayStringResId: Int
|
abstract val displayStringResId: Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the resource ID for the supporting display string specific to each
|
||||||
|
* UsernameType subclass.
|
||||||
|
*/
|
||||||
|
abstract val supportingStringResId: Int?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a PlusAddressedEmail type.
|
* Represents a PlusAddressedEmail type.
|
||||||
*
|
*
|
||||||
|
@ -838,6 +872,9 @@ data class GeneratorState(
|
||||||
) : UsernameType(), Parcelable {
|
) : UsernameType(), Parcelable {
|
||||||
override val displayStringResId: Int
|
override val displayStringResId: Int
|
||||||
get() = UsernameTypeOption.PLUS_ADDRESSED_EMAIL.labelRes
|
get() = UsernameTypeOption.PLUS_ADDRESSED_EMAIL.labelRes
|
||||||
|
|
||||||
|
override val supportingStringResId: Int
|
||||||
|
get() = R.string.plus_addressed_email_description
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -852,6 +889,9 @@ data class GeneratorState(
|
||||||
) : UsernameType(), Parcelable {
|
) : UsernameType(), Parcelable {
|
||||||
override val displayStringResId: Int
|
override val displayStringResId: Int
|
||||||
get() = UsernameTypeOption.CATCH_ALL_EMAIL.labelRes
|
get() = UsernameTypeOption.CATCH_ALL_EMAIL.labelRes
|
||||||
|
|
||||||
|
override val supportingStringResId: Int
|
||||||
|
get() = R.string.catch_all_email_description
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -868,6 +908,9 @@ data class GeneratorState(
|
||||||
) : UsernameType(), Parcelable {
|
) : UsernameType(), Parcelable {
|
||||||
override val displayStringResId: Int
|
override val displayStringResId: Int
|
||||||
get() = UsernameTypeOption.RANDOM_WORD.labelRes
|
get() = UsernameTypeOption.RANDOM_WORD.labelRes
|
||||||
|
|
||||||
|
override val supportingStringResId: Int?
|
||||||
|
get() = null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -883,6 +926,9 @@ data class GeneratorState(
|
||||||
override val displayStringResId: Int
|
override val displayStringResId: Int
|
||||||
get() = UsernameTypeOption.FORWARDED_EMAIL_ALIAS.labelRes
|
get() = UsernameTypeOption.FORWARDED_EMAIL_ALIAS.labelRes
|
||||||
|
|
||||||
|
override val supportingStringResId: Int
|
||||||
|
get() = R.string.forwarded_email_description
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum representing the types of services,
|
* Enum representing the types of services,
|
||||||
* allowing for different service configurations.
|
* allowing for different service configurations.
|
||||||
|
@ -1203,6 +1249,19 @@ sealed class GeneratorAction {
|
||||||
*/
|
*/
|
||||||
data class EmailTextChange(val email: String) : PlusAddressedEmail()
|
data class EmailTextChange(val email: String) : PlusAddressedEmail()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents actions specifically related to Catch-All Email.
|
||||||
|
*/
|
||||||
|
sealed class CatchAllEmail : UsernameType() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when the domain text input is changed.
|
||||||
|
*
|
||||||
|
* @property domain The new domain text.
|
||||||
|
*/
|
||||||
|
data class DomainTextChange(val domain: String) : CatchAllEmail()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -993,6 +993,41 @@ class GeneratorScreenTest : BaseComposeTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `in Username_CatchAllEmail state, updating text in email field should send EmailTextChange action`() {
|
||||||
|
updateState(
|
||||||
|
GeneratorState(
|
||||||
|
generatedText = "Placeholder",
|
||||||
|
selectedType = GeneratorState.MainType.Username(
|
||||||
|
GeneratorState.MainType.Username.UsernameType.CatchAllEmail(
|
||||||
|
domainName = "",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
composeTestRule.setContent {
|
||||||
|
GeneratorScreen(viewModel = viewModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newDomain = "test.com"
|
||||||
|
|
||||||
|
// Find the text field for Catch-All Email and input text
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithText("Domain name (required)")
|
||||||
|
.performScrollTo()
|
||||||
|
.performTextInput(newDomain)
|
||||||
|
|
||||||
|
verify {
|
||||||
|
viewModel.trySendAction(
|
||||||
|
GeneratorAction.MainType.Username.UsernameType.CatchAllEmail.DomainTextChange(
|
||||||
|
domain = newDomain,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//endregion Username Plus Addressed Email Tests
|
//endregion Username Plus Addressed Email Tests
|
||||||
|
|
||||||
private fun updateState(state: GeneratorState) {
|
private fun updateState(state: GeneratorState) {
|
||||||
|
|
|
@ -27,6 +27,10 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
private val initialUsernameState = createPlusAddressedEmailState()
|
private val initialUsernameState = createPlusAddressedEmailState()
|
||||||
private val usernameSavedStateHandle = createSavedStateHandleWithState(initialUsernameState)
|
private val usernameSavedStateHandle = createSavedStateHandleWithState(initialUsernameState)
|
||||||
|
|
||||||
|
private val initialCatchAllEmailState = createCatchAllEmailState()
|
||||||
|
private val catchAllEmailSavedStateHandle =
|
||||||
|
createSavedStateHandleWithState(initialCatchAllEmailState)
|
||||||
|
|
||||||
private val fakeGeneratorRepository = FakeGeneratorRepository().apply {
|
private val fakeGeneratorRepository = FakeGeneratorRepository().apply {
|
||||||
setMockGeneratePasswordResult(
|
setMockGeneratePasswordResult(
|
||||||
GeneratedPasswordResult.Success("defaultPassword"),
|
GeneratedPasswordResult.Success("defaultPassword"),
|
||||||
|
@ -826,6 +830,48 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class CatchAllEmailActions {
|
||||||
|
private val defaultCatchAllEmailState = createCatchAllEmailState()
|
||||||
|
private lateinit var viewModel: GeneratorViewModel
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setup() {
|
||||||
|
viewModel = GeneratorViewModel(catchAllEmailSavedStateHandle, fakeGeneratorRepository)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `DomainTextChange should update domain correctly`() =
|
||||||
|
runTest {
|
||||||
|
val newDomain = "test.com"
|
||||||
|
viewModel.actionChannel.trySend(
|
||||||
|
GeneratorAction
|
||||||
|
.MainType
|
||||||
|
.Username
|
||||||
|
.UsernameType
|
||||||
|
.CatchAllEmail
|
||||||
|
.DomainTextChange(
|
||||||
|
domain = newDomain,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val expectedState = defaultCatchAllEmailState.copy(
|
||||||
|
selectedType = GeneratorState.MainType.Username(
|
||||||
|
selectedType = GeneratorState
|
||||||
|
.MainType
|
||||||
|
.Username
|
||||||
|
.UsernameType
|
||||||
|
.CatchAllEmail(
|
||||||
|
domainName = newDomain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
//region Helper Functions
|
//region Helper Functions
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
|
@ -888,6 +934,19 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun createCatchAllEmailState(
|
||||||
|
generatedText: String = "defaultCatchAllEmail",
|
||||||
|
domain: String = "defaultDomain",
|
||||||
|
): GeneratorState =
|
||||||
|
GeneratorState(
|
||||||
|
generatedText = generatedText,
|
||||||
|
selectedType = GeneratorState.MainType.Username(
|
||||||
|
GeneratorState.MainType.Username.UsernameType.CatchAllEmail(
|
||||||
|
domainName = domain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
private fun createSavedStateHandleWithState(state: GeneratorState) =
|
private fun createSavedStateHandleWithState(state: GeneratorState) =
|
||||||
SavedStateHandle().apply {
|
SavedStateHandle().apply {
|
||||||
set("state", state)
|
set("state", state)
|
||||||
|
|
Loading…
Reference in a new issue