summaryrefslogtreecommitdiffhomepage
path: root/android/app/src/androidTest
diff options
context:
space:
mode:
authorJonatan Rhodin <jonatan.rhodin@mullvad.net>2023-11-16 11:08:21 +0100
committerJonatan Rhodin <jonatan.rhodin@mullvad.net>2023-11-16 11:08:21 +0100
commit04c6609948a770aed30491c66c4c6d779bc71c92 (patch)
tree4184b4e7a8c8ebfe8c4273e9f472779ee8f7bae9 /android/app/src/androidTest
parent67710f3e2ef57ecbe60c1bcb444ab047f11f79a5 (diff)
parentc8c896bc00b6b23ffaaf3a8708f03efb3ab3a0f2 (diff)
downloadmullvadvpn-04c6609948a770aed30491c66c4c6d779bc71c92.tar.xz
mullvadvpn-04c6609948a770aed30491c66c4c6d779bc71c92.zip
Merge branch 'google-play-in-app-purchases-droid-277'
Diffstat (limited to 'android/app/src/androidTest')
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt234
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt240
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt301
3 files changed, 764 insertions, 11 deletions
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt
index aec6c85595..e997ae29e4 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt
@@ -1,21 +1,33 @@
package net.mullvad.mullvadvpn.compose.screen
+import android.app.Activity
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import io.mockk.MockKAnnotations
+import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import net.mullvad.mullvadvpn.compose.setContentWithTheme
+import net.mullvad.mullvadvpn.compose.state.PaymentState
+import net.mullvad.mullvadvpn.compose.test.PLAY_PAYMENT_INFO_ICON_TEST_TAG
+import net.mullvad.mullvadvpn.lib.payment.model.PaymentProduct
+import net.mullvad.mullvadvpn.lib.payment.model.PaymentStatus
+import net.mullvad.mullvadvpn.lib.payment.model.ProductId
+import net.mullvad.mullvadvpn.lib.payment.model.ProductPrice
+import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult
+import net.mullvad.mullvadvpn.util.toPaymentDialogData
import net.mullvad.mullvadvpn.viewmodel.AccountUiState
import net.mullvad.mullvadvpn.viewmodel.AccountViewModel
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+@OptIn(ExperimentalMaterial3Api::class)
class AccountScreenTest {
@get:Rule val composeTestRule = createComposeRule()
@@ -24,12 +36,12 @@ class AccountScreenTest {
MockKAnnotations.init(this)
}
- @OptIn(ExperimentalMaterial3Api::class)
@Test
fun testDefaultState() {
// Arrange
composeTestRule.setContentWithTheme {
AccountScreen(
+ showSitePayment = true,
uiState =
AccountUiState(
deviceName = DUMMY_DEVICE_NAME,
@@ -48,13 +60,13 @@ class AccountScreenTest {
}
}
- @OptIn(ExperimentalMaterial3Api::class)
@Test
fun testManageAccountClick() {
// Arrange
val mockedClickHandler: () -> Unit = mockk(relaxed = true)
composeTestRule.setContentWithTheme {
AccountScreen(
+ showSitePayment = true,
uiState =
AccountUiState(
deviceName = DUMMY_DEVICE_NAME,
@@ -74,13 +86,13 @@ class AccountScreenTest {
verify { mockedClickHandler.invoke() }
}
- @OptIn(ExperimentalMaterial3Api::class)
@Test
fun testRedeemVoucherClick() {
// Arrange
val mockedClickHandler: () -> Unit = mockk(relaxed = true)
composeTestRule.setContentWithTheme {
AccountScreen(
+ showSitePayment = true,
uiState =
AccountUiState(
deviceName = DUMMY_DEVICE_NAME,
@@ -100,13 +112,13 @@ class AccountScreenTest {
verify { mockedClickHandler.invoke() }
}
- @OptIn(ExperimentalMaterial3Api::class)
@Test
fun testLogoutClick() {
// Arrange
val mockedClickHandler: () -> Unit = mockk(relaxed = true)
composeTestRule.setContentWithTheme {
AccountScreen(
+ showSitePayment = true,
uiState =
AccountUiState(
deviceName = DUMMY_DEVICE_NAME,
@@ -126,6 +138,220 @@ class AccountScreenTest {
verify { mockedClickHandler.invoke() }
}
+ @Test
+ fun testShowPurchaseCompleteDialog() {
+ // Arrange
+ composeTestRule.setContentWithTheme {
+ AccountScreen(
+ showSitePayment = true,
+ uiState =
+ AccountUiState.default()
+ .copy(
+ paymentDialogData =
+ PurchaseResult.Completed.Success.toPaymentDialogData()
+ ),
+ uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(),
+ enterTransitionEndAction = MutableSharedFlow<Unit>().asSharedFlow()
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Time was successfully added").assertExists()
+ }
+
+ @Test
+ fun testShowVerificationErrorDialog() {
+ // Arrange
+ composeTestRule.setContentWithTheme {
+ AccountScreen(
+ showSitePayment = true,
+ uiState =
+ AccountUiState.default()
+ .copy(
+ paymentDialogData =
+ PurchaseResult.Error.VerificationError(null).toPaymentDialogData()
+ ),
+ uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(),
+ enterTransitionEndAction = MutableSharedFlow<Unit>().asSharedFlow()
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Verifying purchase").assertExists()
+ }
+
+ @Test
+ fun testShowFetchProductsErrorDialog() {
+ // Arrange
+ composeTestRule.setContentWithTheme {
+ AccountScreen(
+ showSitePayment = true,
+ uiState =
+ AccountUiState.default()
+ .copy(
+ paymentDialogData =
+ PurchaseResult.Error.FetchProductsError(ProductId(""), null)
+ .toPaymentDialogData()
+ ),
+ uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(),
+ enterTransitionEndAction = MutableSharedFlow<Unit>().asSharedFlow()
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Google Play unavailable").assertExists()
+ }
+
+ @Test
+ fun testShowBillingErrorPaymentButton() {
+ // Arrange
+ composeTestRule.setContentWithTheme {
+ AccountScreen(
+ showSitePayment = true,
+ uiState =
+ AccountUiState.default().copy(billingPaymentState = PaymentState.Error.Billing),
+ uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(),
+ enterTransitionEndAction = MutableSharedFlow<Unit>().asSharedFlow()
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Add 30 days time").assertExists()
+ }
+
+ @Test
+ fun testShowBillingPaymentAvailable() {
+ // Arrange
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.status } returns null
+ composeTestRule.setContentWithTheme {
+ AccountScreen(
+ showSitePayment = true,
+ uiState =
+ AccountUiState.default()
+ .copy(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(),
+ enterTransitionEndAction = MutableSharedFlow<Unit>().asSharedFlow()
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Add 30 days time ($10)").assertExists()
+ }
+
+ @Test
+ fun testShowPendingPayment() {
+ // Arrange
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.status } returns PaymentStatus.PENDING
+ composeTestRule.setContentWithTheme {
+ AccountScreen(
+ showSitePayment = true,
+ uiState =
+ AccountUiState.default()
+ .copy(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(),
+ enterTransitionEndAction = MutableSharedFlow<Unit>().asSharedFlow()
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Google Play payment pending").assertExists()
+ }
+
+ @Test
+ fun testShowPendingPaymentInfoDialog() {
+ // Arrange
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.status } returns PaymentStatus.PENDING
+ composeTestRule.setContentWithTheme {
+ AccountScreen(
+ showSitePayment = true,
+ uiState =
+ AccountUiState.default()
+ .copy(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(),
+ enterTransitionEndAction = MutableSharedFlow<Unit>().asSharedFlow()
+ )
+ }
+
+ // Act
+ composeTestRule.onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).performClick()
+
+ // Assert
+ composeTestRule
+ .onNodeWithText(
+ "We are currently verifying your purchase, this might take some time. Your time will be added if the verification is successful."
+ )
+ .assertExists()
+ }
+
+ @Test
+ fun testShowVerificationInProgress() {
+ // Arrange
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.status } returns PaymentStatus.VERIFICATION_IN_PROGRESS
+ composeTestRule.setContentWithTheme {
+ AccountScreen(
+ showSitePayment = true,
+ uiState =
+ AccountUiState.default()
+ .copy(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(),
+ enterTransitionEndAction = MutableSharedFlow<Unit>().asSharedFlow()
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Verifying purchase").assertExists()
+ }
+
+ @Test
+ fun testOnPurchaseBillingProductClick() {
+ // Arrange
+ val clickHandler: (ProductId, () -> Activity) -> Unit = mockk(relaxed = true)
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.productId } returns ProductId("PRODUCT_ID")
+ every { mockPaymentProduct.status } returns null
+ composeTestRule.setContentWithTheme {
+ AccountScreen(
+ showSitePayment = true,
+ uiState =
+ AccountUiState.default()
+ .copy(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ onPurchaseBillingProductClick = clickHandler,
+ uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(),
+ enterTransitionEndAction = MutableSharedFlow<Unit>().asSharedFlow()
+ )
+ }
+
+ // Act
+ composeTestRule.onNodeWithText("Add 30 days time ($10)").performClick()
+
+ // Assert
+ verify { clickHandler.invoke(ProductId("PRODUCT_ID"), any()) }
+ }
+
companion object {
private const val DUMMY_DEVICE_NAME = "fake_name"
private const val DUMMY_ACCOUNT_NUMBER = "fake_number"
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt
index b0198316e3..28e2519c81 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt
@@ -1,16 +1,28 @@
package net.mullvad.mullvadvpn.compose.screen
+import android.app.Activity
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import io.mockk.MockKAnnotations
+import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import net.mullvad.mullvadvpn.compose.setContentWithTheme
import net.mullvad.mullvadvpn.compose.state.OutOfTimeUiState
+import net.mullvad.mullvadvpn.compose.state.PaymentState
+import net.mullvad.mullvadvpn.compose.test.PLAY_PAYMENT_INFO_ICON_TEST_TAG
+import net.mullvad.mullvadvpn.lib.payment.model.PaymentProduct
+import net.mullvad.mullvadvpn.lib.payment.model.PaymentStatus
+import net.mullvad.mullvadvpn.lib.payment.model.ProductId
+import net.mullvad.mullvadvpn.lib.payment.model.ProductPrice
+import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult
import net.mullvad.mullvadvpn.model.TunnelState
+import net.mullvad.mullvadvpn.util.toPaymentDialogData
import net.mullvad.mullvadvpn.viewmodel.OutOfTimeViewModel
import org.junit.Before
import org.junit.Rule
@@ -174,4 +186,232 @@ class OutOfTimeScreenTest {
// Assert
verify(exactly = 1) { mockClickListener.invoke() }
}
+
+ @Test
+ fun testShowPurchaseCompleteDialog() {
+ // Arrange
+ composeTestRule.setContentWithTheme {
+ OutOfTimeScreen(
+ showSitePayment = true,
+ uiState =
+ OutOfTimeUiState(
+ paymentDialogData = PurchaseResult.Completed.Success.toPaymentDialogData()
+ ),
+ uiSideEffect = MutableStateFlow(OutOfTimeViewModel.UiSideEffect.OpenConnectScreen),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> }
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Time was successfully added").assertExists()
+ }
+
+ @Test
+ fun testShowVerificationErrorDialog() {
+ // Arrange
+ composeTestRule.setContentWithTheme {
+ OutOfTimeScreen(
+ showSitePayment = true,
+ uiState =
+ OutOfTimeUiState(
+ paymentDialogData =
+ PurchaseResult.Error.VerificationError(null).toPaymentDialogData()
+ ),
+ uiSideEffect = MutableStateFlow(OutOfTimeViewModel.UiSideEffect.OpenConnectScreen),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> }
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Verifying purchase").assertExists()
+ }
+
+ @Test
+ fun testShowFetchProductsErrorDialog() {
+ // Arrange
+ composeTestRule.setContentWithTheme {
+ OutOfTimeScreen(
+ showSitePayment = true,
+ uiState =
+ OutOfTimeUiState()
+ .copy(
+ paymentDialogData =
+ PurchaseResult.Error.FetchProductsError(ProductId(""), null)
+ .toPaymentDialogData()
+ ),
+ uiSideEffect = MutableSharedFlow<OutOfTimeViewModel.UiSideEffect>().asSharedFlow()
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Google Play unavailable").assertExists()
+ }
+
+ @Test
+ fun testShowBillingErrorPaymentButton() {
+ // Arrange
+ composeTestRule.setContentWithTheme {
+ OutOfTimeScreen(
+ showSitePayment = true,
+ uiState = OutOfTimeUiState().copy(billingPaymentState = PaymentState.Error.Billing),
+ uiSideEffect = MutableSharedFlow<OutOfTimeViewModel.UiSideEffect>().asSharedFlow(),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> }
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Add 30 days time").assertExists()
+ }
+
+ @Test
+ fun testShowBillingPaymentAvailable() {
+ // Arrange
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.status } returns null
+ composeTestRule.setContentWithTheme {
+ OutOfTimeScreen(
+ showSitePayment = true,
+ uiState =
+ OutOfTimeUiState(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableStateFlow(OutOfTimeViewModel.UiSideEffect.OpenConnectScreen),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> }
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Add 30 days time ($10)").assertExists()
+ }
+
+ @Test
+ fun testShowPendingPayment() {
+ // Arrange
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.status } returns PaymentStatus.PENDING
+ composeTestRule.setContentWithTheme {
+ OutOfTimeScreen(
+ showSitePayment = true,
+ uiState =
+ OutOfTimeUiState()
+ .copy(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableSharedFlow<OutOfTimeViewModel.UiSideEffect>().asSharedFlow()
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Google Play payment pending").assertExists()
+ }
+
+ @Test
+ fun testShowPendingPaymentInfoDialog() {
+ // Arrange
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.status } returns PaymentStatus.PENDING
+ composeTestRule.setContentWithTheme {
+ OutOfTimeScreen(
+ showSitePayment = true,
+ uiState =
+ OutOfTimeUiState()
+ .copy(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableSharedFlow<OutOfTimeViewModel.UiSideEffect>().asSharedFlow()
+ )
+ }
+
+ // Act
+ composeTestRule.onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).performClick()
+
+ // Assert
+ composeTestRule
+ .onNodeWithText(
+ "We are currently verifying your purchase, this might take some time. Your time will be added if the verification is successful."
+ )
+ .assertExists()
+ }
+
+ @Test
+ fun testShowVerificationInProgress() {
+ // Arrange
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.status } returns PaymentStatus.VERIFICATION_IN_PROGRESS
+ composeTestRule.setContentWithTheme {
+ OutOfTimeScreen(
+ showSitePayment = true,
+ uiState =
+ OutOfTimeUiState()
+ .copy(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableSharedFlow<OutOfTimeViewModel.UiSideEffect>().asSharedFlow()
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Verifying purchase").assertExists()
+ }
+
+ @Test
+ fun testOnPurchaseBillingProductClick() {
+ // Arrange
+ val clickHandler: (ProductId, () -> Activity) -> Unit = mockk(relaxed = true)
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.productId } returns ProductId("PRODUCT_ID")
+ every { mockPaymentProduct.status } returns null
+ composeTestRule.setContentWithTheme {
+ OutOfTimeScreen(
+ showSitePayment = true,
+ uiState =
+ OutOfTimeUiState(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableStateFlow(OutOfTimeViewModel.UiSideEffect.OpenConnectScreen),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = clickHandler
+ )
+ }
+
+ // Act
+ composeTestRule.onNodeWithText("Add 30 days time ($10)").performClick()
+
+ // Assert
+ verify { clickHandler(ProductId("PRODUCT_ID"), any()) }
+ }
}
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt
index 8331794cab..a54c41c20d 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt
@@ -1,15 +1,27 @@
package net.mullvad.mullvadvpn.compose.screen
+import android.app.Activity
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import io.mockk.MockKAnnotations
+import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import net.mullvad.mullvadvpn.compose.setContentWithTheme
+import net.mullvad.mullvadvpn.compose.state.PaymentState
import net.mullvad.mullvadvpn.compose.state.WelcomeUiState
+import net.mullvad.mullvadvpn.compose.test.PLAY_PAYMENT_INFO_ICON_TEST_TAG
+import net.mullvad.mullvadvpn.lib.payment.model.PaymentProduct
+import net.mullvad.mullvadvpn.lib.payment.model.PaymentStatus
+import net.mullvad.mullvadvpn.lib.payment.model.ProductId
+import net.mullvad.mullvadvpn.lib.payment.model.ProductPrice
+import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult
+import net.mullvad.mullvadvpn.util.toPaymentDialogData
import net.mullvad.mullvadvpn.viewmodel.WelcomeViewModel
import org.junit.Before
import org.junit.Rule
@@ -35,7 +47,9 @@ class WelcomeScreenTest {
onRedeemVoucherClick = {},
onSettingsClick = {},
onAccountClick = {},
- openConnectScreen = {}
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
)
}
@@ -58,7 +72,9 @@ class WelcomeScreenTest {
onRedeemVoucherClick = {},
onSettingsClick = {},
onAccountClick = {},
- openConnectScreen = {}
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
)
}
@@ -87,7 +103,9 @@ class WelcomeScreenTest {
onRedeemVoucherClick = {},
onSettingsClick = {},
onAccountClick = {},
- openConnectScreen = {}
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
)
}
@@ -108,7 +126,9 @@ class WelcomeScreenTest {
onRedeemVoucherClick = {},
onSettingsClick = {},
onAccountClick = {},
- openConnectScreen = {}
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
)
}
@@ -129,7 +149,9 @@ class WelcomeScreenTest {
onRedeemVoucherClick = {},
onSettingsClick = {},
onAccountClick = {},
- openConnectScreen = mockClickListener
+ openConnectScreen = mockClickListener,
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
)
}
@@ -150,7 +172,9 @@ class WelcomeScreenTest {
onRedeemVoucherClick = {},
onSettingsClick = {},
onAccountClick = {},
- openConnectScreen = {}
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
)
}
@@ -174,7 +198,9 @@ class WelcomeScreenTest {
onRedeemVoucherClick = mockClickListener,
onSettingsClick = {},
onAccountClick = {},
- openConnectScreen = {}
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
)
}
@@ -184,4 +210,265 @@ class WelcomeScreenTest {
// Assert
verify(exactly = 1) { mockClickListener.invoke() }
}
+
+ @Test
+ fun testShowPurchaseCompleteDialog() {
+ // Arrange
+ composeTestRule.setContentWithTheme {
+ WelcomeScreen(
+ showSitePayment = true,
+ uiState =
+ WelcomeUiState(
+ paymentDialogData = PurchaseResult.Completed.Success.toPaymentDialogData()
+ ),
+ uiSideEffect = MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenConnectScreen),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Time was successfully added").assertExists()
+ }
+
+ @Test
+ fun testShowVerificationErrorDialog() {
+ // Arrange
+ composeTestRule.setContentWithTheme {
+ WelcomeScreen(
+ showSitePayment = true,
+ uiState =
+ WelcomeUiState(
+ paymentDialogData =
+ PurchaseResult.Error.VerificationError(null).toPaymentDialogData()
+ ),
+ uiSideEffect = MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenConnectScreen),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Verifying purchase").assertExists()
+ }
+
+ @Test
+ fun testShowFetchProductsErrorDialog() {
+ // Arrange
+ composeTestRule.setContentWithTheme {
+ WelcomeScreen(
+ showSitePayment = true,
+ uiState =
+ WelcomeUiState()
+ .copy(
+ paymentDialogData =
+ PurchaseResult.Error.FetchProductsError(ProductId(""), null)
+ .toPaymentDialogData()
+ ),
+ uiSideEffect = MutableSharedFlow<WelcomeViewModel.UiSideEffect>().asSharedFlow(),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Google Play unavailable").assertExists()
+ }
+
+ @Test
+ fun testShowBillingErrorPaymentButton() {
+ // Arrange
+ composeTestRule.setContentWithTheme {
+ WelcomeScreen(
+ showSitePayment = true,
+ uiState = WelcomeUiState().copy(billingPaymentState = PaymentState.Error.Billing),
+ uiSideEffect = MutableSharedFlow<WelcomeViewModel.UiSideEffect>().asSharedFlow(),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onClosePurchaseResultDialog = {},
+ onPurchaseBillingProductClick = { _, _ -> }
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Add 30 days time").assertExists()
+ }
+
+ @Test
+ fun testShowBillingPaymentAvailable() {
+ // Arrange
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.status } returns null
+ composeTestRule.setContentWithTheme {
+ WelcomeScreen(
+ showSitePayment = true,
+ uiState =
+ WelcomeUiState(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenConnectScreen),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Add 30 days time ($10)").assertExists()
+ }
+
+ @Test
+ fun testShowPendingPayment() {
+ // Arrange
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.status } returns PaymentStatus.PENDING
+ composeTestRule.setContentWithTheme {
+ WelcomeScreen(
+ showSitePayment = true,
+ uiState =
+ WelcomeUiState()
+ .copy(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableSharedFlow<WelcomeViewModel.UiSideEffect>().asSharedFlow(),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Google Play payment pending").assertExists()
+ }
+
+ @Test
+ fun testShowPendingPaymentInfoDialog() {
+ // Arrange
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.status } returns PaymentStatus.PENDING
+ composeTestRule.setContentWithTheme {
+ WelcomeScreen(
+ showSitePayment = true,
+ uiState =
+ WelcomeUiState()
+ .copy(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableSharedFlow<WelcomeViewModel.UiSideEffect>().asSharedFlow(),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
+ )
+ }
+
+ // Act
+ composeTestRule.onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).performClick()
+
+ // Assert
+ composeTestRule
+ .onNodeWithText(
+ "We are currently verifying your purchase, this might take some time. Your time will be added if the verification is successful."
+ )
+ .assertExists()
+ }
+
+ @Test
+ fun testShowVerificationInProgress() {
+ // Arrange
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.status } returns PaymentStatus.VERIFICATION_IN_PROGRESS
+ composeTestRule.setContentWithTheme {
+ WelcomeScreen(
+ showSitePayment = true,
+ uiState =
+ WelcomeUiState()
+ .copy(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableSharedFlow<WelcomeViewModel.UiSideEffect>().asSharedFlow(),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = { _, _ -> },
+ onClosePurchaseResultDialog = {}
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Verifying purchase").assertExists()
+ }
+
+ @Test
+ fun testOnPurchaseBillingProductClick() {
+ // Arrange
+ val clickHandler: (ProductId, () -> Activity) -> Unit = mockk(relaxed = true)
+ val mockPaymentProduct: PaymentProduct = mockk()
+ every { mockPaymentProduct.price } returns ProductPrice("$10")
+ every { mockPaymentProduct.productId } returns ProductId("PRODUCT_ID")
+ every { mockPaymentProduct.status } returns null
+ composeTestRule.setContentWithTheme {
+ WelcomeScreen(
+ showSitePayment = true,
+ uiState =
+ WelcomeUiState(
+ billingPaymentState =
+ PaymentState.PaymentAvailable(listOf(mockPaymentProduct))
+ ),
+ uiSideEffect = MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenConnectScreen),
+ onSitePaymentClick = {},
+ onRedeemVoucherClick = {},
+ onSettingsClick = {},
+ onAccountClick = {},
+ openConnectScreen = {},
+ onPurchaseBillingProductClick = clickHandler,
+ onClosePurchaseResultDialog = {}
+ )
+ }
+
+ // Act
+ composeTestRule.onNodeWithText("Add 30 days time ($10)").performClick()
+
+ // Assert
+ verify { clickHandler(ProductId("PRODUCT_ID"), any()) }
+ }
}