mirror of
https://github.com/bitwarden/android.git
synced 2026-06-02 02:36:58 -05:00
BIT-931: Fill out about UI (#178)
This commit is contained in:
@@ -1,21 +1,42 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.about
|
||||
|
||||
import androidx.compose.ui.platform.ClipboardManager
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.assertIsOff
|
||||
import androidx.compose.ui.test.assertIsOn
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.core.net.toUri
|
||||
import com.x8bit.bitwarden.data.platform.util.toAnnotatedString
|
||||
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 io.mockk.Runs
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class AboutScreenTest : BaseComposeTest() {
|
||||
private val mutableStateFlow = MutableStateFlow(
|
||||
AboutState(
|
||||
version = "Version: 1.0.0 (1)".asText(),
|
||||
isSubmitCrashLogsEnabled = false,
|
||||
),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `on back click should send BackClick`() {
|
||||
val viewModel: AboutViewModel = mockk {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { trySendAction(AboutAction.BackClick) } returns Unit
|
||||
}
|
||||
@@ -30,9 +51,89 @@ class AboutScreenTest : BaseComposeTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateAbout should call onNavigateToAbout`() {
|
||||
fun `on bitwarden help center click should send HelpCenterClick`() {
|
||||
val viewModel: AboutViewModel = mockk {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { trySendAction(AboutAction.HelpCenterClick) } returns Unit
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("Bitwarden Help Center").performClick()
|
||||
verify {
|
||||
viewModel.trySendAction(AboutAction.HelpCenterClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on bitwarden web vault click should send WebVaultClick`() {
|
||||
val viewModel: AboutViewModel = mockk {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { trySendAction(AboutAction.WebVaultClick) } returns Unit
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("Bitwarden web vault").performClick()
|
||||
verify {
|
||||
viewModel.trySendAction(AboutAction.WebVaultClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on CopyToClipboard should call setText on ClipboardManager`() {
|
||||
val text = "copy text"
|
||||
val clipboardManager = mockk<ClipboardManager> {
|
||||
every { setText(any()) } just Runs
|
||||
}
|
||||
val viewModel = mockk<AboutViewModel> {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns flowOf(AboutEvent.CopyToClipboard(text.asText()))
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
clipboardManager = clipboardManager,
|
||||
)
|
||||
}
|
||||
verify {
|
||||
clipboardManager.setText(text.toAnnotatedString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on learn about organizations click should send LearnAboutOrganizationsClick`() {
|
||||
val viewModel: AboutViewModel = mockk {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { trySendAction(AboutAction.LearnAboutOrganizationsClick) } returns Unit
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("Learn about organizations").performClick()
|
||||
verify {
|
||||
viewModel.trySendAction(AboutAction.LearnAboutOrganizationsClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateBack should call onNavigateBack`() {
|
||||
var haveCalledNavigateBack = false
|
||||
val viewModel = mockk<AboutViewModel> {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns flowOf(AboutEvent.NavigateBack)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
@@ -43,4 +144,160 @@ class AboutScreenTest : BaseComposeTest() {
|
||||
}
|
||||
assertTrue(haveCalledNavigateBack)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateToHelpCenter should call launchUri on IntentHandler`() {
|
||||
val intentHandler = mockk<IntentHandler> {
|
||||
every { launchUri(any()) } just Runs
|
||||
}
|
||||
val viewModel = mockk<AboutViewModel> {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns flowOf(AboutEvent.NavigateToHelpCenter)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
intentHandler = intentHandler,
|
||||
)
|
||||
}
|
||||
verify {
|
||||
intentHandler.launchUri("https://bitwarden.com/help".toUri())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateToLearnAboutOrganizations should call launchUri on IntentHandler`() {
|
||||
val intentHandler = mockk<IntentHandler> {
|
||||
every { launchUri(any()) } just Runs
|
||||
}
|
||||
val viewModel = mockk<AboutViewModel> {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns flowOf(AboutEvent.NavigateToLearnAboutOrganizations)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
intentHandler = intentHandler,
|
||||
)
|
||||
}
|
||||
verify {
|
||||
intentHandler.launchUri("https://bitwarden.com/help/about-organizations".toUri())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateToWebVault should call launchUri on IntentHandler`() {
|
||||
val intentHandler = mockk<IntentHandler> {
|
||||
every { launchUri(any()) } just Runs
|
||||
}
|
||||
val viewModel = mockk<AboutViewModel> {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns flowOf(AboutEvent.NavigateToWebVault)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
intentHandler = intentHandler,
|
||||
)
|
||||
}
|
||||
verify {
|
||||
intentHandler.launchUri("https://vault.bitwarden.com".toUri())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on rate the app click should send RateAppClick`() {
|
||||
val viewModel: AboutViewModel = mockk {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { trySendAction(AboutAction.RateAppClick) } returns Unit
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("Rate the app").performClick()
|
||||
verify {
|
||||
viewModel.trySendAction(AboutAction.RateAppClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on submit crash logs toggle should send SubmitCrashLogsClick`() {
|
||||
val enabled = true
|
||||
val viewModel: AboutViewModel = mockk {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { trySendAction(AboutAction.SubmitCrashLogsClick(enabled)) } returns Unit
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("Submit crash logs").performClick()
|
||||
verify {
|
||||
viewModel.trySendAction(AboutAction.SubmitCrashLogsClick(enabled))
|
||||
}
|
||||
}
|
||||
|
||||
fun `on submit crash logs should be toggled on or off according to the state`() {
|
||||
val viewModel = mockk<AboutViewModel>(relaxed = true) {
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("Submit crash logs").assertIsOff()
|
||||
mutableStateFlow.update { it.copy(isSubmitCrashLogsEnabled = true) }
|
||||
composeTestRule.onNodeWithText("Submit crash logs").assertIsOn()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on version info click should send VersionClick`() {
|
||||
val viewModel: AboutViewModel = mockk {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { trySendAction(AboutAction.VersionClick) } returns Unit
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("Version: 1.0.0 (1)").performClick()
|
||||
verify {
|
||||
viewModel.trySendAction(AboutAction.VersionClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `version should update according to the state`() = runTest {
|
||||
val viewModel = mockk<AboutViewModel> {
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("Version: 1.0.0 (1)").assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update { it.copy(version = "Version: 1.1.0 (2)".asText()) }
|
||||
|
||||
composeTestRule.onNodeWithText("Version: 1.1.0 (2)").assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,91 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.about
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import kotlinx.coroutines.test.runTest
|
||||
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 AboutViewModelTest : BaseViewModelTest() {
|
||||
|
||||
private val initialState = createAboutState()
|
||||
private val initialSavedStateHandle = createSavedStateHandleWithState(initialState)
|
||||
|
||||
@Test
|
||||
fun `on BackClick should emit NavigateBack`() = runTest {
|
||||
val viewModel = AboutViewModel()
|
||||
val viewModel = AboutViewModel(initialSavedStateHandle)
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AboutAction.BackClick)
|
||||
assertEquals(AboutEvent.NavigateBack, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on HelpCenterClick should emit NavigateToHelpCenter`() = runTest {
|
||||
val viewModel = AboutViewModel(initialSavedStateHandle)
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AboutAction.HelpCenterClick)
|
||||
assertEquals(AboutEvent.NavigateToHelpCenter, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on LearnAboutOrganizationsClick should emit NavigateToLearnAboutOrganizations`() =
|
||||
runTest {
|
||||
val viewModel = AboutViewModel(initialSavedStateHandle)
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AboutAction.LearnAboutOrganizationsClick)
|
||||
assertEquals(AboutEvent.NavigateToLearnAboutOrganizations, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on RateAppClick should emit ShowToast`() = runTest {
|
||||
val viewModel = AboutViewModel(initialSavedStateHandle)
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AboutAction.RateAppClick)
|
||||
assertEquals(AboutEvent.ShowToast("Navigate to rate the app.".asText()), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on SubmitCrashLogsClick should update isSubmitCrashLogsEnabled to true`() = runTest {
|
||||
val viewModel = AboutViewModel(initialSavedStateHandle)
|
||||
assertFalse(viewModel.stateFlow.value.isSubmitCrashLogsEnabled)
|
||||
viewModel.trySendAction(AboutAction.SubmitCrashLogsClick(true))
|
||||
assertTrue(viewModel.stateFlow.value.isSubmitCrashLogsEnabled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on VersionClick should emit CopyToClipboard`() = runTest {
|
||||
val viewModel = AboutViewModel(initialSavedStateHandle)
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AboutAction.VersionClick)
|
||||
assertEquals(AboutEvent.CopyToClipboard("0".asText()), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on WebVaultClick should emit NavigateToWebVault`() = runTest {
|
||||
val viewModel = AboutViewModel(initialSavedStateHandle)
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AboutAction.WebVaultClick)
|
||||
assertEquals(AboutEvent.NavigateToWebVault, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAboutState(): AboutState =
|
||||
AboutState(
|
||||
version = "0".asText(),
|
||||
isSubmitCrashLogsEnabled = false,
|
||||
)
|
||||
|
||||
private fun createSavedStateHandleWithState(state: AboutState) =
|
||||
SavedStateHandle().apply {
|
||||
set("state", state)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user