Provide IntentManager from compositionLocal (#630)

This commit is contained in:
David Perez authored and Álison Fernandes committed 2024-06-20 17:08:07 +01:00
1 parent 7bf249c0dd
commit c52ae0ed2a
21 files changed
+227 -537

No files matched your search

@@ -1,6 +1,5 @@
package com.x8bit.bitwarden.ui.auth.feature.createaccount
import android.content.Intent
import android.net.Uri
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
@@ -17,6 +16,7 @@ import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performTextInput
import androidx.core.net.toUri
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.AcceptPoliciesToggle
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.CheckDataBreachesToggle
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.CloseClick
@@ -26,71 +26,64 @@ import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.Pas
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.PasswordInputChange
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.SubmitClick
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.components.BasicDialogState
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
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.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.update
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
class CreateAccountScreenTest : BaseComposeTest() {
@Test
fun `app bar submit click should send SubmitClick action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns emptyFlow()
every { trySendAction(SubmitClick) } returns Unit
}
private var onNavigateBackCalled = false
private var onNavigateToLoginCalled = false
private val intentManager = mockk<IntentManager>(relaxed = true) {
every { startCustomTabsActivity(any()) } just runs
every { startActivity(any()) } just runs
}
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
private val mutableEventFlow = bufferedMutableSharedFlow<CreateAccountEvent>()
private val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns mutableStateFlow
every { eventFlow } returns mutableEventFlow
every { trySendAction(any()) } just runs
}
@Before
fun setup() {
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
onNavigateBack = { onNavigateBackCalled = true },
onNavigateToLogin = { _, _ -> onNavigateToLoginCalled = true },
intentManager = intentManager,
viewModel = viewModel,
)
}
}
@Test
fun `app bar submit click should send SubmitClick action`() {
composeTestRule.onNodeWithText("Submit").performClick()
verify { viewModel.trySendAction(SubmitClick) }
}
@Test
fun `close click should send CloseClick action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns emptyFlow()
every { trySendAction(CloseClick) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
}
composeTestRule.onNodeWithContentDescription("Close").performClick()
verify { viewModel.trySendAction(CloseClick) }
}
@Test
fun `check data breaches click should send CheckDataBreachesToggle action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns emptyFlow()
every { trySendAction(CheckDataBreachesToggle(true)) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
}
composeTestRule
.onNodeWithText("Check known data breaches for this password")
.performScrollTo()
@@ -100,19 +93,6 @@ class CreateAccountScreenTest : BaseComposeTest() {
@Test
fun `accept policies should be toggled on or off according to the state`() {
val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns mutableStateFlow
every { eventFlow } returns emptyFlow()
every { trySendAction(AcceptPoliciesToggle(true)) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
}
composeTestRule
.onNodeWithText("By activating this switch you agree", substring = true)
.assertIsOff()
@@ -126,18 +106,6 @@ class CreateAccountScreenTest : BaseComposeTest() {
@Test
fun `accept policies click should send AcceptPoliciesToggle action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns emptyFlow()
every { trySendAction(AcceptPoliciesToggle(true)) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
}
composeTestRule
.onNodeWithText("By activating this switch you agree", substring = true)
.performScrollTo()
@@ -147,188 +115,61 @@ class CreateAccountScreenTest : BaseComposeTest() {
@Test
fun `NavigateBack event should invoke navigate back lambda`() {
var onNavigateBackCalled = false
val onNavigateBack = { onNavigateBackCalled = true }
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns flowOf(CreateAccountEvent.NavigateBack)
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = onNavigateBack,
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
}
mutableEventFlow.tryEmit(CreateAccountEvent.NavigateBack)
assertTrue(onNavigateBackCalled)
}
@Test
fun `NavigateToLogin event should invoke navigate login lambda`() {
var onNavigateToLoginCalled = false
val onNavigateToLogin = { _: String, _: String -> onNavigateToLoginCalled = true }
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns flowOf(
CreateAccountEvent.NavigateToLogin(
email = "",
captchaToken = "",
),
)
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = onNavigateToLogin,
viewModel = viewModel,
)
}
mutableEventFlow.tryEmit(CreateAccountEvent.NavigateToLogin(email = "", captchaToken = ""))
assertTrue(onNavigateToLoginCalled)
}
@Test
fun `NavigateToCaptcha event should invoke intent handler`() {
fun `NavigateToCaptcha event should invoke intent manager`() {
val mockUri = mockk<Uri>()
val intentHandler = mockk<IntentHandler>(relaxed = true) {
every { startCustomTabsActivity(mockUri) } returns Unit
}
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns flowOf(
CreateAccountEvent.NavigateToCaptcha(
uri = mockUri,
),
)
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
intentHandler = intentHandler,
)
}
mutableEventFlow.tryEmit(CreateAccountEvent.NavigateToCaptcha(uri = mockUri))
verify {
intentHandler.startCustomTabsActivity(mockUri)
intentManager.startCustomTabsActivity(mockUri)
}
}
@Test
fun `NavigateToPrivacyPolicy event should invoke intent handler`() {
val expectedIntent =
Intent(Intent.ACTION_VIEW, Uri.parse("https://bitwarden.com/privacy/"))
val intentHandler = mockk<IntentHandler>(relaxed = true) {
every { startActivity(expectedIntent) } returns Unit
}
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns flowOf(CreateAccountEvent.NavigateToPrivacyPolicy)
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
intentHandler = intentHandler,
)
}
fun `NavigateToPrivacyPolicy event should invoke intent manager`() {
mutableEventFlow.tryEmit(CreateAccountEvent.NavigateToPrivacyPolicy)
verify {
intentHandler.launchUri("https://bitwarden.com/privacy/".toUri())
intentManager.launchUri("https://bitwarden.com/privacy/".toUri())
}
}
@Test
fun `NavigateToTerms event should invoke intent handler`() {
val expectedIntent =
Intent(Intent.ACTION_VIEW, Uri.parse("https://bitwarden.com/terms/"))
val intentHandler = mockk<IntentHandler>(relaxed = true) {
every { startActivity(expectedIntent) } returns Unit
}
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns flowOf(CreateAccountEvent.NavigateToTerms)
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
intentHandler = intentHandler,
)
}
fun `NavigateToTerms event should invoke intent manager`() {
mutableEventFlow.tryEmit(CreateAccountEvent.NavigateToTerms)
verify {
intentHandler.launchUri("https://bitwarden.com/terms/".toUri())
intentManager.launchUri("https://bitwarden.com/terms/".toUri())
}
}
@Test
fun `email input change should send EmailInputChange action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns emptyFlow()
every { trySendAction(EmailInputChange("input")) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
}
composeTestRule.onNodeWithText("Email address").performTextInput(TEST_INPUT)
verify { viewModel.trySendAction(EmailInputChange(TEST_INPUT)) }
}
@Test
fun `password input change should send PasswordInputChange action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns emptyFlow()
every { trySendAction(PasswordInputChange("input")) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
}
composeTestRule.onNodeWithText("Master password").performTextInput(TEST_INPUT)
verify { viewModel.trySendAction(PasswordInputChange(TEST_INPUT)) }
}
@Test
fun `confirm password input change should send ConfirmPasswordInputChange action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns emptyFlow()
every { trySendAction(ConfirmPasswordInputChange("input")) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
}
composeTestRule.onNodeWithText("Re-type master password").performTextInput(TEST_INPUT)
verify { viewModel.trySendAction(ConfirmPasswordInputChange(TEST_INPUT)) }
}
@Test
fun `password hint input change should send PasswordHintChange action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns emptyFlow()
every { trySendAction(PasswordHintChange("input")) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
}
composeTestRule
.onNodeWithText("Master password hint (optional)")
.performTextInput(TEST_INPUT)
@@ -337,26 +178,15 @@ class CreateAccountScreenTest : BaseComposeTest() {
@Test
fun `clicking OK on the error dialog should send ErrorDialogDismiss action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(
DEFAULT_STATE.copy(
dialog = CreateAccountDialog.Error(
BasicDialogState.Shown(
title = "title".asText(),
message = "message".asText(),
),
mutableStateFlow.update {
it.copy(
dialog = CreateAccountDialog.Error(
BasicDialogState.Shown(
title = "title".asText(),
message = "message".asText(),
),
),
)
every { eventFlow } returns emptyFlow()
every { trySendAction(CreateAccountAction.ErrorDialogDismiss) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
}
composeTestRule
.onAllNodesWithText("Ok")
@@ -367,19 +197,8 @@ class CreateAccountScreenTest : BaseComposeTest() {
@Test
fun `clicking No on the HIBP dialog should send ErrorDialogDismiss action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(
DEFAULT_STATE.copy(dialog = CreateAccountDialog.HaveIBeenPwned),
)
every { eventFlow } returns emptyFlow()
every { trySendAction(CreateAccountAction.ErrorDialogDismiss) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
mutableStateFlow.update {
it.copy(dialog = CreateAccountDialog.HaveIBeenPwned)
}
composeTestRule
.onAllNodesWithText("No")
@@ -390,19 +209,8 @@ class CreateAccountScreenTest : BaseComposeTest() {
@Test
fun `clicking Yes on the HIBP dialog should send ContinueWithBreachedPasswordClick action`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(
DEFAULT_STATE.copy(dialog = CreateAccountDialog.HaveIBeenPwned),
)
every { eventFlow } returns emptyFlow()
every { trySendAction(CreateAccountAction.ErrorDialogDismiss) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
mutableStateFlow.update {
it.copy(dialog = CreateAccountDialog.HaveIBeenPwned)
}
composeTestRule
.onAllNodesWithText("Yes")
@@ -413,44 +221,21 @@ class CreateAccountScreenTest : BaseComposeTest() {
@Test
fun `when BasicDialogState is Shown should show dialog`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(
DEFAULT_STATE.copy(
dialog = CreateAccountDialog.Error(
BasicDialogState.Shown(
title = "title".asText(),
message = "message".asText(),
),
mutableStateFlow.update {
it.copy(
dialog = CreateAccountDialog.Error(
BasicDialogState.Shown(
title = "title".asText(),
message = "message".asText(),
),
),
)
every { eventFlow } returns emptyFlow()
every { trySendAction(CreateAccountAction.ErrorDialogDismiss) } returns Unit
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
}
composeTestRule.onNode(isDialog()).assertIsDisplayed()
}
@Test
fun `password strength should change as state changes`() {
val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns mutableStateFlow
every { eventFlow } returns emptyFlow()
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
}
mutableStateFlow.update {
DEFAULT_STATE.copy(passwordStrengthState = PasswordStrengthState.WEAK_1)
}
@@ -479,23 +264,10 @@ class CreateAccountScreenTest : BaseComposeTest() {
@Test
fun `toggling one password field visibility should toggle the other`() {
val viewModel = mockk<CreateAccountViewModel>(relaxed = true) {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns emptyFlow()
}
composeTestRule.setContent {
CreateAccountScreen(
onNavigateBack = {},
onNavigateToLogin = { _, _ -> },
viewModel = viewModel,
)
}
// should start with 2 Show buttons:
composeTestRule
.onAllNodesWithContentDescription("Show")
.assertCountEquals(2)
.get(0)
.assertCountEquals(2)[0]
.performClick()
// after clicking there should be no Show buttons:
@@ -506,8 +278,7 @@ class CreateAccountScreenTest : BaseComposeTest() {
// and there should be 2 hide buttons now, and we'll click the second one:
composeTestRule
.onAllNodesWithContentDescription("Hide")
.assertCountEquals(2)
.get(1)
.assertCountEquals(2)[1]
.performClick()
// then there should be two show buttons again
@@ -15,10 +15,10 @@ import androidx.compose.ui.test.performScrollTo
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.IntentHandler
import com.x8bit.bitwarden.ui.platform.components.BasicDialogState
import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
import com.x8bit.bitwarden.ui.util.assertLockOrLogoutDialogIsDisplayed
import com.x8bit.bitwarden.ui.util.assertLogoutConfirmationDialogIsDisplayed
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
@@ -41,7 +41,7 @@ import org.junit.Before
import org.junit.Test
class LoginScreenTest : BaseComposeTest() {
private val intentHandler = mockk<IntentHandler>(relaxed = true) {
private val intentManager = mockk<IntentManager>(relaxed = true) {
every { startCustomTabsActivity(any()) } returns Unit
}
private var onNavigateBackCalled = false
@@ -62,7 +62,7 @@ class LoginScreenTest : BaseComposeTest() {
onNavigateToEnterpriseSignOn = { onNavigateToEnterpriseSignOnCalled = true },
onNavigateToLoginWithDevice = { onNavigateToLoginWithDeviceCalled = true },
viewModel = viewModel,
intentHandler = intentHandler,
intentManager = intentManager,
)
}
}
@@ -262,10 +262,10 @@ class LoginScreenTest : BaseComposeTest() {
}
@Test
fun `NavigateToCaptcha should call intentHandler startCustomTabsActivity`() {
fun `NavigateToCaptcha should call intentManager startCustomTabsActivity`() {
val mockUri = mockk<Uri>()
mutableEventFlow.tryEmit(LoginEvent.NavigateToCaptcha(mockUri))
verify { intentHandler.startCustomTabsActivity(mockUri) }
verify { intentManager.startCustomTabsActivity(mockUri) }
}
@Test
@@ -11,43 +11,55 @@ 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.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 io.mockk.Runs
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
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.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
class AboutScreenTest : BaseComposeTest() {
private var haveCalledNavigateBack = false
private val mutableStateFlow = MutableStateFlow(
AboutState(
version = "Version: 1.0.0 (1)".asText(),
isSubmitCrashLogsEnabled = false,
),
)
private val mutableEventFlow = bufferedMutableSharedFlow<AboutEvent>()
val viewModel: AboutViewModel = mockk {
every { stateFlow } returns mutableStateFlow
every { eventFlow } returns mutableEventFlow
every { trySendAction(any()) } just runs
}
@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
}
private val intentManager: IntentManager = mockk {
every { launchUri(any()) } just runs
}
@Before
fun setup() {
composeTestRule.setContent {
AboutScreen(
viewModel = viewModel,
onNavigateBack = { },
intentManager = intentManager,
onNavigateBack = { haveCalledNavigateBack = true },
)
}
}
@Test
fun `on back click should send BackClick`() {
composeTestRule.onNodeWithContentDescription("Back").performClick()
verify { viewModel.trySendAction(AboutAction.BackClick) }
}
@@ -55,17 +67,6 @@ class AboutScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `on bitwarden help center click should display confirmation dialog and confirm click should emit 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.onNode(isDialog()).assertDoesNotExist()
composeTestRule.onNodeWithText("Bitwarden Help Center").performClick()
composeTestRule.onNode(isDialog()).assertExists()
@@ -82,17 +83,6 @@ class AboutScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `on bitwarden web vault click should display confirmation dialog and confirm click should emit 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.onNode(isDialog()).assertDoesNotExist()
composeTestRule.onNodeWithText("Bitwarden web vault").performClick()
composeTestRule.onNode(isDialog()).assertExists()
@@ -109,17 +99,6 @@ class AboutScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `on learn about organizations click should display confirmation dialog and confirm click should emit 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.onNode(isDialog()).assertDoesNotExist()
composeTestRule.onNodeWithText("Learn about organizations").performClick()
composeTestRule.onNode(isDialog()).assertExists()
@@ -135,97 +114,37 @@ class AboutScreenTest : BaseComposeTest() {
@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 {
AboutScreen(
viewModel = viewModel,
onNavigateBack = { haveCalledNavigateBack = true },
)
}
mutableEventFlow.tryEmit(AboutEvent.NavigateBack)
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,
)
}
fun `on NavigateToHelpCenter should call launchUri on IntentManager`() {
mutableEventFlow.tryEmit(AboutEvent.NavigateToHelpCenter)
verify {
intentHandler.launchUri("https://bitwarden.com/help".toUri())
intentManager.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,
)
}
fun `on NavigateToLearnAboutOrganizations should call launchUri on IntentManager`() {
mutableEventFlow.tryEmit(AboutEvent.NavigateToLearnAboutOrganizations)
verify {
intentHandler.launchUri("https://bitwarden.com/help/about-organizations".toUri())
intentManager.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,
)
}
fun `on NavigateToWebVault should call launchUri on IntentManager`() {
mutableEventFlow.tryEmit(AboutEvent.NavigateToWebVault)
verify {
intentHandler.launchUri("https://vault.bitwarden.com".toUri())
intentManager.launchUri("https://vault.bitwarden.com".toUri())
}
}
@Suppress("MaxLineLength")
@Test
fun `on rate the app click should display confirmation dialog and confirm click should emit 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.onNode(isDialog()).assertDoesNotExist()
composeTestRule.onNodeWithText("Rate the app").performClick()
composeTestRule.onNode(isDialog()).assertExists()
@@ -242,17 +161,6 @@ class AboutScreenTest : BaseComposeTest() {
@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))
@@ -260,16 +168,6 @@ class AboutScreenTest : BaseComposeTest() {
}
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()
@@ -277,17 +175,6 @@ class AboutScreenTest : BaseComposeTest() {
@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)
@@ -296,16 +183,6 @@ class AboutScreenTest : BaseComposeTest() {
@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()) }
@@ -21,8 +21,8 @@ import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
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.manager.intent.IntentManager
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
import io.mockk.every
import io.mockk.just
@@ -41,7 +41,7 @@ class AccountSecurityScreenTest : BaseComposeTest() {
private var onNavigateBackCalled = false
private var onNavigateToDeleteAccountCalled = false
private val intentHandler = mockk<IntentHandler> {
private val intentManager = mockk<IntentManager> {
every { launchUri(any()) } just runs
}
private val mutableEventFlow = bufferedMutableSharedFlow<AccountSecurityEvent>()
@@ -58,7 +58,7 @@ class AccountSecurityScreenTest : BaseComposeTest() {
onNavigateBack = { onNavigateBackCalled = true },
onNavigateToDeleteAccount = { onNavigateToDeleteAccountCalled = true },
viewModel = viewModel,
intentHandler = intentHandler,
intentManager = intentManager,
)
}
}
@@ -997,10 +997,10 @@ class AccountSecurityScreenTest : BaseComposeTest() {
}
@Test
fun `on NavigateToFingerprintPhrase should call launchUri on intentHandler`() {
fun `on NavigateToFingerprintPhrase should call launchUri on intentManager`() {
mutableEventFlow.tryEmit(AccountSecurityEvent.NavigateToFingerprintPhrase)
verify {
intentHandler.launchUri("http://bitwarden.com/help/fingerprint-phrase".toUri())
intentManager.launchUri("http://bitwarden.com/help/fingerprint-phrase".toUri())
}
}
@@ -20,8 +20,8 @@ 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.manager.intent.IntentManager
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
import com.x8bit.bitwarden.ui.util.isProgressBar
import io.mockk.every
@@ -41,7 +41,7 @@ class SendScreenTest : BaseComposeTest() {
private var onNavigateToNewSendCalled = false
private var onNavigateToEditSendId: String? = null
private val intentHandler = mockk<IntentHandler> {
private val intentManager = mockk<IntentManager> {
every { launchUri(any()) } just runs
every { shareText(any()) } just runs
}
@@ -59,7 +59,7 @@ class SendScreenTest : BaseComposeTest() {
viewModel = viewModel,
onNavigateToAddSend = { onNavigateToNewSendCalled = true },
onNavigateToEditSend = { onNavigateToEditSendId = it },
intentHandler = intentHandler,
intentManager = intentManager,
)
}
}
@@ -78,19 +78,19 @@ class SendScreenTest : BaseComposeTest() {
}
@Test
fun `on NavigateToAboutSend should call launchUri on intentHandler`() {
fun `on NavigateToAboutSend should call launchUri on intentManager`() {
mutableEventFlow.tryEmit(SendEvent.NavigateToAboutSend)
verify {
intentHandler.launchUri("https://bitwarden.com/products/send".toUri())
intentManager.launchUri("https://bitwarden.com/products/send".toUri())
}
}
@Test
fun `on ShowShareSheet should call shareText on IntentHandler`() {
fun `on ShowShareSheet should call shareText on IntentManager`() {
val text = "sharable stuff"
mutableEventFlow.tryEmit(SendEvent.ShowShareSheet(text))
verify {
intentHandler.shareText(text)
intentManager.shareText(text)
}
}
@@ -22,8 +22,8 @@ import androidx.compose.ui.test.performScrollTo
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.IntentHandler
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.model.AddSendType
import com.x8bit.bitwarden.ui.util.isEditableText
import com.x8bit.bitwarden.ui.util.isProgressBar
@@ -44,7 +44,7 @@ class AddSendScreenTest : BaseComposeTest() {
private var onNavigateBackCalled = false
private val intentHandler: IntentHandler = mockk {
private val intentManager: IntentManager = mockk {
every { shareText(any()) } just runs
}
private val mutableEventFlow = bufferedMutableSharedFlow<AddSendEvent>()
@@ -59,7 +59,7 @@ class AddSendScreenTest : BaseComposeTest() {
composeTestRule.setContent {
AddSendScreen(
viewModel = viewModel,
intentHandler = intentHandler,
intentManager = intentManager,
onNavigateBack = { onNavigateBackCalled = true },
)
}
@@ -72,11 +72,11 @@ class AddSendScreenTest : BaseComposeTest() {
}
@Test
fun `on ShowShareSheet should call shareText on IntentHandler`() {
fun `on ShowShareSheet should call shareText on IntentManager`() {
val text = "sharable stuff"
mutableEventFlow.tryEmit(AddSendEvent.ShowShareSheet(text))
verify {
intentHandler.shareText(text)
intentManager.shareText(text)
}
}
@@ -25,8 +25,8 @@ import androidx.compose.ui.test.performTextInput
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.manager.intent.IntentManager
import com.x8bit.bitwarden.ui.util.assertScrollableNodeDoesNotExist
import com.x8bit.bitwarden.ui.util.isProgressBar
import com.x8bit.bitwarden.ui.util.onFirstNodeWithTextAfterScroll
@@ -53,7 +53,7 @@ class VaultItemScreenTest : BaseComposeTest() {
private var onNavigateToVaultEditItemId: String? = null
private var onNavigateToMoveToOrganizationItemId: String? = null
private val intentHandler = mockk<IntentHandler>()
private val intentManager = mockk<IntentManager>()
private val mutableEventFlow = bufferedMutableSharedFlow<VaultItemEvent>()
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
@@ -70,7 +70,7 @@ class VaultItemScreenTest : BaseComposeTest() {
onNavigateBack = { onNavigateBackCalled = true },
onNavigateToVaultAddEditItem = { onNavigateToVaultEditItemId = it },
onNavigateToMoveToOrganization = { onNavigateToMoveToOrganizationItemId = it },
intentHandler = intentHandler,
intentManager = intentManager,
)
}
}
@@ -109,12 +109,12 @@ class VaultItemScreenTest : BaseComposeTest() {
fun `NavigateToUri event should invoke launchUri`() {
val uriString = "http://www.example.com"
val uri = uriString.toUri()
every { intentHandler.launchUri(uri) } just runs
every { intentManager.launchUri(uri) } just runs
mutableEventFlow.tryEmit(VaultItemEvent.NavigateToUri(uriString))
verify(exactly = 1) {
intentHandler.launchUri(uri)
intentManager.launchUri(uri)
}
}
@@ -17,7 +17,7 @@ import androidx.compose.ui.test.performTextInput
import androidx.test.core.app.ApplicationProvider
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.manager.intent.IntentManager
import com.x8bit.bitwarden.ui.platform.manager.permissions.FakePermissionManager
import io.mockk.every
import io.mockk.mockk
@@ -40,7 +40,7 @@ class ManualCodeEntryScreenTests : BaseComposeTest() {
MutableStateFlow(ManualCodeEntryState(""))
private val fakePermissionManager: FakePermissionManager = FakePermissionManager()
private val intentHandler = mockk<IntentHandler>(relaxed = true)
private val intentManager = mockk<IntentManager>(relaxed = true)
private val viewModel = mockk<ManualCodeEntryViewModel>(relaxed = true) {
every { eventFlow } returns mutableEventFlow
@@ -57,7 +57,7 @@ class ManualCodeEntryScreenTests : BaseComposeTest() {
onNavigateToScanQrCodeCalled = true
},
permissionsManager = fakePermissionManager,
intentHandler = intentHandler,
intentManager = intentManager,
)
}
}
@@ -86,7 +86,7 @@ class ManualCodeEntryScreenTests : BaseComposeTest() {
)
val intentSlot = slot<Intent>()
verify { intentHandler.startActivity(capture(intentSlot)) }
verify { intentManager.startActivity(capture(intentSlot)) }
assertEquals(
uri,
@@ -18,9 +18,9 @@ import androidx.compose.ui.test.performScrollToNode
import com.x8bit.bitwarden.R
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.components.model.AccountSummary
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
import com.x8bit.bitwarden.ui.util.assertLockOrLogoutDialogIsDisplayed
import com.x8bit.bitwarden.ui.util.assertLogoutConfirmationDialogIsDisplayed
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
@@ -58,7 +58,7 @@ class VaultScreenTest : BaseComposeTest() {
private var onNavigateToVaultEditItemId: String? = null
private var onNavigateToVaultItemListingType: VaultItemListingType? = null
private var onDimBottomNavBarRequestCalled = false
private val intentHandler = mockk<IntentHandler>(relaxed = true)
private val intentManager = mockk<IntentManager>(relaxed = true)
private val mutableEventFlow = bufferedMutableSharedFlow<VaultEvent>()
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
@@ -77,7 +77,7 @@ class VaultScreenTest : BaseComposeTest() {
onNavigateToVaultEditItemScreen = { onNavigateToVaultEditItemId = it },
onNavigateToVaultItemListingScreen = { onNavigateToVaultItemListingType = it },
onDimBottomNavBarRequest = { onDimBottomNavBarRequestCalled = true },
intentHandler = intentHandler,
intentManager = intentManager,
)
}
}
@@ -618,9 +618,9 @@ class VaultScreenTest : BaseComposeTest() {
}
@Test
fun `NavigateOutOfApp event should call exitApplication on the IntentHandler`() {
fun `NavigateOutOfApp event should call exitApplication on the IntentManager`() {
mutableEventFlow.tryEmit(VaultEvent.NavigateOutOfApp)
verify { intentHandler.exitApplication() }
verify { intentManager.exitApplication() }
}
@Test