mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
BIT-1648: Add the copy totp code overflow option (#850)
This commit is contained in:
parent
0c6ea8d18d
commit
20dd839923
13 changed files with 283 additions and 3 deletions
|
@ -59,6 +59,7 @@ fun SearchContent(
|
|||
is ListingItemOverflowAction.VaultAction.CopyNoteClick,
|
||||
is ListingItemOverflowAction.VaultAction.CopyNumberClick,
|
||||
is ListingItemOverflowAction.VaultAction.CopyPasswordClick,
|
||||
is ListingItemOverflowAction.VaultAction.CopyTotpClick,
|
||||
is ListingItemOverflowAction.VaultAction.CopySecurityCodeClick,
|
||||
is ListingItemOverflowAction.VaultAction.CopyUsernameClick,
|
||||
is ListingItemOverflowAction.VaultAction.EditClick,
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl
|
|||
import com.x8bit.bitwarden.data.platform.repository.util.baseWebSendUrl
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
|
@ -190,6 +191,10 @@ class SearchViewModel @Inject constructor(
|
|||
is ListingItemOverflowAction.VaultAction.ViewClick -> {
|
||||
handleViewCipherClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopyTotpClick -> {
|
||||
handleCopyTotpClick(overflowAction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,6 +216,15 @@ class SearchViewModel @Inject constructor(
|
|||
sendEvent(SearchEvent.NavigateToEditSend(action.sendId))
|
||||
}
|
||||
|
||||
private fun handleCopyTotpClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopyTotpClick,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val result = vaultRepo.generateTotp(action.totpCode, clock.instant())
|
||||
sendAction(SearchAction.Internal.GenerateTotpResultReceive(result))
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRemovePasswordClick(
|
||||
action: ListingItemOverflowAction.SendAction.RemovePasswordClick,
|
||||
) {
|
||||
|
@ -283,6 +297,10 @@ class SearchViewModel @Inject constructor(
|
|||
handleDeleteSendResultReceive(action)
|
||||
}
|
||||
|
||||
is SearchAction.Internal.GenerateTotpResultReceive -> {
|
||||
handleGenerateTotpResultReceive(action)
|
||||
}
|
||||
|
||||
is SearchAction.Internal.RemovePasswordSendResultReceive -> {
|
||||
handleRemovePasswordSendResultReceive(action)
|
||||
}
|
||||
|
@ -320,6 +338,17 @@ class SearchViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleGenerateTotpResultReceive(
|
||||
action: SearchAction.Internal.GenerateTotpResultReceive,
|
||||
) {
|
||||
when (val result = action.result) {
|
||||
is GenerateTotpResult.Error -> Unit
|
||||
is GenerateTotpResult.Success -> {
|
||||
clipboardManager.setText(result.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRemovePasswordSendResultReceive(
|
||||
action: SearchAction.Internal.RemovePasswordSendResultReceive,
|
||||
) {
|
||||
|
@ -758,6 +787,13 @@ sealed class SearchAction {
|
|||
val result: DeleteSendResult,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a result for generating a verification code has been received.
|
||||
*/
|
||||
data class GenerateTotpResultReceive(
|
||||
val result: GenerateTotpResult,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a result for removing the password protection from a send has been received.
|
||||
*/
|
||||
|
|
|
@ -66,6 +66,7 @@ fun VaultItemListingContent(
|
|||
is ListingItemOverflowAction.VaultAction.EditClick,
|
||||
is ListingItemOverflowAction.VaultAction.LaunchClick,
|
||||
is ListingItemOverflowAction.VaultAction.ViewClick,
|
||||
is ListingItemOverflowAction.VaultAction.CopyTotpClick,
|
||||
null,
|
||||
-> Unit
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.x8bit.bitwarden.data.platform.repository.util.baseWebSendUrl
|
|||
import com.x8bit.bitwarden.data.platform.repository.util.map
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
|
@ -256,6 +257,15 @@ class VaultItemListingViewModel @Inject constructor(
|
|||
clipboardManager.setText(action.securityCode)
|
||||
}
|
||||
|
||||
private fun handleCopyTotpClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopyTotpClick,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val result = vaultRepository.generateTotp(action.totpCode, clock.instant())
|
||||
sendAction(VaultItemListingsAction.Internal.GenerateTotpResultReceive(result))
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyUsernameClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopyUsernameClick,
|
||||
) {
|
||||
|
@ -347,6 +357,10 @@ class VaultItemListingViewModel @Inject constructor(
|
|||
handleCopySecurityCodeClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopyTotpClick -> {
|
||||
handleCopyTotpClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopyUsernameClick -> {
|
||||
handleCopyUsernameClick(overflowAction)
|
||||
}
|
||||
|
@ -383,6 +397,10 @@ class VaultItemListingViewModel @Inject constructor(
|
|||
is VaultItemListingsAction.Internal.IconLoadingSettingReceive -> {
|
||||
handleIconsSettingReceived(action)
|
||||
}
|
||||
|
||||
is VaultItemListingsAction.Internal.GenerateTotpResultReceive -> {
|
||||
handleGenerateTotpResultReceive(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -445,6 +463,17 @@ class VaultItemListingViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleGenerateTotpResultReceive(
|
||||
action: VaultItemListingsAction.Internal.GenerateTotpResultReceive,
|
||||
) {
|
||||
when (val result = action.result) {
|
||||
is GenerateTotpResult.Error -> Unit
|
||||
is GenerateTotpResult.Success -> {
|
||||
clipboardManager.setText(result.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleVaultDataReceive(
|
||||
action: VaultItemListingsAction.Internal.VaultDataReceive,
|
||||
) {
|
||||
|
@ -1033,6 +1062,13 @@ sealed class VaultItemListingsAction {
|
|||
*/
|
||||
data class DeleteSendResultReceive(val result: DeleteSendResult) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a result for generating a verification code has been received.
|
||||
*/
|
||||
data class GenerateTotpResultReceive(
|
||||
val result: GenerateTotpResult,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a result for removing the password protection from a send has been received.
|
||||
*/
|
||||
|
|
|
@ -97,6 +97,14 @@ sealed class ListingItemOverflowAction : Parcelable {
|
|||
override val title: Text get() = R.string.copy_password.asText()
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on the copy TOTP code overflow option.
|
||||
*/
|
||||
@Parcelize
|
||||
data class CopyTotpClick(val totpCode: String) : VaultAction() {
|
||||
override val title: Text get() = R.string.copy_totp.asText()
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on the copy number overflow option.
|
||||
*/
|
||||
|
|
|
@ -23,6 +23,9 @@ fun CipherView.toOverflowActions(): List<ListingItemOverflowAction.VaultAction>
|
|||
this.login?.password?.let {
|
||||
ListingItemOverflowAction.VaultAction.CopyPasswordClick(password = it)
|
||||
},
|
||||
this.login?.totp
|
||||
?.let { ListingItemOverflowAction.VaultAction.CopyTotpClick(totpCode = it) }
|
||||
.takeIf { this.type == CipherType.LOGIN },
|
||||
this.card?.number?.let {
|
||||
ListingItemOverflowAction.VaultAction.CopyNumberClick(number = it)
|
||||
},
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
|||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
|
@ -37,7 +38,9 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.time.Clock
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
@ -46,10 +49,11 @@ import javax.inject.Inject
|
|||
@Suppress("TooManyFunctions")
|
||||
@HiltViewModel
|
||||
class VaultViewModel @Inject constructor(
|
||||
private val clipboardManager: BitwardenClipboardManager,
|
||||
private val authRepository: AuthRepository,
|
||||
private val vaultRepository: VaultRepository,
|
||||
private val clipboardManager: BitwardenClipboardManager,
|
||||
private val clock: Clock,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val vaultRepository: VaultRepository,
|
||||
) : BaseViewModel<VaultState, VaultEvent, VaultAction>(
|
||||
initialState = run {
|
||||
val userState = requireNotNull(authRepository.userStateFlow.value)
|
||||
|
@ -293,6 +297,10 @@ class VaultViewModel @Inject constructor(
|
|||
handleCopySecurityCodeClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopyTotpClick -> {
|
||||
handleCopyTotpClick(overflowAction)
|
||||
}
|
||||
|
||||
is ListingItemOverflowAction.VaultAction.CopyUsernameClick -> {
|
||||
handleCopyUsernameClick(overflowAction)
|
||||
}
|
||||
|
@ -333,6 +341,15 @@ class VaultViewModel @Inject constructor(
|
|||
clipboardManager.setText(action.securityCode)
|
||||
}
|
||||
|
||||
private fun handleCopyTotpClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopyTotpClick,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val result = vaultRepository.generateTotp(action.totpCode, clock.instant())
|
||||
sendAction(VaultAction.Internal.GenerateTotpResultReceive(result))
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyUsernameClick(
|
||||
action: ListingItemOverflowAction.VaultAction.CopyUsernameClick,
|
||||
) {
|
||||
|
@ -353,6 +370,10 @@ class VaultViewModel @Inject constructor(
|
|||
|
||||
private fun handleInternalAction(action: VaultAction.Internal) {
|
||||
when (action) {
|
||||
is VaultAction.Internal.GenerateTotpResultReceive -> {
|
||||
handleGenerateTotpResultReceive(action)
|
||||
}
|
||||
|
||||
is VaultAction.Internal.PullToRefreshEnableReceive -> {
|
||||
handlePullToRefreshEnableReceive(action)
|
||||
}
|
||||
|
@ -365,6 +386,17 @@ class VaultViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleGenerateTotpResultReceive(
|
||||
action: VaultAction.Internal.GenerateTotpResultReceive,
|
||||
) {
|
||||
when (val result = action.result) {
|
||||
is GenerateTotpResult.Error -> Unit
|
||||
is GenerateTotpResult.Success -> {
|
||||
clipboardManager.setText(result.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePullToRefreshEnableReceive(
|
||||
action: VaultAction.Internal.PullToRefreshEnableReceive,
|
||||
) {
|
||||
|
@ -1002,6 +1034,13 @@ sealed class VaultAction {
|
|||
val isIconLoadingDisabled: Boolean,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a result for generating a verification code has been received.
|
||||
*/
|
||||
data class GenerateTotpResultReceive(
|
||||
val result: GenerateTotpResult,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates that the pull to refresh feature toggle has changed.
|
||||
*/
|
||||
|
|
|
@ -18,6 +18,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
|||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
|
@ -331,6 +332,51 @@ class SearchViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `OverflowOptionClick Vault CopyTotpClick with GenerateTotpCode success should call setText on the ClipboardManager`() =
|
||||
runTest {
|
||||
val totpCode = "totpCode"
|
||||
val code = "Code"
|
||||
|
||||
coEvery {
|
||||
vaultRepository.generateTotp(totpCode, clock.instant())
|
||||
} returns GenerateTotpResult.Success(code, 30)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(
|
||||
SearchAction.OverflowOptionClick(
|
||||
ListingItemOverflowAction.VaultAction.CopyTotpClick(totpCode),
|
||||
),
|
||||
)
|
||||
|
||||
verify(exactly = 1) {
|
||||
clipboardManager.setText(code)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `OverflowOptionClick Vault CopyTotpClick with GenerateTotpCode failure should not call setText on the ClipboardManager`() =
|
||||
runTest {
|
||||
val totpCode = "totpCode"
|
||||
|
||||
coEvery {
|
||||
vaultRepository.generateTotp(totpCode, clock.instant())
|
||||
} returns GenerateTotpResult.Error
|
||||
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(
|
||||
SearchAction.OverflowOptionClick(
|
||||
ListingItemOverflowAction.VaultAction.CopyTotpClick(totpCode),
|
||||
),
|
||||
)
|
||||
|
||||
verify(exactly = 0) {
|
||||
clipboardManager.setText(text = any<String>())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `OverflowOptionClick Vault CopyPasswordClick should call setText on the ClipboardManager`() =
|
||||
|
|
|
@ -45,6 +45,9 @@ fun createMockDisplayItemForCipher(
|
|||
ListingItemOverflowAction.VaultAction.CopyPasswordClick(
|
||||
password = "mockPassword-$number",
|
||||
),
|
||||
ListingItemOverflowAction.VaultAction.CopyTotpClick(
|
||||
totpCode = "mockTotp-$number",
|
||||
),
|
||||
ListingItemOverflowAction.VaultAction.LaunchClick(
|
||||
url = "www.mockuri$number.com",
|
||||
),
|
||||
|
|
|
@ -25,6 +25,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
|||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
|
@ -511,6 +512,51 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `OverflowOptionClick Vault CopyTotpClick with GenerateTotpCode success should call setText on the ClipboardManager`() =
|
||||
runTest {
|
||||
val totpCode = "totpCode"
|
||||
val code = "Code"
|
||||
|
||||
coEvery {
|
||||
vaultRepository.generateTotp(totpCode, clock.instant())
|
||||
} returns GenerateTotpResult.Success(code, 30)
|
||||
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
viewModel.trySendAction(
|
||||
VaultItemListingsAction.OverflowOptionClick(
|
||||
ListingItemOverflowAction.VaultAction.CopyTotpClick(totpCode),
|
||||
),
|
||||
)
|
||||
|
||||
verify(exactly = 1) {
|
||||
clipboardManager.setText(code)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `OverflowOptionClick Vault CopyTotpClick with GenerateTotpCode failure should not call setText on the ClipboardManager`() =
|
||||
runTest {
|
||||
val totpCode = "totpCode"
|
||||
|
||||
coEvery {
|
||||
vaultRepository.generateTotp(totpCode, clock.instant())
|
||||
} returns GenerateTotpResult.Error
|
||||
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
viewModel.trySendAction(
|
||||
VaultItemListingsAction.OverflowOptionClick(
|
||||
ListingItemOverflowAction.VaultAction.CopyTotpClick(totpCode),
|
||||
),
|
||||
)
|
||||
|
||||
verify(exactly = 0) {
|
||||
clipboardManager.setText(text = any<String>())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `OverflowOptionClick Vault CopyUsernameClick should call setText on the ClipboardManager`() =
|
||||
|
|
|
@ -46,6 +46,9 @@ fun createMockDisplayItemForCipher(
|
|||
ListingItemOverflowAction.VaultAction.CopyPasswordClick(
|
||||
password = "mockPassword-$number",
|
||||
),
|
||||
ListingItemOverflowAction.VaultAction.CopyTotpClick(
|
||||
totpCode = "mockTotp-$number",
|
||||
),
|
||||
ListingItemOverflowAction.VaultAction.LaunchClick(
|
||||
url = "www.mockuri$number.com",
|
||||
),
|
||||
|
|
|
@ -20,6 +20,7 @@ class CipherViewExtensionsTest {
|
|||
val id = "mockId-1"
|
||||
val username = "Bitwarden"
|
||||
val password = "password"
|
||||
val totpCode = "mockTotp-1"
|
||||
val uri = "www.test.com"
|
||||
val cipher = createMockCipherView(number = 1, cipherType = CipherType.LOGIN).copy(
|
||||
id = id,
|
||||
|
@ -38,6 +39,7 @@ class CipherViewExtensionsTest {
|
|||
ListingItemOverflowAction.VaultAction.EditClick(cipherId = id),
|
||||
ListingItemOverflowAction.VaultAction.CopyUsernameClick(username = username),
|
||||
ListingItemOverflowAction.VaultAction.CopyPasswordClick(password = password),
|
||||
ListingItemOverflowAction.VaultAction.CopyTotpClick(totpCode = totpCode),
|
||||
ListingItemOverflowAction.VaultAction.LaunchClick(url = uri),
|
||||
),
|
||||
result,
|
||||
|
@ -58,6 +60,7 @@ class CipherViewExtensionsTest {
|
|||
username = null,
|
||||
password = null,
|
||||
uris = null,
|
||||
totp = null,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCollectionV
|
|||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
|
@ -25,6 +26,7 @@ import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterData
|
|||
import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.util.toViewState
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultItemListingType
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
|
@ -36,9 +38,16 @@ 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
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
|
||||
@Suppress("LargeClass")
|
||||
class VaultViewModelTest : BaseViewModelTest() {
|
||||
private val clock: Clock = Clock.fixed(
|
||||
Instant.parse("2023-10-27T12:00:00Z"),
|
||||
ZoneOffset.UTC,
|
||||
)
|
||||
|
||||
private val clipboardManager: BitwardenClipboardManager = mockk {
|
||||
every { setText(any<String>()) } just runs
|
||||
|
@ -1108,6 +1117,51 @@ class VaultViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `OverflowOptionClick Vault CopyTotpClick with GenerateTotpCode success should call setText on the ClipboardManager`() =
|
||||
runTest {
|
||||
val totpCode = "totpCode"
|
||||
val code = "Code"
|
||||
|
||||
coEvery {
|
||||
vaultRepository.generateTotp(totpCode, clock.instant())
|
||||
} returns GenerateTotpResult.Success(code, 30)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(
|
||||
VaultAction.OverflowOptionClick(
|
||||
ListingItemOverflowAction.VaultAction.CopyTotpClick(totpCode),
|
||||
),
|
||||
)
|
||||
|
||||
verify(exactly = 1) {
|
||||
clipboardManager.setText(code)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `OverflowOptionClick Vault CopyTotpClick with GenerateTotpCode failure should not call setText on the ClipboardManager`() =
|
||||
runTest {
|
||||
val totpCode = "totpCode"
|
||||
|
||||
coEvery {
|
||||
vaultRepository.generateTotp(totpCode, clock.instant())
|
||||
} returns GenerateTotpResult.Error
|
||||
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(
|
||||
VaultAction.OverflowOptionClick(
|
||||
ListingItemOverflowAction.VaultAction.CopyTotpClick(totpCode),
|
||||
),
|
||||
)
|
||||
|
||||
verify(exactly = 0) {
|
||||
clipboardManager.setText(text = any<String>())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `OverflowOptionClick Vault CopySecurityCodeClick should call setText on the ClipboardManager`() =
|
||||
|
@ -1188,8 +1242,9 @@ class VaultViewModelTest : BaseViewModelTest() {
|
|||
|
||||
private fun createViewModel(): VaultViewModel =
|
||||
VaultViewModel(
|
||||
clipboardManager = clipboardManager,
|
||||
authRepository = authRepository,
|
||||
clipboardManager = clipboardManager,
|
||||
clock = clock,
|
||||
settingsRepository = settingsRepository,
|
||||
vaultRepository = vaultRepository,
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue