mirror of
https://github.com/bitwarden/android.git
synced 2026-05-07 19:39:41 -05:00
PM-35281: feat: Update the BlockAutofill screen UI (#6807)
This commit is contained in:
@@ -42,7 +42,6 @@ fun AddEditBlockedUriDialog(
|
||||
onUriChange: (String) -> Unit,
|
||||
onCancelClick: () -> Unit,
|
||||
onSaveClick: (String) -> Unit,
|
||||
onDeleteClick: (() -> Unit)? = null,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
Dialog(
|
||||
@@ -66,7 +65,13 @@ fun AddEditBlockedUriDialog(
|
||||
modifier = Modifier
|
||||
.padding(top = 24.dp, start = 24.dp, end = 24.dp)
|
||||
.fillMaxWidth(),
|
||||
text = stringResource(id = BitwardenString.new_uri),
|
||||
text = stringResource(
|
||||
id = if (isEdit) {
|
||||
BitwardenString.edit_blocked_uri
|
||||
} else {
|
||||
BitwardenString.new_blocked_uri
|
||||
},
|
||||
),
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
style = BitwardenTheme.typography.headlineSmall,
|
||||
)
|
||||
@@ -104,13 +109,6 @@ fun AddEditBlockedUriDialog(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.padding(start = 8.dp, top = 24.dp, bottom = 24.dp, end = 24.dp),
|
||||
) {
|
||||
if (isEdit && onDeleteClick != null) {
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = BitwardenString.remove),
|
||||
onClick = onDeleteClick,
|
||||
)
|
||||
}
|
||||
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = BitwardenString.cancel),
|
||||
onClick = onCancelClick,
|
||||
|
||||
@@ -4,8 +4,6 @@ import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -17,17 +15,17 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
@@ -38,18 +36,24 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.bitwarden.ui.platform.base.util.bottomDivider
|
||||
import com.bitwarden.ui.platform.base.util.cardStyle
|
||||
import com.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||
import com.bitwarden.ui.platform.components.appbar.action.BitwardenOverflowActionItem
|
||||
import com.bitwarden.ui.platform.components.appbar.model.OverflowMenuItemData
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
import com.bitwarden.ui.platform.components.fab.BitwardenFloatingActionButton
|
||||
import com.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.bitwarden.ui.platform.resource.BitwardenDrawable
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* Displays the block auto-fill screen.
|
||||
* Displays the block autofill screen.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -74,7 +78,6 @@ fun BlockAutoFillScreen(
|
||||
BlockAutoFillAction.SaveUri(newUri = newUri, originalUri = originalUri),
|
||||
)
|
||||
},
|
||||
onRemoveClick = { viewModel.trySendAction(BlockAutoFillAction.RemoveUriClick(it)) },
|
||||
onDismissRequest = { viewModel.trySendAction(BlockAutoFillAction.DismissDialog) },
|
||||
)
|
||||
|
||||
@@ -107,84 +110,94 @@ fun BlockAutoFillScreen(
|
||||
}
|
||||
},
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
when (val viewState = state.viewState) {
|
||||
is BlockAutoFillState.ViewState.Content -> {
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 8.dp, horizontal = 20.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = BitwardenString
|
||||
.auto_fill_will_not_be_offered_for_these_ur_is,
|
||||
),
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items(viewState.blockedUris, key = { it }) { uri ->
|
||||
BlockAutoFillListItem(
|
||||
label = uri,
|
||||
onClick = {
|
||||
viewModel.trySendAction(BlockAutoFillAction.EditUriClick(uri))
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is BlockAutoFillState.ViewState.Empty -> {
|
||||
item {
|
||||
BlockAutoFillNoItems(
|
||||
addItemClickAction = {
|
||||
viewModel.trySendAction(BlockAutoFillAction.AddUriClick)
|
||||
},
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
}
|
||||
when (val viewState = state.viewState) {
|
||||
is BlockAutoFillState.ViewState.Content -> {
|
||||
BlockedAutofillContent(
|
||||
viewState = viewState,
|
||||
onEditUriClick = {
|
||||
viewModel.trySendAction(BlockAutoFillAction.EditUriClick(it))
|
||||
},
|
||||
onRemoveClick = {
|
||||
viewModel.trySendAction(BlockAutoFillAction.RemoveUriClick(it))
|
||||
},
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
|
||||
BlockAutoFillState.ViewState.Empty -> {
|
||||
BlockAutoFillNoItems(
|
||||
addItemClickAction = {
|
||||
viewModel.trySendAction(BlockAutoFillAction.AddUriClick)
|
||||
},
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BlockedAutofillContent(
|
||||
viewState: BlockAutoFillState.ViewState.Content,
|
||||
onEditUriClick: (String) -> Unit,
|
||||
onRemoveClick: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(modifier = modifier) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = BitwardenString.auto_fill_will_not_be_offered_for_these_ur_is,
|
||||
),
|
||||
textAlign = TextAlign.Center,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.animateItem(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||
}
|
||||
|
||||
itemsIndexed(
|
||||
items = viewState.blockedUris,
|
||||
key = { _, uri -> uri },
|
||||
) { index, uri ->
|
||||
BlockAutoFillListItem(
|
||||
label = uri,
|
||||
onDeleteClick = { onRemoveClick(uri) },
|
||||
onEditClick = { onEditUriClick(uri) },
|
||||
cardStyle = viewState.blockedUris.toListItemCardStyle(index = index),
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth()
|
||||
.animateItem(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BlockAutoFillDialogs(
|
||||
dialogState: BlockAutoFillState.DialogState? = null,
|
||||
onUriTextChange: (String) -> Unit,
|
||||
onSaveClick: (String, String?) -> Unit,
|
||||
onRemoveClick: (String) -> Unit,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
when (dialogState) {
|
||||
is BlockAutoFillState.DialogState.AddEdit -> {
|
||||
AddEditBlockedUriDialog(
|
||||
uri = dialogState.uri,
|
||||
isEdit = dialogState.originalUri != null,
|
||||
isEdit = dialogState.isEdit,
|
||||
errorMessage = dialogState.errorMessage?.invoke(),
|
||||
onUriChange = onUriTextChange,
|
||||
onDismissRequest = onDismissRequest,
|
||||
onDeleteClick = if (dialogState.isEdit) {
|
||||
{ dialogState.originalUri?.let { onRemoveClick(it) } }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onCancelClick = onDismissRequest,
|
||||
onSaveClick = { newUri -> onSaveClick(newUri, dialogState.originalUri) },
|
||||
)
|
||||
@@ -203,7 +216,7 @@ private fun BlockAutoFillNoItems(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
modifier = modifier.verticalScroll(state = rememberScrollState()),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
@@ -217,66 +230,87 @@ private fun BlockAutoFillNoItems(
|
||||
.size(size = 124.dp)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||
|
||||
Text(
|
||||
textAlign = TextAlign.Center,
|
||||
text = stringResource(id = BitwardenString.no_uris_blocked),
|
||||
style = BitwardenTheme.typography.titleMedium,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
|
||||
Text(
|
||||
textAlign = TextAlign.Center,
|
||||
text = stringResource(
|
||||
id = BitwardenString.auto_fill_will_not_be_offered_for_these_ur_is,
|
||||
),
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
BitwardenOutlinedButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
BitwardenFilledButton(
|
||||
label = stringResource(id = BitwardenString.new_blocked_uri),
|
||||
onClick = addItemClickAction,
|
||||
icon = rememberVectorPainter(id = BitwardenDrawable.ic_plus_small),
|
||||
modifier = Modifier
|
||||
.wrapContentWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BlockAutoFillListItem(
|
||||
label: String,
|
||||
onClick: () -> Unit,
|
||||
onEditClick: () -> Unit,
|
||||
onDeleteClick: () -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(
|
||||
color = BitwardenTheme.colorScheme.background.pressed,
|
||||
),
|
||||
onClick = onClick,
|
||||
)
|
||||
.bottomDivider(paddingStart = 16.dp)
|
||||
.padding(end = 8.dp, top = 16.dp, bottom = 16.dp)
|
||||
.then(modifier),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardStyle(
|
||||
cardStyle = cardStyle,
|
||||
paddingStart = 16.dp,
|
||||
paddingEnd = 4.dp,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(end = 16.dp)
|
||||
.weight(1f),
|
||||
text = label,
|
||||
style = BitwardenTheme.typography.bodyLarge,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
modifier = Modifier
|
||||
.padding(end = 16.dp)
|
||||
.weight(weight = 1f),
|
||||
)
|
||||
Icon(
|
||||
painter = rememberVectorPainter(id = BitwardenDrawable.ic_pencil_square),
|
||||
contentDescription = null,
|
||||
tint = BitwardenTheme.colorScheme.icon.primary,
|
||||
modifier = Modifier.size(24.dp),
|
||||
BitwardenOverflowActionItem(
|
||||
menuItemDataList = persistentListOf(
|
||||
OverflowMenuItemData(
|
||||
text = stringResource(id = BitwardenString.edit),
|
||||
onClick = onEditClick,
|
||||
),
|
||||
OverflowMenuItemData(
|
||||
text = stringResource(id = BitwardenString.delete),
|
||||
onClick = onDeleteClick,
|
||||
),
|
||||
),
|
||||
vectorIconRes = BitwardenDrawable.ic_ellipsis_horizontal,
|
||||
testTag = "Options",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.blockautofill.util.isValidPattern
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.blockautofill.util.validateUri
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import javax.inject.Inject
|
||||
@@ -40,7 +43,9 @@ class BlockAutoFillViewModel @Inject constructor(
|
||||
mutableStateFlow.update { currentState ->
|
||||
if (uris.isNotEmpty()) {
|
||||
currentState.copy(
|
||||
viewState = BlockAutoFillState.ViewState.Content(uris.map { it }),
|
||||
viewState = BlockAutoFillState.ViewState.Content(
|
||||
blockedUris = uris.distinct().toImmutableList(),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
currentState.copy(
|
||||
@@ -163,7 +168,7 @@ class BlockAutoFillViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the state for block auto fill.
|
||||
* Represents the state for block autofill.
|
||||
*
|
||||
* @property viewState indicates what view state the screen is in.
|
||||
*/
|
||||
@@ -203,7 +208,7 @@ data class BlockAutoFillState(
|
||||
*/
|
||||
@Parcelize
|
||||
data class Content(
|
||||
val blockedUris: List<String> = emptyList(),
|
||||
val blockedUris: ImmutableList<String> = persistentListOf(),
|
||||
) : ViewState()
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,10 +11,12 @@ import androidx.compose.ui.test.performScrollTo
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.bitwarden.ui.util.assertNoDialogExists
|
||||
import com.bitwarden.ui.util.assertNoPopupExists
|
||||
import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
@@ -68,7 +70,7 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `Screen should display content state view when in ViewState Content`() {
|
||||
mutableStateFlow.value = BlockAutoFillState(
|
||||
viewState = BlockAutoFillState.ViewState.Content(listOf("uri1", "uri2")),
|
||||
viewState = BlockAutoFillState.ViewState.Content(persistentListOf("uri1", "uri2")),
|
||||
)
|
||||
|
||||
listOf("uri1", "uri2").forEach { uri ->
|
||||
@@ -109,17 +111,6 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
|
||||
verify { viewModel.trySendAction(BlockAutoFillAction.AddUriClick) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on URI item click should send EditUriClick`() {
|
||||
mutableStateFlow.value = BlockAutoFillState(
|
||||
viewState = BlockAutoFillState.ViewState.Content(listOf("uri1")),
|
||||
)
|
||||
|
||||
composeTestRule.onNodeWithText("uri1").performClick()
|
||||
|
||||
verify { viewModel.trySendAction(BlockAutoFillAction.EditUriClick(uri = "uri1")) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should show add URI dialog according to state`() {
|
||||
composeTestRule.assertNoDialogExists()
|
||||
@@ -130,33 +121,36 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
|
||||
originalUri = null,
|
||||
errorMessage = null,
|
||||
),
|
||||
viewState = BlockAutoFillState.ViewState.Content(listOf("uri1")),
|
||||
viewState = BlockAutoFillState.ViewState.Content(persistentListOf("uri1")),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("New URI")
|
||||
.onNodeWithText("New blocked URI")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking a uri from the list should send EditUriClick action`() {
|
||||
val testUri = "http://test.com"
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
fun `clicking edit in popup menu should send EditUriClick action`() {
|
||||
composeTestRule.assertNoPopupExists()
|
||||
|
||||
val uriToRemove = "http://uriToRemove.com"
|
||||
mutableStateFlow.value = BlockAutoFillState(
|
||||
dialog = BlockAutoFillState.DialogState.AddEdit(
|
||||
uri = testUri,
|
||||
originalUri = testUri,
|
||||
errorMessage = null,
|
||||
viewState = BlockAutoFillState.ViewState.Content(
|
||||
blockedUris = persistentListOf(uriToRemove),
|
||||
),
|
||||
viewState = BlockAutoFillState.ViewState.Content(listOf("uri1")),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("New URI")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
.onNodeWithContentDescription(label = "More options")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Edit")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
verify { viewModel.trySendAction(BlockAutoFillAction.EditUriClick(uri = uriToRemove)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -171,7 +165,7 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
|
||||
originalUri = null,
|
||||
errorMessage = errorMessage.asText(),
|
||||
),
|
||||
viewState = BlockAutoFillState.ViewState.Content(listOf("uri1")),
|
||||
viewState = BlockAutoFillState.ViewState.Content(persistentListOf("uri1")),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
@@ -190,7 +184,7 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
|
||||
originalUri = null,
|
||||
errorMessage = null,
|
||||
),
|
||||
viewState = BlockAutoFillState.ViewState.Content(listOf("existingUri")),
|
||||
viewState = BlockAutoFillState.ViewState.Content(persistentListOf("existingUri")),
|
||||
)
|
||||
|
||||
val newUri = "http://newuri.com"
|
||||
@@ -209,7 +203,7 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
|
||||
originalUri = null,
|
||||
errorMessage = null,
|
||||
),
|
||||
viewState = BlockAutoFillState.ViewState.Content(emptyList()),
|
||||
viewState = BlockAutoFillState.ViewState.Content(persistentListOf()),
|
||||
)
|
||||
|
||||
composeTestRule.onNodeWithText("Cancel").performClick()
|
||||
@@ -218,20 +212,25 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking remove in dialog should send RemoveUriClick action`() {
|
||||
composeTestRule.assertNoDialogExists()
|
||||
fun `clicking delete in popup menu should send RemoveUriClick action`() {
|
||||
composeTestRule.assertNoPopupExists()
|
||||
|
||||
val uriToRemove = "http://uriToRemove.com"
|
||||
mutableStateFlow.value = BlockAutoFillState(
|
||||
dialog = BlockAutoFillState.DialogState.AddEdit(
|
||||
uri = uriToRemove,
|
||||
originalUri = uriToRemove,
|
||||
errorMessage = null,
|
||||
viewState = BlockAutoFillState.ViewState.Content(
|
||||
blockedUris = persistentListOf(uriToRemove),
|
||||
),
|
||||
viewState = BlockAutoFillState.ViewState.Content(listOf(uriToRemove, "otherUri")),
|
||||
)
|
||||
|
||||
composeTestRule.onNodeWithText("Remove").performClick()
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "More options")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Delete")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
verify { viewModel.trySendAction(BlockAutoFillAction.RemoveUriClick(uri = uriToRemove)) }
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -24,7 +26,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
|
||||
val viewModel = createViewModel()
|
||||
val expectedState = BlockAutoFillState(
|
||||
viewState = BlockAutoFillState.ViewState.Content(
|
||||
blockedUris = listOf("blockedUri"),
|
||||
blockedUris = persistentListOf("blockedUri"),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -64,7 +66,9 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
|
||||
originalUri = null,
|
||||
errorMessage = null,
|
||||
),
|
||||
viewState = BlockAutoFillState.ViewState.Content(blockedUris = listOf("blockedUri")),
|
||||
viewState = BlockAutoFillState.ViewState.Content(
|
||||
blockedUris = persistentListOf("blockedUri"),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
@@ -82,7 +86,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
|
||||
originalUri = testUri,
|
||||
errorMessage = null,
|
||||
),
|
||||
viewState = BlockAutoFillState.ViewState.Content(listOf("blockedUri")),
|
||||
viewState = BlockAutoFillState.ViewState.Content(persistentListOf("blockedUri")),
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
@@ -104,7 +108,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
|
||||
val expectedState = BlockAutoFillState(
|
||||
dialog = null,
|
||||
viewState = BlockAutoFillState.ViewState.Content(
|
||||
blockedUris = listOf("http://b.com"),
|
||||
blockedUris = persistentListOf("http://b.com"),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -128,7 +132,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
|
||||
val expectedState = BlockAutoFillState(
|
||||
dialog = null,
|
||||
viewState = BlockAutoFillState.ViewState.Content(
|
||||
blockedUris = blockedUris,
|
||||
blockedUris = blockedUris.toImmutableList(),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -152,7 +156,11 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
|
||||
val expectedState = BlockAutoFillState(
|
||||
dialog = null,
|
||||
viewState = BlockAutoFillState.ViewState.Content(
|
||||
blockedUris = listOf("http://existing.com", "http://new.com", "http://another.com"),
|
||||
blockedUris = persistentListOf(
|
||||
"http://existing.com",
|
||||
"http://new.com",
|
||||
"http://another.com",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -189,7 +197,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
|
||||
val expectedState = BlockAutoFillState(
|
||||
dialog = null,
|
||||
viewState = BlockAutoFillState.ViewState.Content(
|
||||
blockedUris = listOf("http://other.com", "http://new.com"),
|
||||
blockedUris = persistentListOf("http://other.com", "http://new.com"),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -217,7 +225,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
|
||||
val expectedState = BlockAutoFillState(
|
||||
dialog = null,
|
||||
viewState = BlockAutoFillState.ViewState.Content(
|
||||
blockedUris = listOf("http://other.com", "http://same.com"),
|
||||
blockedUris = persistentListOf("http://other.com", "http://same.com"),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -249,7 +257,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
|
||||
errorMessage = BitwardenString.the_urix_is_already_blocked.asText("http://b.com"),
|
||||
),
|
||||
viewState = BlockAutoFillState.ViewState.Content(
|
||||
blockedUris = listOf("http://a.com", "http://b.com"),
|
||||
blockedUris = persistentListOf("http://a.com", "http://b.com"),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -295,7 +295,6 @@ Scanning will happen automatically.</string>
|
||||
<string name="field_type_text">Text</string>
|
||||
<string name="select_type_field">What type of custom field do you want to add?</string>
|
||||
<string name="remove">Remove</string>
|
||||
<string name="new_uri">New URI</string>
|
||||
<string name="add_website">Add website</string>
|
||||
<string name="base_domain">Base domain</string>
|
||||
<string name="default_text">Default (%1$s)</string>
|
||||
@@ -667,7 +666,9 @@ Do you want to switch to this account?</string>
|
||||
<string name="you_will_be_notified_once_approved">You will be notified once approved.</string>
|
||||
<string name="trouble_logging_in">Trouble logging in?</string>
|
||||
<string name="block_auto_fill">Block autofill</string>
|
||||
<string name="no_uris_blocked">No URIs blocked</string>
|
||||
<string name="auto_fill_will_not_be_offered_for_these_ur_is">Autofill will not be offered for these URIs.</string>
|
||||
<string name="edit_blocked_uri">Edit blocked URI</string>
|
||||
<string name="new_blocked_uri">New blocked URI</string>
|
||||
<string name="invalid_format_use_https_http_or_android_app">Invalid format. Use https://, http://, or androidapp://</string>
|
||||
<string name="enter_uri">Enter URI</string>
|
||||
|
||||
Reference in New Issue
Block a user