mirror of
https://github.com/bitwarden/android.git
synced 2026-10-23 11:33:22 -05:00
BIT-1518: Process shared sends (#698)
This commit is contained in:
1 parent
4510695f76
commit
c9d7a48598
14 files changed
+462
-60
No files matched your search
+42
-1
@@ -23,6 +23,7 @@ import androidx.compose.ui.test.performTextInput
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
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.tools.feature.send.addsend.model.AddSendType
|
||||
@@ -45,6 +46,9 @@ class AddSendScreenTest : BaseComposeTest() {
|
||||
|
||||
private var onNavigateBackCalled = false
|
||||
|
||||
private val exitManager: ExitManager = mockk(relaxed = true) {
|
||||
every { exitApplication() } just runs
|
||||
}
|
||||
private val permissionsManager = FakePermissionManager()
|
||||
private val intentManager: IntentManager = mockk(relaxed = true) {
|
||||
every { shareText(any()) } just runs
|
||||
@@ -61,6 +65,7 @@ class AddSendScreenTest : BaseComposeTest() {
|
||||
composeTestRule.setContent {
|
||||
AddSendScreen(
|
||||
viewModel = viewModel,
|
||||
exitManager = exitManager,
|
||||
intentManager = intentManager,
|
||||
permissionsManager = permissionsManager,
|
||||
onNavigateBack = { onNavigateBackCalled = true },
|
||||
@@ -74,6 +79,14 @@ class AddSendScreenTest : BaseComposeTest() {
|
||||
assert(onNavigateBackCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ExitApp should call exitApplication on ExitManager`() {
|
||||
mutableEventFlow.tryEmit(AddSendEvent.ExitApp)
|
||||
verify {
|
||||
exitManager.exitApplication()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on ShowShareSheet should call shareText on IntentManager`() {
|
||||
val text = "sharable stuff"
|
||||
@@ -91,6 +104,14 @@ class AddSendScreenTest : BaseComposeTest() {
|
||||
verify { viewModel.trySendAction(AddSendAction.CloseClick) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `display navigation icon according to state`() {
|
||||
mutableStateFlow.update { it.copy(isShared = false) }
|
||||
composeTestRule.onNodeWithContentDescription("Close").assertIsDisplayed()
|
||||
mutableStateFlow.update { it.copy(isShared = true) }
|
||||
composeTestRule.onNodeWithContentDescription("Close").assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on save click should send SaveClick`() {
|
||||
composeTestRule
|
||||
@@ -282,7 +303,25 @@ class AddSendScreenTest : BaseComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `segmented buttons should appear based on state`() {
|
||||
mutableStateFlow.update { it.copy(addSendType = AddSendType.AddItem) }
|
||||
mutableStateFlow.update { it.copy(isShared = true) }
|
||||
composeTestRule
|
||||
.onNodeWithText("Type")
|
||||
.assertDoesNotExist()
|
||||
composeTestRule
|
||||
.onAllNodesWithText("File")
|
||||
.filterToOne(!isEditableText)
|
||||
.assertDoesNotExist()
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Text")
|
||||
.filterToOne(!isEditableText)
|
||||
.assertDoesNotExist()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
isShared = false,
|
||||
addSendType = AddSendType.AddItem,
|
||||
)
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText("Type")
|
||||
.performScrollTo()
|
||||
@@ -889,6 +928,8 @@ class AddSendScreenTest : BaseComposeTest() {
|
||||
addSendType = AddSendType.AddItem,
|
||||
viewState = DEFAULT_VIEW_STATE,
|
||||
dialogState = null,
|
||||
shouldFinishOnComplete = false,
|
||||
isShared = false,
|
||||
isPremiumUser = false,
|
||||
baseWebSendUrl = "https://vault.bitwarden.com/#/send/",
|
||||
)
|
||||
|
||||
+76
-1
@@ -56,6 +56,8 @@ class AddSendViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
private val mutableUserStateFlow = MutableStateFlow<UserState?>(DEFAULT_USER_STATE)
|
||||
private val authRepository: AuthRepository = mockk {
|
||||
every { specialCircumstance } returns null
|
||||
every { specialCircumstance = null } just runs
|
||||
every { userStateFlow } returns mutableUserStateFlow
|
||||
}
|
||||
private val environmentRepository: EnvironmentRepository = mockk {
|
||||
@@ -108,8 +110,9 @@ class AddSendViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `SaveClick with createSend success should emit NavigateBack and ShowShareSheet`() =
|
||||
fun `SaveClick with createSend success should emit NavigateBack and ShowShareSheet when not an external shared`() =
|
||||
runTest {
|
||||
val viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(name = "input"),
|
||||
@@ -137,6 +140,76 @@ class AddSendViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `SaveClick with createSend success should copy the send URL to the clipboard and emit NavigateBack`() =
|
||||
runTest {
|
||||
val viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(name = "input"),
|
||||
)
|
||||
val initialState = DEFAULT_STATE.copy(
|
||||
shouldFinishOnComplete = false,
|
||||
isShared = true,
|
||||
viewState = viewState,
|
||||
)
|
||||
val mockSendView = mockk<SendView>()
|
||||
every { viewState.toSendView(clock) } returns mockSendView
|
||||
val sendUrl = "www.test.com/send/test"
|
||||
val resultSendView = mockk<SendView> {
|
||||
every { toSendUrl(DEFAULT_ENVIRONMENT_URL) } returns sendUrl
|
||||
}
|
||||
coEvery {
|
||||
vaultRepository.createSend(sendView = mockSendView, fileUri = null)
|
||||
} returns CreateSendResult.Success(sendView = resultSendView)
|
||||
val viewModel = createViewModel(initialState)
|
||||
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AddSendAction.SaveClick)
|
||||
assertEquals(AddSendEvent.NavigateBack, awaitItem())
|
||||
}
|
||||
assertEquals(initialState, viewModel.stateFlow.value)
|
||||
coVerify(exactly = 1) {
|
||||
vaultRepository.createSend(sendView = mockSendView, fileUri = null)
|
||||
authRepository.specialCircumstance = null
|
||||
clipboardManager.setText(sendUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `SaveClick with createSend success should copy the send URL to the clipboard and emit ExitApp`() =
|
||||
runTest {
|
||||
val viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(name = "input"),
|
||||
)
|
||||
val initialState = DEFAULT_STATE.copy(
|
||||
shouldFinishOnComplete = true,
|
||||
isShared = true,
|
||||
viewState = viewState,
|
||||
)
|
||||
val mockSendView = mockk<SendView>()
|
||||
every { viewState.toSendView(clock) } returns mockSendView
|
||||
val sendUrl = "www.test.com/send/test"
|
||||
val resultSendView = mockk<SendView> {
|
||||
every { toSendUrl(DEFAULT_ENVIRONMENT_URL) } returns sendUrl
|
||||
}
|
||||
coEvery {
|
||||
vaultRepository.createSend(sendView = mockSendView, fileUri = null)
|
||||
} returns CreateSendResult.Success(sendView = resultSendView)
|
||||
val viewModel = createViewModel(initialState)
|
||||
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AddSendAction.SaveClick)
|
||||
assertEquals(AddSendEvent.ExitApp, awaitItem())
|
||||
}
|
||||
assertEquals(initialState, viewModel.stateFlow.value)
|
||||
coVerify(exactly = 1) {
|
||||
vaultRepository.createSend(sendView = mockSendView, fileUri = null)
|
||||
authRepository.specialCircumstance = null
|
||||
clipboardManager.setText(sendUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `SaveClick with createSend failure should show error dialog`() = runTest {
|
||||
val viewState = DEFAULT_VIEW_STATE.copy(
|
||||
@@ -915,6 +988,8 @@ class AddSendViewModelTest : BaseViewModelTest() {
|
||||
addSendType = AddSendType.AddItem,
|
||||
viewState = DEFAULT_VIEW_STATE,
|
||||
dialogState = null,
|
||||
shouldFinishOnComplete = false,
|
||||
isShared = false,
|
||||
isPremiumUser = false,
|
||||
baseWebSendUrl = DEFAULT_ENVIRONMENT_URL,
|
||||
)
|
||||
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
package com.x8bit.bitwarden.ui.tools.feature.send.addsend.util
|
||||
|
||||
import android.net.Uri
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.AddSendState
|
||||
import io.mockk.mockk
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class SpecialCircumstanceExtensionsTest {
|
||||
|
||||
@Test
|
||||
fun `toSendType with TextSend should return Text SendType with correct text`() {
|
||||
val text = "Share Text"
|
||||
val expected = AddSendState.ViewState.Content.SendType.Text(
|
||||
input = text,
|
||||
isHideByDefaultChecked = false,
|
||||
)
|
||||
val specialCircumstance = UserState.SpecialCircumstance.ShareNewSend(
|
||||
data = IntentManager.ShareData.TextSend(
|
||||
subject = "",
|
||||
text = text,
|
||||
),
|
||||
shouldFinishWhenComplete = false,
|
||||
)
|
||||
|
||||
val result = specialCircumstance.toSendType()
|
||||
|
||||
assertEquals(expected, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSendType with FileSend should return File SendType with correct data`() {
|
||||
val uri = mockk<Uri>()
|
||||
val fileName = "Share Name"
|
||||
val sizeBytes = 100L
|
||||
val expected = AddSendState.ViewState.Content.SendType.File(
|
||||
uri = uri,
|
||||
name = fileName,
|
||||
sizeBytes = sizeBytes,
|
||||
displaySize = null,
|
||||
)
|
||||
val specialCircumstance = UserState.SpecialCircumstance.ShareNewSend(
|
||||
data = IntentManager.ShareData.FileSend(
|
||||
fileData = IntentManager.FileData(
|
||||
fileName = fileName,
|
||||
uri = uri,
|
||||
sizeBytes = sizeBytes,
|
||||
),
|
||||
),
|
||||
shouldFinishWhenComplete = false,
|
||||
)
|
||||
|
||||
val result = specialCircumstance.toSendType()
|
||||
|
||||
assertEquals(expected, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSendType with null SpecialCircumstance should return null`() {
|
||||
val specialCircumstance: UserState.SpecialCircumstance? = null
|
||||
assertNull(specialCircumstance.toSendType())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSendName with TextSend should return subject`() {
|
||||
val subject = "Subject"
|
||||
val specialCircumstance = UserState.SpecialCircumstance.ShareNewSend(
|
||||
data = IntentManager.ShareData.TextSend(
|
||||
subject = subject,
|
||||
text = "",
|
||||
),
|
||||
shouldFinishWhenComplete = false,
|
||||
)
|
||||
|
||||
val result = specialCircumstance.toSendName()
|
||||
|
||||
assertEquals(subject, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSendName with FileSend should return file name`() {
|
||||
val fileName = "File Name"
|
||||
val specialCircumstance = UserState.SpecialCircumstance.ShareNewSend(
|
||||
data = IntentManager.ShareData.FileSend(
|
||||
fileData = IntentManager.FileData(
|
||||
fileName = fileName,
|
||||
uri = mockk(),
|
||||
sizeBytes = 0L,
|
||||
),
|
||||
),
|
||||
shouldFinishWhenComplete = false,
|
||||
)
|
||||
|
||||
val result = specialCircumstance.toSendName()
|
||||
|
||||
assertEquals(fileName, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSendName with null SpecialCircumstance should return null`() {
|
||||
val specialCircumstance: UserState.SpecialCircumstance? = null
|
||||
assertNull(specialCircumstance.toSendName())
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `shouldFinishOnComplete with ShareNewSend shouldFinishWhenComplete true should return true`() {
|
||||
val specialCircumstance = UserState.SpecialCircumstance.ShareNewSend(
|
||||
data = mockk(),
|
||||
shouldFinishWhenComplete = true,
|
||||
)
|
||||
assertTrue(specialCircumstance.shouldFinishOnComplete())
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `shouldFinishOnComplete with ShareNewSend shouldFinishWhenComplete false should return false`() {
|
||||
val specialCircumstance = UserState.SpecialCircumstance.ShareNewSend(
|
||||
data = mockk(),
|
||||
shouldFinishWhenComplete = false,
|
||||
)
|
||||
assertFalse(specialCircumstance.shouldFinishOnComplete())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldFinishOnComplete with null SpecialCircumstance should return false`() {
|
||||
val specialCircumstance: UserState.SpecialCircumstance? = null
|
||||
assertFalse(specialCircumstance.shouldFinishOnComplete())
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFl
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
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.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.exit.ExitManager
|
||||
import com.x8bit.bitwarden.ui.util.assertLockOrLogoutDialogIsDisplayed
|
||||
import com.x8bit.bitwarden.ui.util.assertLogoutConfirmationDialogIsDisplayed
|
||||
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
|
||||
@@ -61,7 +61,7 @@ class VaultScreenTest : BaseComposeTest() {
|
||||
private var onNavigateToVaultItemListingType: VaultItemListingType? = null
|
||||
private var onDimBottomNavBarRequestCalled = false
|
||||
private var onNavigateToVerificationCodeScreen = false
|
||||
private val intentManager = mockk<IntentManager>(relaxed = true)
|
||||
private val exitManager = mockk<ExitManager>(relaxed = true)
|
||||
|
||||
private val mutableEventFlow = bufferedMutableSharedFlow<VaultEvent>()
|
||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
||||
@@ -81,7 +81,7 @@ class VaultScreenTest : BaseComposeTest() {
|
||||
onNavigateToVaultItemListingScreen = { onNavigateToVaultItemListingType = it },
|
||||
onDimBottomNavBarRequest = { onDimBottomNavBarRequestCalled = true },
|
||||
onNavigateToVerificationCodeScreen = { onNavigateToVerificationCodeScreen = true },
|
||||
intentManager = intentManager,
|
||||
exitManager = exitManager,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -646,9 +646,9 @@ class VaultScreenTest : BaseComposeTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `NavigateOutOfApp event should call exitApplication on the IntentManager`() {
|
||||
fun `NavigateOutOfApp event should call exitApplication on the ExitManager`() {
|
||||
mutableEventFlow.tryEmit(VaultEvent.NavigateOutOfApp)
|
||||
verify { intentManager.exitApplication() }
|
||||
verify { exitManager.exitApplication() }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in new issue
Block a user