mirror of
https://github.com/bitwarden/android.git
synced 2026-05-30 07:44:15 -05:00
Populate the send screen with real data (#488)
This commit is contained in:
@@ -9,7 +9,10 @@ import java.time.ZonedDateTime
|
||||
/**
|
||||
* Create a mock [SendView] with a given [number].
|
||||
*/
|
||||
fun createMockSendView(number: Int): SendView =
|
||||
fun createMockSendView(
|
||||
number: Int,
|
||||
type: SendType = SendType.FILE,
|
||||
): SendView =
|
||||
SendView(
|
||||
id = "mockId-$number",
|
||||
accessId = "mockAccessId-$number",
|
||||
@@ -17,7 +20,7 @@ fun createMockSendView(number: Int): SendView =
|
||||
notes = "mockNotes-$number",
|
||||
key = "mockKey-$number",
|
||||
password = "mockPassword-$number",
|
||||
type = SendType.FILE,
|
||||
type = type,
|
||||
file = createMockFileView(number = number),
|
||||
text = createMockTextView(number = number),
|
||||
maxAccessCount = 1u,
|
||||
|
||||
@@ -1,24 +1,39 @@
|
||||
package com.x8bit.bitwarden.ui.tools.feature.send
|
||||
|
||||
import androidx.compose.ui.platform.ClipboardManager
|
||||
import androidx.compose.ui.test.assert
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.assertTextEquals
|
||||
import androidx.compose.ui.test.filterToOne
|
||||
import androidx.compose.ui.test.hasAnyAncestor
|
||||
import androidx.compose.ui.test.hasClickAction
|
||||
import androidx.compose.ui.test.hasScrollToNodeAction
|
||||
import androidx.compose.ui.test.hasText
|
||||
import androidx.compose.ui.test.isDialog
|
||||
import androidx.compose.ui.test.isDisplayed
|
||||
import androidx.compose.ui.test.isPopup
|
||||
import androidx.compose.ui.test.onAllNodesWithText
|
||||
import androidx.compose.ui.test.onChildren
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performScrollToNode
|
||||
import androidx.core.net.toUri
|
||||
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.IntentHandler
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toAnnotatedString
|
||||
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
|
||||
import com.x8bit.bitwarden.ui.util.isProgressBar
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
@@ -26,7 +41,12 @@ class SendScreenTest : BaseComposeTest() {
|
||||
|
||||
private var onNavigateToNewSendCalled = false
|
||||
|
||||
private val intentHandler = mockk<IntentHandler>()
|
||||
private val clipboardManager = mockk<ClipboardManager> {
|
||||
every { setText(any()) } just runs
|
||||
}
|
||||
private val intentHandler = mockk<IntentHandler> {
|
||||
every { launchUri(any()) } just runs
|
||||
}
|
||||
private val mutableEventFlow = bufferedMutableSharedFlow<SendEvent>()
|
||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
||||
private val viewModel = mockk<SendViewModel>(relaxed = true) {
|
||||
@@ -39,12 +59,36 @@ class SendScreenTest : BaseComposeTest() {
|
||||
composeTestRule.setContent {
|
||||
SendScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateAddSend = { onNavigateToNewSendCalled = true },
|
||||
onNavigateToAddSend = { onNavigateToNewSendCalled = true },
|
||||
clipboardManager = clipboardManager,
|
||||
intentHandler = intentHandler,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on CopyToClipboard should call setText on the clipboardManager`() {
|
||||
val text = "copy text"
|
||||
mutableEventFlow.tryEmit(SendEvent.CopyToClipboard(text.asText()))
|
||||
verify {
|
||||
clipboardManager.setText(text.toAnnotatedString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateToNewSend should call onNavigateToNewSend`() {
|
||||
mutableEventFlow.tryEmit(SendEvent.NavigateNewSend)
|
||||
assertTrue(onNavigateToNewSendCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateToAboutSend should call launchUri on intentHandler`() {
|
||||
mutableEventFlow.tryEmit(SendEvent.NavigateToAboutSend)
|
||||
verify {
|
||||
intentHandler.launchUri("https://bitwarden.com/products/send".toUri())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on overflow item click should display menu`() {
|
||||
composeTestRule
|
||||
@@ -131,7 +175,7 @@ class SendScreenTest : BaseComposeTest() {
|
||||
composeTestRule.onNodeWithContentDescription("Add item").assertDoesNotExist()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(viewState = SendState.ViewState.Content)
|
||||
it.copy(viewState = DEFAULT_CONTENT_VIEW_STATE)
|
||||
}
|
||||
composeTestRule.onNodeWithContentDescription("Add item").assertIsDisplayed()
|
||||
}
|
||||
@@ -166,12 +210,6 @@ class SendScreenTest : BaseComposeTest() {
|
||||
verify { viewModel.trySendAction(SendAction.SearchClick) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateToNewSend should call onNavigateToNewSend`() {
|
||||
mutableEventFlow.tryEmit(SendEvent.NavigateNewSend)
|
||||
assert(onNavigateToNewSendCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `progressbar should be displayed according to state`() {
|
||||
mutableStateFlow.update {
|
||||
@@ -190,7 +228,7 @@ class SendScreenTest : BaseComposeTest() {
|
||||
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(viewState = SendState.ViewState.Content)
|
||||
it.copy(viewState = DEFAULT_CONTENT_VIEW_STATE)
|
||||
}
|
||||
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
|
||||
}
|
||||
@@ -216,8 +254,265 @@ class SendScreenTest : BaseComposeTest() {
|
||||
viewModel.trySendAction(SendAction.RefreshClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `text type count should be updated according to state`() {
|
||||
val rowText = "Text"
|
||||
mutableStateFlow.update {
|
||||
it.copy(viewState = DEFAULT_CONTENT_VIEW_STATE)
|
||||
}
|
||||
composeTestRule.onNode(hasScrollToNodeAction()).performScrollToNode(hasText(rowText))
|
||||
composeTestRule
|
||||
.onAllNodes(hasText(rowText))
|
||||
.filterToOne(hasClickAction())
|
||||
.assertTextEquals(rowText, 1.toString())
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(viewState = DEFAULT_CONTENT_VIEW_STATE.copy(textTypeCount = 3))
|
||||
}
|
||||
composeTestRule.onNode(hasScrollToNodeAction()).performScrollToNode(hasText(rowText))
|
||||
composeTestRule
|
||||
.onAllNodes(hasText(rowText))
|
||||
.filterToOne(hasClickAction())
|
||||
.assertTextEquals(rowText, 3.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `text type row click should send TextTypeClick`() {
|
||||
val rowText = "Text"
|
||||
mutableStateFlow.update {
|
||||
it.copy(viewState = DEFAULT_CONTENT_VIEW_STATE)
|
||||
}
|
||||
composeTestRule.onNode(hasScrollToNodeAction()).performScrollToNode(hasText(rowText))
|
||||
composeTestRule
|
||||
.onAllNodes(hasText(rowText))
|
||||
.filterToOne(hasClickAction())
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(SendAction.TextTypeClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `file type count should be updated according to state`() {
|
||||
val rowText = "File"
|
||||
mutableStateFlow.update {
|
||||
it.copy(viewState = DEFAULT_CONTENT_VIEW_STATE)
|
||||
}
|
||||
composeTestRule.onNode(hasScrollToNodeAction()).performScrollToNode(hasText(rowText))
|
||||
composeTestRule
|
||||
.onAllNodes(hasText(rowText))
|
||||
.filterToOne(hasClickAction())
|
||||
.assertTextEquals(rowText, 1.toString())
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(viewState = DEFAULT_CONTENT_VIEW_STATE.copy(fileTypeCount = 3))
|
||||
}
|
||||
composeTestRule.onNode(hasScrollToNodeAction()).performScrollToNode(hasText(rowText))
|
||||
composeTestRule
|
||||
.onAllNodes(hasText(rowText))
|
||||
.filterToOne(hasClickAction())
|
||||
.assertTextEquals(rowText, 3.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `file type row click should send FileTypeClick`() {
|
||||
val rowText = "File"
|
||||
mutableStateFlow.update {
|
||||
it.copy(viewState = DEFAULT_CONTENT_VIEW_STATE)
|
||||
}
|
||||
composeTestRule.onNode(hasScrollToNodeAction()).performScrollToNode(hasText(rowText))
|
||||
composeTestRule
|
||||
.onAllNodes(hasText(rowText))
|
||||
.filterToOne(hasClickAction())
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(SendAction.FileTypeClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on send item click should send SendClick`() {
|
||||
val rowText = "mockName-1"
|
||||
mutableStateFlow.update {
|
||||
it.copy(viewState = DEFAULT_CONTENT_VIEW_STATE)
|
||||
}
|
||||
composeTestRule.onNode(hasScrollToNodeAction()).performScrollToNode(hasText(rowText))
|
||||
composeTestRule
|
||||
.onAllNodes(hasText(rowText))
|
||||
.filterToOne(hasClickAction())
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(SendAction.SendClick(DEFAULT_SEND_ITEM))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on send item overflow click should display dialog`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = SendState.ViewState.Content(
|
||||
textTypeCount = 0,
|
||||
fileTypeCount = 1,
|
||||
sendItems = listOf(DEFAULT_SEND_ITEM),
|
||||
),
|
||||
)
|
||||
}
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("Options")
|
||||
.assertIsDisplayed()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNode(isDialog())
|
||||
.onChildren()
|
||||
.filterToOne(hasText(DEFAULT_SEND_ITEM.name))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on send item overflow dialog edit click should send SendClick`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = SendState.ViewState.Content(
|
||||
textTypeCount = 0,
|
||||
fileTypeCount = 1,
|
||||
sendItems = listOf(DEFAULT_SEND_ITEM),
|
||||
),
|
||||
)
|
||||
}
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("Options")
|
||||
.assertIsDisplayed()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Edit")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(SendAction.SendClick(DEFAULT_SEND_ITEM))
|
||||
}
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on send item overflow dialog copy click should send CopyClick`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = SendState.ViewState.Content(
|
||||
textTypeCount = 0,
|
||||
fileTypeCount = 1,
|
||||
sendItems = listOf(DEFAULT_SEND_ITEM),
|
||||
),
|
||||
)
|
||||
}
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("Options")
|
||||
.assertIsDisplayed()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Copy link")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(SendAction.CopyClick(DEFAULT_SEND_ITEM))
|
||||
}
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on send item overflow dialog share link click should send ShareClick`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = SendState.ViewState.Content(
|
||||
textTypeCount = 0,
|
||||
fileTypeCount = 1,
|
||||
sendItems = listOf(DEFAULT_SEND_ITEM),
|
||||
),
|
||||
)
|
||||
}
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("Options")
|
||||
.assertIsDisplayed()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Share link")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(SendAction.ShareClick(DEFAULT_SEND_ITEM))
|
||||
}
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on send item overflow dialog cancel click should close the dialog`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = SendState.ViewState.Content(
|
||||
textTypeCount = 0,
|
||||
fileTypeCount = 1,
|
||||
sendItems = listOf(DEFAULT_SEND_ITEM),
|
||||
),
|
||||
)
|
||||
}
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("Options")
|
||||
.assertIsDisplayed()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Cancel")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
composeTestRule.assertNoDialogExists()
|
||||
}
|
||||
}
|
||||
|
||||
private val DEFAULT_STATE: SendState = SendState(
|
||||
viewState = SendState.ViewState.Loading,
|
||||
)
|
||||
|
||||
private val DEFAULT_SEND_ITEM: SendState.ViewState.Content.SendItem =
|
||||
SendState.ViewState.Content.SendItem(
|
||||
id = "mockId-1",
|
||||
name = "mockName-1",
|
||||
deletionDate = "1",
|
||||
type = SendState.ViewState.Content.SendItem.Type.FILE,
|
||||
)
|
||||
|
||||
private val DEFAULT_CONTENT_VIEW_STATE: SendState.ViewState.Content = SendState.ViewState.Content(
|
||||
textTypeCount = 1,
|
||||
fileTypeCount = 1,
|
||||
sendItems = listOf(
|
||||
DEFAULT_SEND_ITEM,
|
||||
SendState.ViewState.Content.SendItem(
|
||||
id = "mockId-2",
|
||||
name = "mockName-2",
|
||||
deletionDate = "1",
|
||||
type = SendState.ViewState.Content.SendItem.Type.TEXT,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -110,6 +110,54 @@ class SendViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `CopyClick should emit ShowToast`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
val sendItem = mockk<SendState.ViewState.Content.SendItem>()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(SendAction.CopyClick(sendItem))
|
||||
assertEquals(SendEvent.ShowToast("Not yet implemented".asText()), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `SendClick should emit ShowToast`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
val sendItem = mockk<SendState.ViewState.Content.SendItem>()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(SendAction.SendClick(sendItem))
|
||||
assertEquals(SendEvent.ShowToast("Not yet implemented".asText()), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ShareClick should emit ShowToast`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
val sendItem = mockk<SendState.ViewState.Content.SendItem>()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(SendAction.ShareClick(sendItem))
|
||||
assertEquals(SendEvent.ShowToast("Not yet implemented".asText()), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `FileTypeClick should emit ShowToast`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(SendAction.FileTypeClick)
|
||||
assertEquals(SendEvent.ShowToast("Not yet implemented".asText()), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `TextTypeClick should emit ShowToast`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(SendAction.TextTypeClick)
|
||||
assertEquals(SendEvent.ShowToast("Not yet implemented".asText()), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `VaultRepository SendData Error should update view state to Error`() {
|
||||
val viewModel = createViewModel()
|
||||
@@ -129,7 +177,7 @@ class SendViewModelTest : BaseViewModelTest() {
|
||||
@Test
|
||||
fun `VaultRepository SendData Loaded should update view state`() {
|
||||
val viewModel = createViewModel()
|
||||
val viewState = SendState.ViewState.Content
|
||||
val viewState = mockk<SendState.ViewState.Content>()
|
||||
val sendData = mockk<SendData> {
|
||||
every { toViewState() } returns viewState
|
||||
}
|
||||
@@ -172,7 +220,7 @@ class SendViewModelTest : BaseViewModelTest() {
|
||||
@Test
|
||||
fun `VaultRepository SendData Pending should update view state`() {
|
||||
val viewModel = createViewModel()
|
||||
val viewState = SendState.ViewState.Content
|
||||
val viewState = mockk<SendState.ViewState.Content>()
|
||||
val sendData = mockk<SendData> {
|
||||
every { toViewState() } returns viewState
|
||||
}
|
||||
|
||||
@@ -1,13 +1,29 @@
|
||||
package com.x8bit.bitwarden.ui.tools.feature.send.util
|
||||
|
||||
import com.bitwarden.core.SendType
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.SendState
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.util.TimeZone
|
||||
|
||||
class SendDataExtensionsTest {
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
// Setting the timezone so the tests pass consistently no matter the environment.
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"))
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
// Clearing the timezone after the test.
|
||||
TimeZone.setDefault(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toViewState should return Empty when SendData is empty`() {
|
||||
val sendData = SendData(emptyList())
|
||||
@@ -20,12 +36,33 @@ class SendDataExtensionsTest {
|
||||
@Test
|
||||
fun `toViewState should return Content when SendData is not empty`() {
|
||||
val list = listOf(
|
||||
createMockSendView(number = 1),
|
||||
createMockSendView(number = 1, type = SendType.FILE),
|
||||
createMockSendView(number = 2, type = SendType.TEXT),
|
||||
)
|
||||
val sendData = SendData(list)
|
||||
|
||||
val result = sendData.toViewState()
|
||||
|
||||
assertEquals(SendState.ViewState.Content, result)
|
||||
assertEquals(
|
||||
SendState.ViewState.Content(
|
||||
textTypeCount = 1,
|
||||
fileTypeCount = 1,
|
||||
sendItems = listOf(
|
||||
SendState.ViewState.Content.SendItem(
|
||||
id = "mockId-1",
|
||||
name = "mockName-1",
|
||||
deletionDate = "Oct 27, 2023, 12:00 PM",
|
||||
type = SendState.ViewState.Content.SendItem.Type.FILE,
|
||||
),
|
||||
SendState.ViewState.Content.SendItem(
|
||||
id = "mockId-2",
|
||||
name = "mockName-2",
|
||||
deletionDate = "Oct 27, 2023, 12:00 PM",
|
||||
type = SendState.ViewState.Content.SendItem.Type.TEXT,
|
||||
),
|
||||
),
|
||||
),
|
||||
result,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user