mirror of
https://github.com/bitwarden/android.git
synced 2025-03-16 19:28:44 +03:00
BIT-2295: Simplify the pending requests UI (#1325)
This commit is contained in:
parent
b739be712a
commit
25c7ed0835
17 changed files with 77 additions and 673 deletions
|
@ -14,6 +14,7 @@
|
|||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
|
||||
<application
|
||||
android:name=".BitwardenApplication"
|
||||
|
|
|
@ -221,19 +221,6 @@ interface SettingsDiskSource {
|
|||
blockedAutofillUris: List<String>?,
|
||||
)
|
||||
|
||||
/**
|
||||
* Gets whether or not the given [userId] has enabled approving passwordless logins.
|
||||
*/
|
||||
fun getApprovePasswordlessLoginsEnabled(userId: String): Boolean?
|
||||
|
||||
/**
|
||||
* Stores whether or not [isApprovePasswordlessLoginsEnabled] for the given [userId].
|
||||
*/
|
||||
fun storeApprovePasswordlessLoginsEnabled(
|
||||
userId: String,
|
||||
isApprovePasswordlessLoginsEnabled: Boolean?,
|
||||
)
|
||||
|
||||
/**
|
||||
* Gets whether or not the given [userId] has enabled screen capture.
|
||||
*/
|
||||
|
|
|
@ -26,7 +26,6 @@ private const val DEFAULT_URI_MATCH_TYPE_KEY = "defaultUriMatch"
|
|||
private const val DISABLE_AUTO_TOTP_COPY_KEY = "disableAutoTotpCopy"
|
||||
private const val DISABLE_AUTOFILL_SAVE_PROMPT_KEY = "autofillDisableSavePrompt"
|
||||
private const val DISABLE_ICON_LOADING_KEY = "disableFavicon"
|
||||
private const val APPROVE_PASSWORDLESS_LOGINS_KEY = "approvePasswordlessLogins"
|
||||
private const val SCREEN_CAPTURE_ALLOW_KEY = "screenCaptureAllowed"
|
||||
private const val SYSTEM_BIOMETRIC_INTEGRITY_SOURCE_KEY = "biometricIntegritySource"
|
||||
private const val ACCOUNT_BIOMETRIC_INTEGRITY_VALID_KEY = "accountBiometricIntegrityValid"
|
||||
|
@ -139,10 +138,6 @@ class SettingsDiskSourceImpl(
|
|||
storePullToRefreshEnabled(userId = userId, isPullToRefreshEnabled = null)
|
||||
storeInlineAutofillEnabled(userId = userId, isInlineAutofillEnabled = null)
|
||||
storeBlockedAutofillUris(userId = userId, blockedAutofillUris = null)
|
||||
storeApprovePasswordlessLoginsEnabled(
|
||||
userId = userId,
|
||||
isApprovePasswordlessLoginsEnabled = null,
|
||||
)
|
||||
storeLastSyncTime(userId = userId, lastSyncTime = null)
|
||||
storeClearClipboardFrequencySeconds(userId = userId, frequency = null)
|
||||
removeWithPrefix(prefix = ACCOUNT_BIOMETRIC_INTEGRITY_VALID_KEY.appendIdentifier(userId))
|
||||
|
@ -356,20 +351,6 @@ class SettingsDiskSourceImpl(
|
|||
bufferedMutableSharedFlow(replay = 1)
|
||||
}
|
||||
|
||||
override fun getApprovePasswordlessLoginsEnabled(userId: String): Boolean? {
|
||||
return getBoolean(key = APPROVE_PASSWORDLESS_LOGINS_KEY.appendIdentifier(userId))
|
||||
}
|
||||
|
||||
override fun storeApprovePasswordlessLoginsEnabled(
|
||||
userId: String,
|
||||
isApprovePasswordlessLoginsEnabled: Boolean?,
|
||||
) {
|
||||
putBoolean(
|
||||
key = APPROVE_PASSWORDLESS_LOGINS_KEY.appendIdentifier(userId),
|
||||
value = isApprovePasswordlessLoginsEnabled,
|
||||
)
|
||||
}
|
||||
|
||||
override fun getScreenCaptureAllowed(userId: String): Boolean? {
|
||||
return getBoolean(key = SCREEN_CAPTURE_ALLOW_KEY.appendIdentifier(userId))
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.x8bit.bitwarden.data.platform.manager
|
|||
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.PushDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.PushTokenRequest
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.service.PushService
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
|
@ -36,10 +35,8 @@ import javax.inject.Inject
|
|||
/**
|
||||
* Primary implementation of [PushManager].
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
class PushManagerImpl @Inject constructor(
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
private val settingsDiskSource: SettingsDiskSource,
|
||||
private val pushDiskSource: PushDiskSource,
|
||||
private val pushService: PushService,
|
||||
private val clock: Clock,
|
||||
|
@ -125,16 +122,14 @@ class PushManagerImpl @Inject constructor(
|
|||
NotificationType.AUTH_REQUEST,
|
||||
NotificationType.AUTH_REQUEST_RESPONSE,
|
||||
-> {
|
||||
if (settingsDiskSource.getApprovePasswordlessLoginsEnabled(userId) == true) {
|
||||
val payload: NotificationPayload.PasswordlessRequestNotification =
|
||||
json.decodeFromString(string = notification.payload)
|
||||
mutablePasswordlessRequestSharedFlow.tryEmit(
|
||||
PasswordlessRequestData(
|
||||
loginRequestId = payload.id,
|
||||
userId = payload.userId,
|
||||
),
|
||||
)
|
||||
}
|
||||
val payload: NotificationPayload.PasswordlessRequestNotification =
|
||||
json.decodeFromString(string = notification.payload)
|
||||
mutablePasswordlessRequestSharedFlow.tryEmit(
|
||||
PasswordlessRequestData(
|
||||
loginRequestId = payload.id,
|
||||
userId = payload.userId,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
NotificationType.LOG_OUT -> {
|
||||
|
|
|
@ -143,7 +143,6 @@ object PlatformManagerModule {
|
|||
@Singleton
|
||||
fun providePushManager(
|
||||
authDiskSource: AuthDiskSource,
|
||||
settingsDiskSource: SettingsDiskSource,
|
||||
pushDiskSource: PushDiskSource,
|
||||
pushService: PushService,
|
||||
dispatcherManager: DispatcherManager,
|
||||
|
@ -151,7 +150,6 @@ object PlatformManagerModule {
|
|||
json: Json,
|
||||
): PushManager = PushManagerImpl(
|
||||
authDiskSource = authDiskSource,
|
||||
settingsDiskSource = settingsDiskSource,
|
||||
pushDiskSource = pushDiskSource,
|
||||
pushService = pushService,
|
||||
dispatcherManager = dispatcherManager,
|
||||
|
|
|
@ -118,11 +118,6 @@ interface SettingsRepository {
|
|||
*/
|
||||
var blockedAutofillUris: List<String>
|
||||
|
||||
/**
|
||||
* Whether or not approving passwordless logins is enabled for the current user.
|
||||
*/
|
||||
var isApprovePasswordlessLoginsEnabled: Boolean
|
||||
|
||||
/**
|
||||
* Emits updates whenever there is a change in the app's status for supporting autofill.
|
||||
*
|
||||
|
|
|
@ -245,19 +245,6 @@ class SettingsRepositoryImpl(
|
|||
)
|
||||
}
|
||||
|
||||
override var isApprovePasswordlessLoginsEnabled: Boolean
|
||||
get() = activeUserId
|
||||
?.let {
|
||||
settingsDiskSource.getApprovePasswordlessLoginsEnabled(it)
|
||||
}
|
||||
?: false
|
||||
set(value) {
|
||||
val userId = activeUserId ?: return
|
||||
settingsDiskSource.storeApprovePasswordlessLoginsEnabled(
|
||||
userId = userId,
|
||||
isApprovePasswordlessLoginsEnabled = value,
|
||||
)
|
||||
}
|
||||
override val isAutofillEnabledStateFlow: StateFlow<Boolean> =
|
||||
autofillEnabledManager.isAutofillEnabledStateFlow
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
|
||||
|
||||
import android.Manifest
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
|
@ -58,10 +56,8 @@ import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenWideSwitch
|
|||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalBiometricsManager
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalPermissionsManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricsManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
|
||||
import com.x8bit.bitwarden.ui.platform.theme.LocalNonMaterialColors
|
||||
import com.x8bit.bitwarden.ui.platform.theme.LocalNonMaterialTypography
|
||||
import com.x8bit.bitwarden.ui.platform.util.displayLabel
|
||||
|
@ -84,7 +80,6 @@ fun AccountSecurityScreen(
|
|||
viewModel: AccountSecurityViewModel = hiltViewModel(),
|
||||
biometricsManager: BiometricsManager = LocalBiometricsManager.current,
|
||||
intentManager: IntentManager = LocalIntentManager.current,
|
||||
permissionsManager: PermissionsManager = LocalPermissionsManager.current,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsState()
|
||||
val context = LocalContext.current
|
||||
|
@ -149,7 +144,7 @@ fun AccountSecurityScreen(
|
|||
},
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
Modifier
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
|
@ -160,31 +155,15 @@ fun AccountSecurityScreen(
|
|||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
ApprovePasswordlessLoginsRow(
|
||||
isApproveLoginRequestsEnabled = state.isApproveLoginRequestsEnabled,
|
||||
onApprovePasswordlessLoginsAction = remember(viewModel) {
|
||||
{ viewModel.trySendAction(it) }
|
||||
},
|
||||
permissionsManager = permissionsManager,
|
||||
onPushNotificationConfirm = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AccountSecurityAction.PushNotificationConfirm) }
|
||||
BitwardenTextRow(
|
||||
text = stringResource(id = R.string.pending_log_in_requests),
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AccountSecurityAction.PendingLoginRequestsClick) }
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("ApproveLoginRequestsSwitch")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.testTag("PendingLogInRequestsLabel")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
if (state.isApproveLoginRequestsEnabled) {
|
||||
BitwardenTextRow(
|
||||
text = stringResource(id = R.string.pending_log_in_requests),
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AccountSecurityAction.PendingLoginRequestsClick) }
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("PendingLogInRequestsLabel")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
BitwardenListHeaderText(
|
||||
|
@ -824,92 +803,3 @@ private fun FingerPrintPhraseDialog(
|
|||
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun ApprovePasswordlessLoginsRow(
|
||||
isApproveLoginRequestsEnabled: Boolean,
|
||||
@Suppress("MaxLineLength")
|
||||
onApprovePasswordlessLoginsAction: (AccountSecurityAction.ApprovePasswordlessLoginsToggle) -> Unit,
|
||||
onPushNotificationConfirm: () -> Unit,
|
||||
permissionsManager: PermissionsManager,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var shouldShowConfirmationDialog by remember { mutableStateOf(false) }
|
||||
var shouldShowPermissionDialog by remember { mutableStateOf(false) }
|
||||
BitwardenWideSwitch(
|
||||
label = stringResource(
|
||||
id = R.string.use_this_device_to_approve_login_requests_made_from_other_devices,
|
||||
),
|
||||
isChecked = isApproveLoginRequestsEnabled,
|
||||
onCheckedChange = { isChecked ->
|
||||
if (isChecked) {
|
||||
onApprovePasswordlessLoginsAction(
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.PendingEnabled,
|
||||
)
|
||||
shouldShowConfirmationDialog = true
|
||||
} else {
|
||||
onApprovePasswordlessLoginsAction(
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.Disabled,
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
)
|
||||
|
||||
if (shouldShowConfirmationDialog) {
|
||||
BitwardenTwoButtonDialog(
|
||||
title = stringResource(id = R.string.approve_login_requests),
|
||||
message = stringResource(
|
||||
id = R.string.use_this_device_to_approve_login_requests_made_from_other_devices,
|
||||
),
|
||||
confirmButtonText = stringResource(id = R.string.yes),
|
||||
dismissButtonText = stringResource(id = R.string.no),
|
||||
onConfirmClick = {
|
||||
onApprovePasswordlessLoginsAction(
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.Enabled,
|
||||
)
|
||||
shouldShowConfirmationDialog = false
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
@Suppress("MaxLineLength")
|
||||
if (!permissionsManager.checkPermission(Manifest.permission.POST_NOTIFICATIONS)) {
|
||||
shouldShowPermissionDialog = true
|
||||
}
|
||||
}
|
||||
},
|
||||
onDismissClick = {
|
||||
onApprovePasswordlessLoginsAction(
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.Disabled,
|
||||
)
|
||||
shouldShowConfirmationDialog = false
|
||||
},
|
||||
onDismissRequest = {
|
||||
onApprovePasswordlessLoginsAction(
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.Disabled,
|
||||
)
|
||||
shouldShowConfirmationDialog = false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (shouldShowPermissionDialog) {
|
||||
BitwardenTwoButtonDialog(
|
||||
title = null,
|
||||
message = stringResource(
|
||||
id = R.string.receive_push_notifications_for_new_login_requests,
|
||||
),
|
||||
confirmButtonText = stringResource(id = R.string.settings),
|
||||
dismissButtonText = stringResource(id = R.string.no_thanks),
|
||||
onConfirmClick = {
|
||||
shouldShowPermissionDialog = false
|
||||
onPushNotificationConfirm()
|
||||
},
|
||||
onDismissClick = {
|
||||
shouldShowPermissionDialog = false
|
||||
},
|
||||
onDismissRequest = {
|
||||
shouldShowPermissionDialog = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,14 +41,13 @@ class AccountSecurityViewModel @Inject constructor(
|
|||
private val vaultRepository: VaultRepository,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val environmentRepository: EnvironmentRepository,
|
||||
private val policyManager: PolicyManager,
|
||||
policyManager: PolicyManager,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : BaseViewModel<AccountSecurityState, AccountSecurityEvent, AccountSecurityAction>(
|
||||
initialState = savedStateHandle[KEY_STATE]
|
||||
?: AccountSecurityState(
|
||||
dialog = null,
|
||||
fingerprintPhrase = "".asText(), // This will be filled in dynamically
|
||||
isApproveLoginRequestsEnabled = settingsRepository.isApprovePasswordlessLoginsEnabled,
|
||||
isUnlockWithBiometricsEnabled = settingsRepository.isUnlockWithBiometricsEnabled,
|
||||
isUnlockWithPasswordEnabled = authRepository
|
||||
.userStateFlow
|
||||
|
@ -118,11 +117,6 @@ class AccountSecurityViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is AccountSecurityAction.UnlockWithPinToggle -> handleUnlockWithPinToggle(action)
|
||||
|
||||
is AccountSecurityAction.ApprovePasswordlessLoginsToggle -> {
|
||||
handleApprovePasswordlessLoginsToggle(action)
|
||||
}
|
||||
|
||||
is AccountSecurityAction.PushNotificationConfirm -> handlePushNotificationConfirm()
|
||||
is AccountSecurityAction.Internal -> handleInternalAction(action)
|
||||
}
|
||||
|
@ -158,26 +152,6 @@ class AccountSecurityViewModel @Inject constructor(
|
|||
vaultRepository.lockVaultForCurrentUser()
|
||||
}
|
||||
|
||||
private fun handleApprovePasswordlessLoginsToggle(
|
||||
action: AccountSecurityAction.ApprovePasswordlessLoginsToggle,
|
||||
) {
|
||||
when (action) {
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.Disabled -> {
|
||||
settingsRepository.isApprovePasswordlessLoginsEnabled = false
|
||||
mutableStateFlow.update { it.copy(isApproveLoginRequestsEnabled = false) }
|
||||
}
|
||||
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.Enabled -> {
|
||||
settingsRepository.isApprovePasswordlessLoginsEnabled = true
|
||||
mutableStateFlow.update { it.copy(isApproveLoginRequestsEnabled = true) }
|
||||
}
|
||||
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.PendingEnabled -> {
|
||||
mutableStateFlow.update { it.copy(isApproveLoginRequestsEnabled = true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePushNotificationConfirm() {
|
||||
sendEvent(AccountSecurityEvent.NavigateToApplicationDataSettings)
|
||||
}
|
||||
|
@ -368,7 +342,6 @@ class AccountSecurityViewModel @Inject constructor(
|
|||
data class AccountSecurityState(
|
||||
val dialog: AccountSecurityDialog?,
|
||||
val fingerprintPhrase: Text,
|
||||
val isApproveLoginRequestsEnabled: Boolean,
|
||||
val isUnlockWithBiometricsEnabled: Boolean,
|
||||
val isUnlockWithPasswordEnabled: Boolean,
|
||||
val isUnlockWithPinEnabled: Boolean,
|
||||
|
@ -551,26 +524,6 @@ sealed class AccountSecurityAction {
|
|||
*/
|
||||
data object PushNotificationConfirm : AccountSecurityAction()
|
||||
|
||||
/**
|
||||
* User toggled the approve passwordless logins switch.
|
||||
*/
|
||||
sealed class ApprovePasswordlessLoginsToggle : AccountSecurityAction() {
|
||||
/**
|
||||
* The toggle was enabled and confirmed.
|
||||
*/
|
||||
data object Enabled : ApprovePasswordlessLoginsToggle()
|
||||
|
||||
/**
|
||||
* The toggle was enabled but not yet confirmed.
|
||||
*/
|
||||
data object PendingEnabled : ApprovePasswordlessLoginsToggle()
|
||||
|
||||
/**
|
||||
* The toggle was disabled.
|
||||
*/
|
||||
data object Disabled : ApprovePasswordlessLoginsToggle()
|
||||
}
|
||||
|
||||
/**
|
||||
* User toggled the unlock with pin switch.
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.vault
|
||||
|
||||
import android.Manifest
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.scaleIn
|
||||
|
@ -54,9 +56,11 @@ import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
|||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalExitManager
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalPermissionsManager
|
||||
import com.x8bit.bitwarden.ui.platform.feature.search.model.SearchType
|
||||
import com.x8bit.bitwarden.ui.platform.manager.exit.ExitManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
|
||||
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.handlers.VaultHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultItemListingType
|
||||
|
@ -80,6 +84,7 @@ fun VaultScreen(
|
|||
onDimBottomNavBarRequest: (shouldDim: Boolean) -> Unit,
|
||||
exitManager: ExitManager = LocalExitManager.current,
|
||||
intentManager: IntentManager = LocalIntentManager.current,
|
||||
permissionsManager: PermissionsManager = LocalPermissionsManager.current,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsState()
|
||||
val context = LocalContext.current
|
||||
|
@ -120,6 +125,7 @@ fun VaultScreen(
|
|||
}
|
||||
}
|
||||
val vaultHandlers = remember(viewModel) { VaultHandlers.create(viewModel) }
|
||||
VaultScreenPushNotifications(permissionsManager = permissionsManager)
|
||||
VaultScreenScaffold(
|
||||
state = state,
|
||||
pullToRefreshState = pullToRefreshState,
|
||||
|
@ -128,6 +134,25 @@ fun VaultScreen(
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the notifications permission request.
|
||||
*/
|
||||
@Composable
|
||||
private fun VaultScreenPushNotifications(
|
||||
permissionsManager: PermissionsManager,
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
|
||||
val launcher = permissionsManager.getLauncher {
|
||||
// We do not actually care what the response is, we just need
|
||||
// to give the user a chance to give us the permission.
|
||||
}
|
||||
LaunchedEffect(key1 = Unit) {
|
||||
if (!permissionsManager.checkPermission(Manifest.permission.POST_NOTIFICATIONS)) {
|
||||
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scaffold for the [VaultScreen]
|
||||
*/
|
||||
|
|
|
@ -127,10 +127,6 @@ class SettingsDiskSourceTest {
|
|||
userId = userId,
|
||||
blockedAutofillUris = listOf("www.example.com"),
|
||||
)
|
||||
settingsDiskSource.storeApprovePasswordlessLoginsEnabled(
|
||||
userId = userId,
|
||||
isApprovePasswordlessLoginsEnabled = true,
|
||||
)
|
||||
settingsDiskSource.storeLastSyncTime(
|
||||
userId = userId,
|
||||
lastSyncTime = Instant.parse("2023-10-27T12:00:00Z"),
|
||||
|
@ -161,7 +157,6 @@ class SettingsDiskSourceTest {
|
|||
assertNull(settingsDiskSource.getPullToRefreshEnabled(userId = userId))
|
||||
assertNull(settingsDiskSource.getInlineAutofillEnabled(userId = userId))
|
||||
assertNull(settingsDiskSource.getBlockedAutofillUris(userId = userId))
|
||||
assertNull(settingsDiskSource.getApprovePasswordlessLoginsEnabled(userId = userId))
|
||||
assertNull(settingsDiskSource.getLastSyncTime(userId = userId))
|
||||
assertNull(settingsDiskSource.getClearClipboardFrequencySeconds(userId = userId))
|
||||
assertNull(
|
||||
|
@ -821,67 +816,6 @@ class SettingsDiskSourceTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getApprovePasswordlessLoginsEnabled when values are present should pull from SharedPreferences`() {
|
||||
val approvePasswordlessLoginsBaseKey = "bwPreferencesStorage:approvePasswordlessLogins"
|
||||
val mockUserId = "mockUserId"
|
||||
val isEnabled = true
|
||||
fakeSharedPreferences
|
||||
.edit {
|
||||
putBoolean(
|
||||
"${approvePasswordlessLoginsBaseKey}_$mockUserId",
|
||||
isEnabled,
|
||||
)
|
||||
}
|
||||
val actual = settingsDiskSource.getApprovePasswordlessLoginsEnabled(userId = mockUserId)
|
||||
assertEquals(
|
||||
isEnabled,
|
||||
actual,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getApprovePasswordlessLoginsEnabled when values are absent should return null`() {
|
||||
val mockUserId = "mockUserId"
|
||||
assertNull(settingsDiskSource.getApprovePasswordlessLoginsEnabled(userId = mockUserId))
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `storeApprovePasswordlessLoginsEnabled for non-null values should update SharedPreferences`() {
|
||||
val approvePasswordlessLoginsBaseKey = "bwPreferencesStorage:approvePasswordlessLogins"
|
||||
val mockUserId = "mockUserId"
|
||||
val isEnabled = true
|
||||
settingsDiskSource.storeApprovePasswordlessLoginsEnabled(
|
||||
userId = mockUserId,
|
||||
isApprovePasswordlessLoginsEnabled = isEnabled,
|
||||
)
|
||||
val actual = fakeSharedPreferences.getBoolean(
|
||||
"${approvePasswordlessLoginsBaseKey}_$mockUserId",
|
||||
false,
|
||||
)
|
||||
assertEquals(
|
||||
isEnabled,
|
||||
actual,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `storeApprovePasswordlessLoginsEnabled for null values should clear SharedPreferences`() {
|
||||
val approvePasswordlessLoginsBaseKey = "bwPreferencesStorage:approvePasswordlessLogins"
|
||||
val mockUserId = "mockUserId"
|
||||
val approvePasswordlessLoginsKey = "${approvePasswordlessLoginsBaseKey}_$mockUserId"
|
||||
fakeSharedPreferences.edit {
|
||||
putBoolean(approvePasswordlessLoginsKey, true)
|
||||
}
|
||||
settingsDiskSource.storeApprovePasswordlessLoginsEnabled(
|
||||
userId = mockUserId,
|
||||
isApprovePasswordlessLoginsEnabled = null,
|
||||
)
|
||||
assertFalse(fakeSharedPreferences.contains(approvePasswordlessLoginsKey))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getScreenCaptureAllowed should pull from SharedPreferences`() {
|
||||
val screenCaptureAllowBaseKey = "bwPreferencesStorage:screenCaptureAllowed"
|
||||
|
|
|
@ -53,7 +53,6 @@ class FakeSettingsDiskSource : SettingsDiskSource {
|
|||
private var storedIsIconLoadingDisabled: Boolean? = null
|
||||
private var storedIsCrashLoggingEnabled: Boolean? = null
|
||||
private var storedInitialAutofillDialogShown: Boolean? = null
|
||||
private val storedApprovePasswordLoginsEnabled = mutableMapOf<String, Boolean?>()
|
||||
private val storedScreenCaptureAllowed = mutableMapOf<String, Boolean?>()
|
||||
private var storedSystemBiometricIntegritySource: String? = null
|
||||
private val storedAccountBiometricIntegrityValidity = mutableMapOf<String, Boolean?>()
|
||||
|
@ -247,16 +246,6 @@ class FakeSettingsDiskSource : SettingsDiskSource {
|
|||
storedBlockedAutofillUris[userId] = blockedAutofillUris
|
||||
}
|
||||
|
||||
override fun getApprovePasswordlessLoginsEnabled(userId: String): Boolean? =
|
||||
storedApprovePasswordLoginsEnabled[userId]
|
||||
|
||||
override fun storeApprovePasswordlessLoginsEnabled(
|
||||
userId: String,
|
||||
isApprovePasswordlessLoginsEnabled: Boolean?,
|
||||
) {
|
||||
storedApprovePasswordLoginsEnabled[userId] = isApprovePasswordlessLoginsEnabled
|
||||
}
|
||||
|
||||
override fun getScreenCaptureAllowed(userId: String): Boolean? =
|
||||
storedScreenCaptureAllowed[userId]
|
||||
|
||||
|
|
|
@ -10,8 +10,6 @@ import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
|
|||
import com.x8bit.bitwarden.data.platform.base.FakeSharedPreferences
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.PushDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.PushDiskSourceImpl
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.util.FakeSettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.di.PlatformNetworkModule
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.PushTokenRequest
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.service.PushService
|
||||
|
@ -51,8 +49,6 @@ class PushManagerTest {
|
|||
|
||||
private val authDiskSource: AuthDiskSource = FakeAuthDiskSource()
|
||||
|
||||
private val settingsDiskSource: SettingsDiskSource = FakeSettingsDiskSource()
|
||||
|
||||
private val pushDiskSource: PushDiskSource = PushDiskSourceImpl(FakeSharedPreferences())
|
||||
|
||||
private val pushService: PushService = mockk()
|
||||
|
@ -63,7 +59,6 @@ class PushManagerTest {
|
|||
fun setUp() {
|
||||
pushManager = PushManagerImpl(
|
||||
authDiskSource = authDiskSource,
|
||||
settingsDiskSource = settingsDiskSource,
|
||||
pushDiskSource = pushDiskSource,
|
||||
pushService = pushService,
|
||||
dispatcherManager = dispatcherManager,
|
||||
|
@ -91,73 +86,33 @@ class PushManagerTest {
|
|||
pushManager.onMessageReceived(INVALID_NOTIFICATION_JSON)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `onMessageReceived auth request emits to nothing when getApprovePasswordlessLoginsEnabled is not true`() =
|
||||
runTest {
|
||||
settingsDiskSource.storeApprovePasswordlessLoginsEnabled(
|
||||
userId = userId,
|
||||
isApprovePasswordlessLoginsEnabled = false,
|
||||
fun `onMessageReceived auth request emits to passwordlessRequestFlow`() = runTest {
|
||||
pushManager.passwordlessRequestFlow.test {
|
||||
pushManager.onMessageReceived(AUTH_REQUEST_NOTIFICATION_JSON)
|
||||
assertEquals(
|
||||
PasswordlessRequestData(
|
||||
loginRequestId = "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321",
|
||||
userId = "078966a2-93c2-4618-ae2a-0a2394c88d37",
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
pushManager.passwordlessRequestFlow.test {
|
||||
pushManager.onMessageReceived(AUTH_REQUEST_NOTIFICATION_JSON)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `onMessageReceived auth request emits to passwordlessRequestFlow when getApprovePasswordlessLoginsEnabled is true`() =
|
||||
runTest {
|
||||
settingsDiskSource.storeApprovePasswordlessLoginsEnabled(
|
||||
userId = userId,
|
||||
isApprovePasswordlessLoginsEnabled = true,
|
||||
fun `onMessageReceived auth request response emits to passwordlessRequestFlow`() = runTest {
|
||||
pushManager.passwordlessRequestFlow.test {
|
||||
pushManager.onMessageReceived(AUTH_REQUEST_RESPONSE_NOTIFICATION_JSON)
|
||||
assertEquals(
|
||||
PasswordlessRequestData(
|
||||
loginRequestId = "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321",
|
||||
userId = "078966a2-93c2-4618-ae2a-0a2394c88d37",
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
pushManager.passwordlessRequestFlow.test {
|
||||
pushManager.onMessageReceived(AUTH_REQUEST_NOTIFICATION_JSON)
|
||||
assertEquals(
|
||||
PasswordlessRequestData(
|
||||
loginRequestId = "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321",
|
||||
userId = "078966a2-93c2-4618-ae2a-0a2394c88d37",
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `onMessageReceived auth request response emits nothing when getApprovePasswordlessLoginsEnabled is not true`() =
|
||||
runTest {
|
||||
settingsDiskSource.storeApprovePasswordlessLoginsEnabled(
|
||||
userId = userId,
|
||||
isApprovePasswordlessLoginsEnabled = false,
|
||||
)
|
||||
pushManager.passwordlessRequestFlow.test {
|
||||
pushManager.onMessageReceived(AUTH_REQUEST_RESPONSE_NOTIFICATION_JSON)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `onMessageReceived auth request response emits to passwordlessRequestFlow when getApprovePasswordlessLoginsEnabled is true`() =
|
||||
runTest {
|
||||
settingsDiskSource.storeApprovePasswordlessLoginsEnabled(
|
||||
userId = userId,
|
||||
isApprovePasswordlessLoginsEnabled = true,
|
||||
)
|
||||
pushManager.passwordlessRequestFlow.test {
|
||||
pushManager.onMessageReceived(AUTH_REQUEST_RESPONSE_NOTIFICATION_JSON)
|
||||
assertEquals(
|
||||
PasswordlessRequestData(
|
||||
loginRequestId = "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321",
|
||||
userId = "078966a2-93c2-4618-ae2a-0a2394c88d37",
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived logout should emit to logoutFlow`() = runTest {
|
||||
|
|
|
@ -905,31 +905,6 @@ class SettingsRepositoryTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isApprovePasswordlessLoginsEnabled should properly update SettingsDiskSource`() {
|
||||
fakeAuthDiskSource.userState = null
|
||||
assertFalse(settingsRepository.isApprovePasswordlessLoginsEnabled)
|
||||
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
// Updates to the disk source change the repository value
|
||||
fakeSettingsDiskSource.storeApprovePasswordlessLoginsEnabled(
|
||||
userId = USER_ID,
|
||||
isApprovePasswordlessLoginsEnabled = true,
|
||||
)
|
||||
assertEquals(
|
||||
true,
|
||||
settingsRepository.isApprovePasswordlessLoginsEnabled,
|
||||
)
|
||||
|
||||
// Updates to the repository value change the disk source
|
||||
settingsRepository.isApprovePasswordlessLoginsEnabled = false
|
||||
assertEquals(
|
||||
false,
|
||||
fakeSettingsDiskSource.getApprovePasswordlessLoginsEnabled(userId = USER_ID),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isScreenCaptureAllowed property should update SettingsDiskSource and emit changes`() =
|
||||
runTest {
|
||||
|
|
|
@ -25,7 +25,6 @@ import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
|||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricsManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.FakePermissionManager
|
||||
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
|
@ -51,7 +50,6 @@ class AccountSecurityScreenTest : BaseComposeTest() {
|
|||
every { startActivity(any()) } just runs
|
||||
every { startApplicationDetailsSettingsActivity() } just runs
|
||||
}
|
||||
private val permissionsManager = FakePermissionManager()
|
||||
private val captureBiometricsSuccess = slot<() -> Unit>()
|
||||
private val captureBiometricsCancel = slot<() -> Unit>()
|
||||
private val captureBiometricsLockOut = slot<() -> Unit>()
|
||||
|
@ -84,7 +82,6 @@ class AccountSecurityScreenTest : BaseComposeTest() {
|
|||
viewModel = viewModel,
|
||||
biometricsManager = biometricsManager,
|
||||
intentManager = intentManager,
|
||||
permissionsManager = permissionsManager,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -95,211 +92,6 @@ class AccountSecurityScreenTest : BaseComposeTest() {
|
|||
verify { viewModel.trySendAction(AccountSecurityAction.LogoutClick) }
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on approve login requests toggle on should send PendingEnabled action and display dialog`() {
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Use this device to approve login requests made from other devices")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Approve login requests")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
composeTestRule
|
||||
.onAllNodesWithText(
|
||||
"Use this device to approve login requests made from other devices",
|
||||
)
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
composeTestRule
|
||||
.onAllNodesWithText("No")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Yes")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.PendingEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on approve login requests toggle off should send Disabled action and hide requests row`() {
|
||||
composeTestRule
|
||||
.onNodeWithText("Pending login requests")
|
||||
.assertDoesNotExist()
|
||||
|
||||
mutableStateFlow.update { it.copy(isApproveLoginRequestsEnabled = true) }
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Use this device to approve login requests made from other devices")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
composeTestRule
|
||||
.onNodeWithText("Pending login requests")
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.Disabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on approve login requests confirm Yes should send Enabled action and hide dialog when permission already granted`() {
|
||||
permissionsManager.checkPermissionResult = true
|
||||
mutableStateFlow.update { it.copy(isApproveLoginRequestsEnabled = false) }
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Use this device to approve login requests made from other devices")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Yes")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.Enabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on approve login requests confirm Yes should send Enabled action and show permission dialog when permission not granted`() {
|
||||
permissionsManager.checkPermissionResult = false
|
||||
mutableStateFlow.update { it.copy(isApproveLoginRequestsEnabled = false) }
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Use this device to approve login requests made from other devices")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Yes")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Receive push notifications for new login requests")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
composeTestRule
|
||||
.onAllNodesWithText("No thanks")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Settings")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.Enabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on approve login requests confirm No should send Disabled action and hide dialog`() {
|
||||
mutableStateFlow.update { it.copy(isApproveLoginRequestsEnabled = false) }
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Use this device to approve login requests made from other devices")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("No")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.Disabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on approve login requests should be toggled on or off according to state`() {
|
||||
composeTestRule
|
||||
.onNodeWithText("Use this device to approve login requests made from other devices")
|
||||
.assertIsOff()
|
||||
mutableStateFlow.update { it.copy(isApproveLoginRequestsEnabled = true) }
|
||||
composeTestRule
|
||||
.onNodeWithText("Use this device to approve login requests made from other devices")
|
||||
.assertIsOn()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on push permission dialog No thanks should hide dialog`() {
|
||||
permissionsManager.checkPermissionResult = false
|
||||
mutableStateFlow.update { it.copy(isApproveLoginRequestsEnabled = false) }
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Use this device to approve login requests made from other devices")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Yes")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("No thanks")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on push permission dialog Settings should hide dialog and send confirm action`() {
|
||||
permissionsManager.checkPermissionResult = false
|
||||
mutableStateFlow.update { it.copy(isApproveLoginRequestsEnabled = false) }
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Use this device to approve login requests made from other devices")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Yes")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Settings")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(AccountSecurityAction.PushNotificationConfirm)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateToApplicationDataSettings should launch the correct intent`() {
|
||||
mutableEventFlow.tryEmit(AccountSecurityEvent.NavigateToApplicationDataSettings)
|
||||
|
@ -309,7 +101,6 @@ class AccountSecurityScreenTest : BaseComposeTest() {
|
|||
|
||||
@Test
|
||||
fun `on pending login requests click should send PendingLoginRequestsClick`() {
|
||||
mutableStateFlow.update { it.copy(isApproveLoginRequestsEnabled = true) }
|
||||
composeTestRule
|
||||
.onNodeWithText("Pending login requests")
|
||||
.performScrollTo()
|
||||
|
@ -1566,19 +1357,16 @@ class AccountSecurityScreenTest : BaseComposeTest() {
|
|||
|
||||
composeTestRule.onNodeWithText(rowText).assertDoesNotExist()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DEFAULT_STATE = AccountSecurityState(
|
||||
dialog = null,
|
||||
fingerprintPhrase = "fingerprint-placeholder".asText(),
|
||||
isApproveLoginRequestsEnabled = false,
|
||||
isUnlockWithBiometricsEnabled = false,
|
||||
isUnlockWithPasswordEnabled = true,
|
||||
isUnlockWithPinEnabled = false,
|
||||
vaultTimeout = VaultTimeout.ThirtyMinutes,
|
||||
vaultTimeoutAction = VaultTimeoutAction.LOCK,
|
||||
vaultTimeoutPolicyMinutes = null,
|
||||
vaultTimeoutPolicyAction = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val DEFAULT_STATE = AccountSecurityState(
|
||||
dialog = null,
|
||||
fingerprintPhrase = "fingerprint-placeholder".asText(),
|
||||
isUnlockWithBiometricsEnabled = false,
|
||||
isUnlockWithPasswordEnabled = true,
|
||||
isUnlockWithPinEnabled = false,
|
||||
vaultTimeout = VaultTimeout.ThirtyMinutes,
|
||||
vaultTimeoutAction = VaultTimeoutAction.LOCK,
|
||||
vaultTimeoutPolicyMinutes = null,
|
||||
vaultTimeoutPolicyAction = null,
|
||||
)
|
||||
|
|
|
@ -35,8 +35,6 @@ import kotlinx.serialization.json.Json
|
|||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AccountSecurityViewModelTest : BaseViewModelTest() {
|
||||
|
@ -49,7 +47,6 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
|
|||
private val vaultRepository: VaultRepository = mockk(relaxed = true)
|
||||
private val settingsRepository: SettingsRepository = mockk {
|
||||
every { isUnlockWithBiometricsEnabled } returns false
|
||||
every { isApprovePasswordlessLoginsEnabled } returns false
|
||||
every { isUnlockWithPinEnabled } returns false
|
||||
every { vaultTimeout } returns VaultTimeout.ThirtyMinutes
|
||||
every { vaultTimeoutAction } returns VaultTimeoutAction.LOCK
|
||||
|
@ -530,54 +527,6 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
|
|||
assertEquals(DEFAULT_STATE.copy(dialog = null), viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on ApprovePasswordlessLoginsToggle enabled should update settings and set isApprovePasswordlessLoginsEnabled to true`() =
|
||||
runTest {
|
||||
every { settingsRepository.isApprovePasswordlessLoginsEnabled = true } just runs
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.Enabled,
|
||||
)
|
||||
expectNoEvents()
|
||||
verify(exactly = 1) { settingsRepository.isApprovePasswordlessLoginsEnabled = true }
|
||||
}
|
||||
assertTrue(viewModel.stateFlow.value.isApproveLoginRequestsEnabled)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on ApprovePasswordlessLoginsToggle pending enabled should set isApprovePasswordlessLoginsEnabled to true`() =
|
||||
runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.PendingEnabled,
|
||||
)
|
||||
expectNoEvents()
|
||||
}
|
||||
assertTrue(viewModel.stateFlow.value.isApproveLoginRequestsEnabled)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on ApprovePasswordlessLoginsToggle disabled should update settings and set isApprovePasswordlessLoginsEnabled to false`() =
|
||||
runTest {
|
||||
every { settingsRepository.isApprovePasswordlessLoginsEnabled = false } just runs
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(
|
||||
AccountSecurityAction.ApprovePasswordlessLoginsToggle.Disabled,
|
||||
)
|
||||
expectNoEvents()
|
||||
verify(exactly = 1) {
|
||||
settingsRepository.isApprovePasswordlessLoginsEnabled = false
|
||||
}
|
||||
}
|
||||
assertFalse(viewModel.stateFlow.value.isApproveLoginRequestsEnabled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on PushNotificationConfirm should send NavigateToApplicationDataSettings event`() =
|
||||
runTest {
|
||||
|
@ -616,7 +565,6 @@ private const val FINGERPRINT: String = "fingerprint"
|
|||
private val DEFAULT_STATE: AccountSecurityState = AccountSecurityState(
|
||||
dialog = null,
|
||||
fingerprintPhrase = FINGERPRINT.asText(),
|
||||
isApproveLoginRequestsEnabled = false,
|
||||
isUnlockWithBiometricsEnabled = false,
|
||||
isUnlockWithPasswordEnabled = true,
|
||||
isUnlockWithPinEnabled = false,
|
||||
|
|
|
@ -25,6 +25,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.asText
|
|||
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
|
||||
import com.x8bit.bitwarden.ui.platform.manager.exit.ExitManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.FakePermissionManager
|
||||
import com.x8bit.bitwarden.ui.util.assertLockOrLogoutDialogIsDisplayed
|
||||
import com.x8bit.bitwarden.ui.util.assertLogoutConfirmationDialogIsDisplayed
|
||||
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
|
||||
|
@ -68,6 +69,7 @@ class VaultScreenTest : BaseComposeTest() {
|
|||
private var onNavigateToSearchScreen = false
|
||||
private val exitManager = mockk<ExitManager>(relaxed = true)
|
||||
private val intentManager = mockk<IntentManager>(relaxed = true)
|
||||
private val permissionsManager = FakePermissionManager()
|
||||
|
||||
private val mutableEventFlow = bufferedMutableSharedFlow<VaultEvent>()
|
||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
||||
|
@ -90,6 +92,7 @@ class VaultScreenTest : BaseComposeTest() {
|
|||
onNavigateToSearchVault = { onNavigateToSearchScreen = true },
|
||||
exitManager = exitManager,
|
||||
intentManager = intentManager,
|
||||
permissionsManager = permissionsManager,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue