From f528376c66375c5699551bf7720bba6144c161c0 Mon Sep 17 00:00:00 2001 From: joshua-livefront <139182194+joshua-livefront@users.noreply.github.com> Date: Thu, 28 Dec 2023 15:51:01 -0500 Subject: [PATCH] BIT-714: Adding ui for duck duck go service type (#445) --- .../feature/generator/GeneratorScreen.kt | 23 ++++++- .../feature/generator/GeneratorViewModel.kt | 57 ++++++++++++++++ .../feature/generator/GeneratorScreenTest.kt | 40 ++++++++++++ .../generator/GeneratorViewModelTest.kt | 65 +++++++++++++++++++ 4 files changed, 184 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreen.kt index 2c0135e1c..aa014a9bf 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreen.kt @@ -838,7 +838,14 @@ private fun ColumnScope.ForwardedEmailAliasTypeContent( } is ServiceType.DuckDuckGo -> { - // TODO: DuckDuckGo Service Implementation (BIT-714) + BitwardenPasswordField( + label = stringResource(id = R.string.api_key_required_parenthesis), + value = usernameTypeState.selectedServiceType.apiKey, + onValueChange = forwardedEmailAliasHandlers.onDuckDuckGoApiKeyTextChange, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) } is ServiceType.FastMail -> { @@ -1171,6 +1178,7 @@ private class PassphraseHandlers( */ private class ForwardedEmailAliasHandlers( val onServiceChange: (ServiceTypeOption) -> Unit, + val onDuckDuckGoApiKeyTextChange: (String) -> Unit, ) { companion object { fun create(viewModel: GeneratorViewModel): ForwardedEmailAliasHandlers { @@ -1187,6 +1195,19 @@ private class ForwardedEmailAliasHandlers( ), ) }, + onDuckDuckGoApiKeyTextChange = { newApiKey -> + viewModel.trySendAction( + GeneratorAction + .MainType + .Username + .UsernameType + .ForwardedEmailAlias + .DuckDuckGo + .ApiKeyTextChange( + apiKey = newApiKey, + ), + ) + }, ) } } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModel.kt index a84945aed..f404390e8 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModel.kt @@ -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.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.DuckDuckGo 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.PlusAddressedEmail import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.RandomWord @@ -113,6 +114,10 @@ class GeneratorViewModel @Inject constructor( handleServiceTypeOptionSelect(action) } + is GeneratorAction.MainType.Username.UsernameType.ForwardedEmailAlias.DuckDuckGo.ApiKeyTextChange -> { + handleDuckDuckGoTextInputChange(action) + } + is GeneratorAction.MainType.Username.UsernameType.PlusAddressedEmail.EmailTextChange -> { handlePlusAddressedEmailTextInputChange(action) } @@ -594,6 +599,21 @@ class GeneratorViewModel @Inject constructor( //endregion Forwarded Email Alias Specific Handlers + private fun handleDuckDuckGoTextInputChange( + action: GeneratorAction + .MainType + .Username + .UsernameType + .ForwardedEmailAlias + .DuckDuckGo + .ApiKeyTextChange, + ) { + updateDuckDuckGoServiceType { duckDuckGoServiceType -> + val newApiKey = action.apiKey + duckDuckGoServiceType.copy(apiKey = newApiKey) + } + } + //region Plus Addressed Email Specific Handlers private fun handlePlusAddressedEmailTextInputChange( @@ -752,6 +772,30 @@ class GeneratorViewModel @Inject constructor( } } + private inline fun updateDuckDuckGoServiceType( + crossinline block: (DuckDuckGo) -> DuckDuckGo, + ) { + updateGeneratorMainTypeUsername { currentUsernameType -> + if (currentUsernameType.selectedType !is ForwardedEmailAlias) { + return@updateGeneratorMainTypeUsername currentUsernameType + } + + val currentServiceType = (currentUsernameType.selectedType).selectedServiceType + if (currentServiceType !is DuckDuckGo) { + return@updateGeneratorMainTypeUsername currentUsernameType + } + + val updatedServiceType = block(currentServiceType) + + currentUsernameType.copy( + selectedType = ForwardedEmailAlias( + selectedServiceType = updatedServiceType, + obfuscatedText = currentUsernameType.selectedType.obfuscatedText, + ), + ) + } + } + private inline fun updatePlusAddressedEmailType( crossinline block: (PlusAddressedEmail) -> PlusAddressedEmail, ) { @@ -1401,6 +1445,19 @@ sealed class GeneratorAction { .ForwardedEmailAlias .ServiceTypeOption, ) : ForwardedEmailAlias() + + /** + * Represents actions specifically related to the DuckDuckGo service. + */ + sealed class DuckDuckGo : ForwardedEmailAlias() { + + /** + * Fired when the api key input text is changed. + * + * @property apiKey The new api key text. + */ + data class ApiKeyTextChange(val apiKey: String) : DuckDuckGo() + } } /** diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreenTest.kt index eaf772aef..653db91dd 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreenTest.kt @@ -927,6 +927,46 @@ class GeneratorScreenTest : BaseComposeTest() { //endregion Forwarded Email Alias Tests + //region DuckDuckGo Service Type Tests + + @Suppress("MaxLineLength") + @Test + fun `in Username_ForwardedEmailAlias_DuckDuckGo state, updating api key text input should send ApiKeyTextChange action`() { + updateState( + GeneratorState( + generatedText = "Placeholder", + selectedType = GeneratorState.MainType.Username( + GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias( + selectedServiceType = GeneratorState + .MainType + .Username + .UsernameType + .ForwardedEmailAlias + .ServiceType + .DuckDuckGo(), + ), + ), + ), + ) + + val newApiKey = "apiKey" + + composeTestRule + .onNodeWithText("API key (required)") + .performScrollTo() + .performTextInput(newApiKey) + + verify { + viewModel.trySendAction( + GeneratorAction.MainType.Username.UsernameType.ForwardedEmailAlias.DuckDuckGo.ApiKeyTextChange( + apiKey = newApiKey, + ), + ) + } + } + + //endregion DuckDuckGo Service Type Tests + //region Username Plus Addressed Email Tests @Suppress("MaxLineLength") diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt index 81e276fc7..a49216bf5 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt @@ -31,6 +31,9 @@ class GeneratorViewModelTest : BaseViewModelTest() { private val forwardedEmailAliasSavedStateHandle = createSavedStateHandleWithState(initialForwardedEmailAliasState) + private val initialDuckDuckGoState = createDuckDuckGoState() + private val duckDuckGoSavedStateHandle = createSavedStateHandleWithState(initialDuckDuckGoState) + private val initialCatchAllEmailState = createCatchAllEmailState() private val catchAllEmailSavedStateHandle = createSavedStateHandleWithState(initialCatchAllEmailState) @@ -951,6 +954,50 @@ class GeneratorViewModelTest : BaseViewModelTest() { } } + @Nested + inner class DuckDuckGoActions { + private val defaultDuckDuckGoState = createDuckDuckGoState() + private lateinit var viewModel: GeneratorViewModel + + @BeforeEach + fun setup() { + viewModel = GeneratorViewModel(duckDuckGoSavedStateHandle, fakeGeneratorRepository) + } + + @Test + fun `ApiKeyTextChange should update api key text correctly`() = runTest { + val newApiKey = "newApiKey" + val action = GeneratorAction + .MainType + .Username + .UsernameType + .ForwardedEmailAlias + .DuckDuckGo.ApiKeyTextChange( + apiKey = newApiKey, + ) + + viewModel.actionChannel.trySend(action) + + val expectedState = defaultDuckDuckGoState.copy( + selectedType = GeneratorState.MainType.Username( + GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias( + selectedServiceType = GeneratorState + .MainType + .Username + .UsernameType + .ForwardedEmailAlias + .ServiceType + .DuckDuckGo( + apiKey = newApiKey, + ), + ), + ), + ) + + assertEquals(expectedState, viewModel.stateFlow.value) + } + } + @Nested inner class PlusAddressedEmailActions { private val defaultPlusAddressedEmailState = createPlusAddressedEmailState() @@ -1158,6 +1205,24 @@ class GeneratorViewModelTest : BaseViewModelTest() { ), ) + private fun createDuckDuckGoState( + generatedText: String = "defaultDuckDuckGo", + ): GeneratorState = + GeneratorState( + generatedText = generatedText, + selectedType = GeneratorState.MainType.Username( + GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias( + selectedServiceType = GeneratorState + .MainType + .Username + .UsernameType + .ForwardedEmailAlias + .ServiceType + .DuckDuckGo(), + ), + ), + ) + private fun createPlusAddressedEmailState( generatedText: String = "defaultPlusAddressedEmail", email: String = "defaultEmail",