mirror of
https://github.com/bitwarden/android.git
synced 2026-05-21 11:56:35 -05:00
Merge branch 'main' into QA-1126b/adding-native-sanity-test
This commit is contained in:
8
.github/workflows/scan-ci.yml
vendored
8
.github/workflows/scan-ci.yml
vendored
@@ -9,16 +9,9 @@ on:
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
check-run:
|
||||
name: Check PR run
|
||||
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
sast:
|
||||
name: Checkmarx
|
||||
uses: bitwarden/gh-actions/.github/workflows/_checkmarx.yml@main
|
||||
needs: check-run
|
||||
secrets:
|
||||
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
|
||||
@@ -32,7 +25,6 @@ jobs:
|
||||
quality:
|
||||
name: Sonar
|
||||
uses: bitwarden/gh-actions/.github/workflows/_sonar.yml@main
|
||||
needs: check-run
|
||||
secrets:
|
||||
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.dropdown
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -12,6 +19,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.ui.platform.base.util.nullableTestTag
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenTextSelectionButton
|
||||
import com.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.bitwarden.ui.platform.components.model.TooltipData
|
||||
@@ -19,8 +27,10 @@ import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenSelectionDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenSelectionRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.model.MultiSelectOption
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
/**
|
||||
* A custom composable representing a multi-select button.
|
||||
@@ -32,8 +42,8 @@ import kotlinx.collections.immutable.persistentListOf
|
||||
* @param options A list of strings representing the available options in the dialog.
|
||||
* @param selectedOption The currently selected option that is displayed in the [OutlinedTextField]
|
||||
* (or `null` if no option is selected).
|
||||
* @param onOptionSelected A lambda that is invoked when an option
|
||||
* is selected from the dropdown menu.
|
||||
* @param onOptionSelected A lambda that is invoked when an option is selected from the dropdown
|
||||
* menu.
|
||||
* @param isEnabled Whether or not the button is enabled.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
|
||||
@@ -61,18 +71,84 @@ fun BitwardenMultiSelectButton(
|
||||
textFieldTestTag: String? = null,
|
||||
actionsPadding: PaddingValues = PaddingValues(end = 4.dp),
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
BitwardenMultiSelectButton(
|
||||
label = label,
|
||||
options = options.map { MultiSelectOption.Row(it) }.toImmutableList(),
|
||||
selectedOption = selectedOption?.let { MultiSelectOption.Row(it) },
|
||||
onOptionSelected = { onOptionSelected(it.title) },
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
isEnabled = isEnabled,
|
||||
supportingContent = supportingText?.let {
|
||||
{
|
||||
Text(
|
||||
text = it,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
},
|
||||
tooltip = tooltip,
|
||||
insets = insets,
|
||||
textFieldTestTag = textFieldTestTag,
|
||||
actionsPadding = actionsPadding,
|
||||
actions = actions,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom composable representing a multi-select button.
|
||||
*
|
||||
* This composable displays an [OutlinedTextField] with a dropdown icon as a trailing icon.
|
||||
* When the field is clicked, a dropdown menu appears with a list of options to select from.
|
||||
*
|
||||
* @param label The descriptive text label for the [OutlinedTextField].
|
||||
* @param options A list of [MultiSelectOption] representing the available options in the dialog.
|
||||
* @param selectedOption The currently selected option that is displayed in the [OutlinedTextField]
|
||||
* (or `null` if no option is selected).
|
||||
* @param onOptionSelected A lambda that is invoked when an option is selected from the dropdown
|
||||
* menu.
|
||||
* @param isEnabled Whether or not the button is enabled.
|
||||
* @param supportingContent An optional supporting content that will appear below the button.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
|
||||
* @param tooltip A nullable [TooltipData], representing the tooltip icon.
|
||||
* @param insets Inner padding to be applied withing the card.
|
||||
* @param textFieldTestTag The optional test tag associated with the inner text field.
|
||||
* @param actionsPadding Padding to be applied to the [actions] block.
|
||||
* @param actions A lambda containing the set of actions (usually icons or similar) to display
|
||||
* in the app bar's trailing side. This lambda extends [RowScope], allowing flexibility in
|
||||
* defining the layout of the actions.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenMultiSelectButton(
|
||||
label: String,
|
||||
options: ImmutableList<MultiSelectOption>,
|
||||
selectedOption: MultiSelectOption.Row?,
|
||||
onOptionSelected: (MultiSelectOption.Row) -> Unit,
|
||||
cardStyle: CardStyle?,
|
||||
modifier: Modifier = Modifier,
|
||||
isEnabled: Boolean = true,
|
||||
supportingContent: @Composable (ColumnScope.() -> Unit)?,
|
||||
tooltip: TooltipData? = null,
|
||||
insets: PaddingValues = PaddingValues(),
|
||||
textFieldTestTag: String? = null,
|
||||
actionsPadding: PaddingValues = PaddingValues(end = 4.dp),
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
var shouldShowDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
BitwardenTextSelectionButton(
|
||||
label = label,
|
||||
selectedOption = selectedOption,
|
||||
selectedOption = selectedOption?.title,
|
||||
onClick = {
|
||||
shouldShowDialog = true
|
||||
},
|
||||
cardStyle = cardStyle,
|
||||
enabled = isEnabled,
|
||||
supportingText = supportingText,
|
||||
supportingContent = supportingContent,
|
||||
tooltip = tooltip,
|
||||
insets = insets,
|
||||
textFieldTestTag = textFieldTestTag,
|
||||
@@ -87,14 +163,60 @@ fun BitwardenMultiSelectButton(
|
||||
title = label,
|
||||
onDismissRequest = { shouldShowDialog = false },
|
||||
) {
|
||||
options.forEach { optionString ->
|
||||
BitwardenMultiSelectDialogContent(
|
||||
options = options,
|
||||
selectedOption = selectedOption,
|
||||
onOptionSelected = { selectedItem ->
|
||||
shouldShowDialog = false
|
||||
onOptionSelected(selectedItem)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the list of items within a multi-select dialog.
|
||||
*
|
||||
* This composable is typically used as the content for [BitwardenSelectionDialog].
|
||||
*
|
||||
* @param options A list of strings representing the available options in the dialog.
|
||||
* @param selectedOption The currently selected option that is displayed in the [OutlinedTextField]
|
||||
* (or `null` if no option is selected).
|
||||
* @param onOptionSelected A lambda that is invoked when an option
|
||||
* is selected from the dropdown menu.
|
||||
*/
|
||||
@Composable
|
||||
fun ColumnScope.BitwardenMultiSelectDialogContent(
|
||||
options: ImmutableList<MultiSelectOption>,
|
||||
selectedOption: MultiSelectOption.Row?,
|
||||
onOptionSelected: (MultiSelectOption.Row) -> Unit,
|
||||
) {
|
||||
options.forEach {
|
||||
when (it) {
|
||||
is MultiSelectOption.Header -> {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.nullableTestTag(tag = it.testTag)
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 4.dp))
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = it.title,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
style = BitwardenTheme.typography.titleSmall,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is MultiSelectOption.Row -> {
|
||||
BitwardenSelectionRow(
|
||||
text = optionString.asText(),
|
||||
isSelected = optionString == selectedOption,
|
||||
onClick = {
|
||||
shouldShowDialog = false
|
||||
onOptionSelected(optionString)
|
||||
},
|
||||
text = it.title.asText(),
|
||||
isSelected = it == selectedOption,
|
||||
onClick = { onOptionSelected(it) },
|
||||
modifier = Modifier.nullableTestTag(tag = it.testTag),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.dropdown.model
|
||||
|
||||
/**
|
||||
* Represents an option in a multi-select list, which can either be a header or a selectable row.
|
||||
*/
|
||||
sealed class MultiSelectOption {
|
||||
/**
|
||||
* The text to display for the option.
|
||||
*/
|
||||
abstract val title: String
|
||||
|
||||
/**
|
||||
* Represents a header item in a multi-select list. Headers are used to visually group related
|
||||
* options within the list.
|
||||
*/
|
||||
data class Header(
|
||||
override val title: String,
|
||||
val testTag: String? = null,
|
||||
) : MultiSelectOption()
|
||||
|
||||
/**
|
||||
* Represents a selectable row item in a multi-select list.
|
||||
*/
|
||||
data class Row(
|
||||
override val title: String,
|
||||
val testTag: String? = null,
|
||||
) : MultiSelectOption()
|
||||
}
|
||||
@@ -31,6 +31,7 @@ fun <T : Any> FlagKey<T>.ListItemContent(
|
||||
FlagKey.CipherKeyEncryption,
|
||||
FlagKey.UserManagedPrivilegedApps,
|
||||
FlagKey.RemoveCardPolicy,
|
||||
FlagKey.EnrollAeadOnKeyRotation,
|
||||
-> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
BooleanFlagItem(
|
||||
@@ -83,4 +84,8 @@ private fun <T : Any> FlagKey<T>.getDisplayLabel(): String = when (this) {
|
||||
FlagKey.BitwardenAuthenticationEnabled -> {
|
||||
stringResource(BitwardenString.bitwarden_authentication_enabled)
|
||||
}
|
||||
|
||||
FlagKey.EnrollAeadOnKeyRotation -> {
|
||||
stringResource(BitwardenString.enroll_aead_on_key_rotation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,9 +124,7 @@ class AppearanceScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `on theme row click should display theme selection dialog`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Default (System). Theme. Change the application's color theme",
|
||||
)
|
||||
.onNodeWithContentDescription(label = "Default (System). Theme")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
composeTestRule
|
||||
@@ -138,9 +136,7 @@ class AppearanceScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `on theme selection dialog item click should send ThemeChange`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Default (System). Theme. Change the application's color theme",
|
||||
)
|
||||
.onNodeWithContentDescription(label = "Default (System). Theme")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
composeTestRule
|
||||
@@ -161,9 +157,7 @@ class AppearanceScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `on theme selection dialog cancel click should dismiss dialog`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Default (System). Theme. Change the application's color theme",
|
||||
)
|
||||
.onNodeWithContentDescription(label = "Default (System). Theme")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
composeTestRule
|
||||
|
||||
@@ -240,9 +240,7 @@ class AutoFillScreenTest : BitwardenComposeTest() {
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Popup (shows over input field). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
label = "Popup (shows over input field). Display autofill suggestions",
|
||||
)
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
@@ -267,18 +265,14 @@ class AutoFillScreenTest : BitwardenComposeTest() {
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions",
|
||||
)
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
mutableStateFlow.update { it.copy(autofillStyle = AutofillStyle.POPUP) }
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Popup (shows over input field). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
label = "Popup (shows over input field). Display autofill suggestions",
|
||||
)
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
@@ -292,9 +286,7 @@ class AutoFillScreenTest : BitwardenComposeTest() {
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions",
|
||||
)
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
@@ -305,9 +297,7 @@ class AutoFillScreenTest : BitwardenComposeTest() {
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions",
|
||||
)
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
@@ -350,9 +340,7 @@ class AutoFillScreenTest : BitwardenComposeTest() {
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions",
|
||||
)
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
@@ -363,9 +351,7 @@ class AutoFillScreenTest : BitwardenComposeTest() {
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions",
|
||||
)
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
@@ -418,7 +404,7 @@ class AutoFillScreenTest : BitwardenComposeTest() {
|
||||
fun `on default URI match type click should display dialog`() {
|
||||
composeTestRule.assertNoDialogExists()
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Default URI match detection.", substring = true)
|
||||
.onNodeWithText(text = "Default URI match detection")
|
||||
.performScrollTo()
|
||||
.assert(!hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
@@ -432,7 +418,7 @@ class AutoFillScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `on default URI match type dialog item click should send DefaultUriMatchTypeSelect and close the dialog`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Default URI match detection.", substring = true)
|
||||
.onNodeWithText(text = "Default URI match detection")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
@@ -454,7 +440,7 @@ class AutoFillScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `on default URI match type dialog cancel click should close the dialog`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Default URI match detection.", substring = true)
|
||||
.onNodeWithText(text = "Default URI match detection")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.flightrecorder
|
||||
|
||||
import androidx.compose.ui.test.assert
|
||||
import androidx.compose.ui.test.filterToOne
|
||||
import androidx.compose.ui.test.hasAnyAncestor
|
||||
import androidx.compose.ui.test.isDialog
|
||||
import androidx.compose.ui.test.isDisplayed
|
||||
import androidx.compose.ui.test.onAllNodesWithText
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.onRoot
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performScrollTo
|
||||
import androidx.compose.ui.test.printToLog
|
||||
import androidx.core.net.toUri
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.ui.util.assertNoDialogExists
|
||||
@@ -83,13 +86,14 @@ class FlightRecorderScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `on logging duration click should display select dialog`() {
|
||||
composeTestRule.assertNoDialogExists()
|
||||
composeTestRule.onRoot().printToLog("BRAIN")
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "1 hour. Logging duration")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Logging duration")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.onAllNodesWithText(text = "Logging duration")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.isDisplayed()
|
||||
}
|
||||
|
||||
|
||||
@@ -97,10 +97,7 @@ class OtherScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `on clear clipboard row click should show show clipboard selection dialog`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Never. Clear clipboard. " +
|
||||
"Automatically clear copied values from your clipboard.",
|
||||
)
|
||||
.onNodeWithContentDescription(label = "Never. Clear clipboard")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
composeTestRule
|
||||
@@ -112,10 +109,7 @@ class OtherScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `on clear clipboard dialog item click should send ClearClipboardFrequencyChange`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Never. Clear clipboard. " +
|
||||
"Automatically clear copied values from your clipboard.",
|
||||
)
|
||||
.onNodeWithContentDescription(label = "Never. Clear clipboard")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
composeTestRule
|
||||
@@ -136,10 +130,7 @@ class OtherScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `on clear clipboard dialog cancel should dismiss dialog`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Never. Clear clipboard. " +
|
||||
"Automatically clear copied values from your clipboard.",
|
||||
)
|
||||
.onNodeWithContentDescription(label = "Never. Clear clipboard")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
composeTestRule.onNodeWithText("Cancel").performClick()
|
||||
|
||||
@@ -229,7 +229,6 @@ class GeneratorScreenTest : BitwardenComposeTest() {
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `clicking a UsernameOption should send UsernameTypeOption action`() {
|
||||
updateState(
|
||||
@@ -242,9 +241,7 @@ class GeneratorScreenTest : BitwardenComposeTest() {
|
||||
|
||||
// Opens the menu
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Plus addressed email. Username type. Use your email provider's subaddress capabilities",
|
||||
)
|
||||
.onNodeWithContentDescription(label = "Plus addressed email. Username type")
|
||||
.performClick()
|
||||
|
||||
// Choose the option from the menu
|
||||
@@ -1459,12 +1456,8 @@ class GeneratorScreenTest : BitwardenComposeTest() {
|
||||
fun `in Username state, clicking the tooltip icon should send the TooltipClick action`() {
|
||||
updateState(DEFAULT_STATE.copy(selectedType = GeneratorState.MainType.Username()))
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Plus addressed email. Username type. Use your email provider's subaddress capabilities",
|
||||
useUnmergedTree = true,
|
||||
)
|
||||
.onNodeWithContentDescription(label = "Plus addressed email. Username type")
|
||||
// Find the button
|
||||
.onChildren()
|
||||
.filterToOne(hasClickAction())
|
||||
|
||||
@@ -121,7 +121,7 @@ class VaultMoveToOrganizationScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `the organization option field description should update according to state`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Choose an organization that", substring = true)
|
||||
.onNodeWithText(text = "Choose an organization that", substring = true)
|
||||
.assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update { currentState ->
|
||||
@@ -130,7 +130,7 @@ class VaultMoveToOrganizationScreenTest : BitwardenComposeTest() {
|
||||
|
||||
composeTestRule
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Choose an organization that", substring = true)
|
||||
.onNodeWithText(text = "Choose an organization that", substring = true)
|
||||
.assertIsNotDisplayed()
|
||||
}
|
||||
|
||||
@@ -169,12 +169,7 @@ class VaultMoveToOrganizationScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `selecting an organization should send OrganizationSelect action`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(
|
||||
label = "mockOrganizationName-1. Organization. " +
|
||||
"Choose an organization that you wish to move this item to. Moving to an " +
|
||||
"organization transfers ownership of the item to that organization. You " +
|
||||
"will no longer be the direct owner of this item once it has been moved.",
|
||||
)
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "mockOrganizationName-1. Organization")
|
||||
.performClick()
|
||||
// Choose the option from the menu
|
||||
composeTestRule
|
||||
@@ -205,12 +200,7 @@ class VaultMoveToOrganizationScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `the organization option field should display according to state`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(
|
||||
label = "mockOrganizationName-1. Organization. " +
|
||||
"Choose an organization that you wish to move this item to. Moving to an " +
|
||||
"organization transfers ownership of the item to that organization. You " +
|
||||
"will no longer be the direct owner of this item once it has been moved.",
|
||||
)
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "mockOrganizationName-1. Organization")
|
||||
.assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update { currentState ->
|
||||
@@ -223,12 +213,7 @@ class VaultMoveToOrganizationScreenTest : BitwardenComposeTest() {
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(
|
||||
label = "mockOrganizationName-2. Organization. " +
|
||||
"Choose an organization that you wish to move this item to. Moving to an " +
|
||||
"organization transfers ownership of the item to that organization. You " +
|
||||
"will no longer be the direct owner of this item once it has been moved.",
|
||||
)
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "mockOrganizationName-2. Organization")
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ fun <T : Any> FlagKey<T>.ListItemContent(
|
||||
FlagKey.CredentialExchangeProtocolImport,
|
||||
FlagKey.RemoveCardPolicy,
|
||||
FlagKey.UserManagedPrivilegedApps,
|
||||
FlagKey.EnrollAeadOnKeyRotation,
|
||||
-> BooleanFlagItem(
|
||||
label = flagKey.getDisplayLabel(),
|
||||
key = flagKey as FlagKey<Boolean>,
|
||||
@@ -76,4 +77,8 @@ private fun <T : Any> FlagKey<T>.getDisplayLabel(): String = when (this) {
|
||||
FlagKey.BitwardenAuthenticationEnabled -> {
|
||||
stringResource(BitwardenString.bitwarden_authentication_enabled)
|
||||
}
|
||||
|
||||
FlagKey.EnrollAeadOnKeyRotation -> {
|
||||
stringResource(BitwardenString.enroll_aead_on_key_rotation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,14 @@ sealed class FlagKey<out T : Any> {
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the feature flag to enable the enrollment of AEAD on key rotation.
|
||||
*/
|
||||
data object EnrollAeadOnKeyRotation : FlagKey<Boolean>() {
|
||||
override val keyName: String = "enroll-aead-on-key-rotation"
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
//region Dummy keys for testing
|
||||
/**
|
||||
* Data object holding the key for a [Boolean] flag to be used in tests.
|
||||
|
||||
@@ -32,6 +32,10 @@ class FlagKeyTest {
|
||||
FlagKey.BitwardenAuthenticationEnabled.keyName,
|
||||
"bitwarden-authentication-enabled",
|
||||
)
|
||||
assertEquals(
|
||||
FlagKey.EnrollAeadOnKeyRotation.keyName,
|
||||
"enroll-aead-on-key-rotation",
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -44,6 +48,7 @@ class FlagKeyTest {
|
||||
FlagKey.UserManagedPrivilegedApps,
|
||||
FlagKey.RemoveCardPolicy,
|
||||
FlagKey.BitwardenAuthenticationEnabled,
|
||||
FlagKey.EnrollAeadOnKeyRotation,
|
||||
).all {
|
||||
!it.defaultValue
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.bitwarden.ui.platform.components.button
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
@@ -20,10 +21,10 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.semantics.CustomAccessibilityAction
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.customActions
|
||||
import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -42,7 +43,6 @@ import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
/**
|
||||
* A button which uses a read-only text field for layout and style purposes.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun BitwardenTextSelectionButton(
|
||||
label: String,
|
||||
@@ -58,15 +58,60 @@ fun BitwardenTextSelectionButton(
|
||||
semanticRole: Role = Role.Button,
|
||||
actionsPadding: PaddingValues = PaddingValues(end = 4.dp),
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
BitwardenTextSelectionButton(
|
||||
label = label,
|
||||
selectedOption = selectedOption,
|
||||
onClick = onClick,
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
tooltip = tooltip,
|
||||
insets = insets,
|
||||
textFieldTestTag = textFieldTestTag,
|
||||
semanticRole = semanticRole,
|
||||
actionsPadding = actionsPadding,
|
||||
actions = actions,
|
||||
supportingContent = supportingText?.let {
|
||||
{
|
||||
Text(
|
||||
text = it,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* A button which uses a read-only text field for layout and style purposes.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun BitwardenTextSelectionButton(
|
||||
label: String,
|
||||
selectedOption: String?,
|
||||
onClick: () -> Unit,
|
||||
cardStyle: CardStyle?,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
tooltip: TooltipData? = null,
|
||||
insets: PaddingValues = PaddingValues(),
|
||||
textFieldTestTag: String? = null,
|
||||
semanticRole: Role = Role.Button,
|
||||
actionsPadding: PaddingValues = PaddingValues(end = 4.dp),
|
||||
supportingContent: @Composable (ColumnScope.() -> Unit)?,
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.clearAndSetSemantics {
|
||||
.semantics {
|
||||
role = semanticRole
|
||||
contentDescription = supportingText
|
||||
?.let { "$selectedOption. $label. $it" }
|
||||
?: "$selectedOption. $label"
|
||||
contentDescription = "$selectedOption. $label"
|
||||
customActions = persistentListOfNotNull(
|
||||
tooltip?.let {
|
||||
CustomAccessibilityAction(
|
||||
@@ -132,7 +177,7 @@ fun BitwardenTextSelectionButton(
|
||||
.nullableTestTag(tag = textFieldTestTag)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
supportingText
|
||||
supportingContent
|
||||
?.let { content ->
|
||||
Spacer(modifier = Modifier.height(height = 6.dp))
|
||||
BitwardenHorizontalDivider(
|
||||
@@ -145,14 +190,7 @@ fun BitwardenTextSelectionButton(
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minHeight = 48.dp)
|
||||
.padding(vertical = 12.dp, horizontal = 16.dp),
|
||||
content = {
|
||||
Text(
|
||||
text = content,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
?: Spacer(modifier = Modifier.height(height = cardStyle?.let { 6.dp } ?: 0.dp))
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<string name="import_format_label_2fas_json">2FAS (no password)</string>
|
||||
<string name="import_format_label_lastpass_json">LastPass (.json)</string>
|
||||
<string name="import_format_label_aegis_json">Aegis (.json)</string>
|
||||
<string name="enroll_aead_on_key_rotation">Enroll AEAD on key rotation</string>
|
||||
|
||||
<!-- endregion Debug Menu -->
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user