mirror of
https://github.com/bitwarden/android.git
synced 2026-05-31 17:56:51 -05:00
BIT-930: Add UI for Other screen (#477)
This commit is contained in:
@@ -1,46 +1,134 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.other
|
||||
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.filterToOne
|
||||
import androidx.compose.ui.test.hasAnyAncestor
|
||||
import androidx.compose.ui.test.isDialog
|
||||
import androidx.compose.ui.test.onAllNodesWithText
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class OtherScreenTest : BaseComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `on back click should send BackClick`() {
|
||||
val viewModel: OtherViewModel = mockk {
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { trySendAction(OtherAction.BackClick) } returns Unit
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
OtherScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithContentDescription("Back").performClick()
|
||||
verify { viewModel.trySendAction(OtherAction.BackClick) }
|
||||
private var haveCalledNavigateBack = false
|
||||
private val mutableEventFlow = bufferedMutableSharedFlow<OtherEvent>()
|
||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
||||
private val viewModel = mockk<OtherViewModel>(relaxed = true) {
|
||||
every { eventFlow } returns mutableEventFlow
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateOther should call onNavigateToOther`() {
|
||||
var haveCalledNavigateBack = false
|
||||
val viewModel = mockk<OtherViewModel> {
|
||||
every { eventFlow } returns flowOf(OtherEvent.NavigateBack)
|
||||
}
|
||||
@Before
|
||||
fun setup() {
|
||||
composeTestRule.setContent {
|
||||
OtherScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { haveCalledNavigateBack = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on allow screen capture confirm should send AllowScreenCaptureToggle`() {
|
||||
composeTestRule.onNodeWithText("Allow screen capture").performClick()
|
||||
composeTestRule.onNodeWithText("Yes").performClick()
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
verify { viewModel.trySendAction(OtherAction.AllowScreenCaptureToggle(true)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on allow screen capture cancel should dismiss dialog`() {
|
||||
composeTestRule.onNodeWithText("Allow screen capture").performClick()
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Cancel")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
composeTestRule.assertNoDialogExists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on allow screen capture row click should display confirm enable screen capture dialog`() {
|
||||
composeTestRule.onNodeWithText("Allow screen capture").performClick()
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Allow screen capture")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on allow sync toggle should send AllowSyncToggle`() {
|
||||
composeTestRule.onNodeWithText("Allow sync on refresh").performClick()
|
||||
verify { viewModel.trySendAction(OtherAction.AllowSyncToggle(true)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on back click should send BackClick`() {
|
||||
composeTestRule.onNodeWithContentDescription("Back").performClick()
|
||||
verify { viewModel.trySendAction(OtherAction.BackClick) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on clear clipboard row click should show show clipboard selection dialog`() {
|
||||
composeTestRule.onNodeWithText("Clear clipboard").performClick()
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Clear clipboard")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on clear clipboard dialog item click should send ClearClipboardFrequencyChange`() {
|
||||
composeTestRule.onNodeWithText("Clear clipboard").performClick()
|
||||
composeTestRule
|
||||
.onAllNodesWithText("10 seconds")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
OtherAction.ClearClipboardFrequencyChange(
|
||||
clearClipboardFrequency = OtherState.ClearClipboardFrequency.TEN_SECONDS,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on clear clipboard dialog cancel should dismiss dialog`() {
|
||||
composeTestRule.onNodeWithText("Clear clipboard").performClick()
|
||||
composeTestRule.onNodeWithText("Cancel").performClick()
|
||||
composeTestRule.assertNoDialogExists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on sync now button click should send SyncNowButtonClick`() {
|
||||
composeTestRule.onNodeWithText("Sync now").performClick()
|
||||
verify { viewModel.trySendAction(OtherAction.SyncNowButtonClick) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateBack should call onNavigateBack`() {
|
||||
mutableEventFlow.tryEmit(OtherEvent.NavigateBack)
|
||||
assertTrue(haveCalledNavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
private val DEFAULT_STATE = OtherState(
|
||||
allowScreenCapture = false,
|
||||
allowSyncOnRefresh = false,
|
||||
clearClipboardFrequency = OtherState.ClearClipboardFrequency.DEFAULT,
|
||||
lastSyncTime = "5/14/2023 4:52 PM",
|
||||
)
|
||||
|
||||
@@ -1,19 +1,131 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.other
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class OtherViewModelTest : BaseViewModelTest() {
|
||||
val vaultRepository = mockk<VaultRepository>()
|
||||
|
||||
@Test
|
||||
fun `initial state should be correct when not set`() {
|
||||
val viewModel = createViewModel(state = null)
|
||||
assertEquals(DEFAULT_STATE, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial state should be correct when set`() {
|
||||
val state = DEFAULT_STATE.copy(
|
||||
clearClipboardFrequency = OtherState.ClearClipboardFrequency.FIVE_MINUTES,
|
||||
)
|
||||
val viewModel = createViewModel(state = state)
|
||||
assertEquals(state, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on AllowScreenCaptureToggled should update value in state`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
expectNoEvents()
|
||||
}
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(
|
||||
DEFAULT_STATE,
|
||||
awaitItem(),
|
||||
)
|
||||
viewModel.trySendAction(OtherAction.AllowScreenCaptureToggle(true))
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(allowScreenCapture = true),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on AllowSyncToggled should update value in state`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
expectNoEvents()
|
||||
}
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(
|
||||
DEFAULT_STATE,
|
||||
awaitItem(),
|
||||
)
|
||||
viewModel.trySendAction(OtherAction.AllowSyncToggle(true))
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(allowSyncOnRefresh = true),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on BackClick should emit NavigateBack`() = runTest {
|
||||
val viewModel = OtherViewModel()
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(OtherAction.BackClick)
|
||||
assertEquals(OtherEvent.NavigateBack, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on ClearClipboardFrequencyChange should update state`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(
|
||||
DEFAULT_STATE,
|
||||
awaitItem(),
|
||||
)
|
||||
viewModel.trySendAction(
|
||||
OtherAction.ClearClipboardFrequencyChange(
|
||||
clearClipboardFrequency = OtherState.ClearClipboardFrequency.ONE_MINUTE,
|
||||
),
|
||||
)
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
clearClipboardFrequency = OtherState.ClearClipboardFrequency.ONE_MINUTE,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on SyncNowButtonClick should sync repo`() = runTest {
|
||||
every { vaultRepository.sync() } just runs
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(OtherAction.SyncNowButtonClick)
|
||||
expectNoEvents()
|
||||
}
|
||||
verify { vaultRepository.sync() }
|
||||
}
|
||||
|
||||
private fun createViewModel(
|
||||
state: OtherState? = null,
|
||||
) = OtherViewModel(
|
||||
vaultRepo = vaultRepository,
|
||||
savedStateHandle = SavedStateHandle().apply {
|
||||
set("state", state)
|
||||
},
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val DEFAULT_STATE = OtherState(
|
||||
allowScreenCapture = false,
|
||||
allowSyncOnRefresh = false,
|
||||
clearClipboardFrequency = OtherState.ClearClipboardFrequency.DEFAULT,
|
||||
lastSyncTime = "5/14/2023 4:52 PM",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user