diff options
| author | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2024-01-11 11:49:30 +0100 |
|---|---|---|
| committer | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2024-01-11 11:49:30 +0100 |
| commit | 6c754c3a45fdb93bcb6f9e615c00e70aaa8d90ba (patch) | |
| tree | 7d25c5e9a9066c7c97517bfbe39469242e0e08e5 /android/app/src | |
| parent | b5decd12a264c3e37092211eb0951dac6c640670 (diff) | |
| parent | b4167469808206ecc7ff2f788befe05e1d7ea57b (diff) | |
| download | mullvadvpn-6c754c3a45fdb93bcb6f9e615c00e70aaa8d90ba.tar.xz mullvadvpn-6c754c3a45fdb93bcb6f9e615c00e70aaa8d90ba.zip | |
Merge branch 'update-to-junit5-droid-569'
Diffstat (limited to 'android/app/src')
46 files changed, 2971 insertions, 2837 deletions
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/ComposeRuleExtensions.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/ComposeRuleExtensions.kt index 69cd530b19..7566051c45 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/ComposeRuleExtensions.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/ComposeRuleExtensions.kt @@ -1,9 +1,9 @@ package net.mullvad.mullvadvpn.compose import androidx.compose.runtime.Composable -import androidx.compose.ui.test.junit4.ComposeContentTestRule +import de.mannodermaus.junit5.compose.ComposeContext import net.mullvad.mullvadvpn.lib.theme.AppTheme -fun ComposeContentTestRule.setContentWithTheme(content: @Composable () -> Unit) { +fun ComposeContext.setContentWithTheme(content: @Composable () -> Unit) { setContent { AppTheme { content() } } } diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialogTest.kt index 43e385b65d..1fd106379d 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialogTest.kt @@ -2,22 +2,26 @@ package net.mullvad.mullvadvpn.compose.dialog import android.annotation.SuppressLint import androidx.compose.runtime.Composable -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performTextInput +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.MockKAnnotations import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.compose.test.CUSTOM_PORT_DIALOG_INPUT_TEST_TAG import net.mullvad.mullvadvpn.model.PortRange import net.mullvad.mullvadvpn.onNodeWithTagAndText -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension class CustomPortDialogTest { - @get:Rule val composeTestRule = createComposeRule() + @OptIn(ExperimentalTestApi::class) + @JvmField + @RegisterExtension + val composeExtension = createComposeExtension() - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) } @@ -40,23 +44,22 @@ class CustomPortDialogTest { } @Test - fun testShowWireguardCustomPortDialogInvalidInt() { - // Input a number to make sure that a too long number does not show and it does not crash - // the app + fun testShowWireguardCustomPortDialogInvalidInt() = + composeExtension.use { + // Input a number to make sure that a too long number does not show and it does not + // crash + // the app - // Arrange - composeTestRule.setContentWithTheme { testWireguardCustomPortDialog() } + // Arrange + setContentWithTheme { testWireguardCustomPortDialog() } - // Act - composeTestRule - .onNodeWithTag(CUSTOM_PORT_DIALOG_INPUT_TEST_TAG) - .performTextInput(invalidCustomPort) + // Act + onNodeWithTag(CUSTOM_PORT_DIALOG_INPUT_TEST_TAG).performTextInput(invalidCustomPort) - // Assert - composeTestRule - .onNodeWithTagAndText(CUSTOM_PORT_DIALOG_INPUT_TEST_TAG, invalidCustomPort) - .assertDoesNotExist() - } + // Assert + onNodeWithTagAndText(CUSTOM_PORT_DIALOG_INPUT_TEST_TAG, invalidCustomPort) + .assertDoesNotExist() + } companion object { const val invalidCustomPort = "21474836471" diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt index bc8d87b244..5f9ffaea95 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt @@ -2,16 +2,20 @@ package net.mullvad.mullvadvpn.compose.dialog import android.annotation.SuppressLint import androidx.compose.runtime.Composable +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsNotEnabled -import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText +import de.mannodermaus.junit5.compose.createComposeExtension import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.viewmodel.DnsDialogViewState -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension class DnsDialogTest { - @get:Rule val composeTestRule = createComposeRule() + @OptIn(ExperimentalTestApi::class) + @JvmField + @RegisterExtension + val composeExtension = createComposeExtension() private val defaultState = DnsDialogViewState( @@ -35,80 +39,86 @@ class DnsDialogTest { } @Test - fun testDnsDialogLanWarningShownWhenLanTrafficDisabledAndLocalAddressUsed() { - // Arrange - composeTestRule.setContentWithTheme { - testDnsDialog(defaultState.copy(isAllowLanEnabled = false, isLocal = true)) - } + fun testDnsDialogLanWarningShownWhenLanTrafficDisabledAndLocalAddressUsed() = + composeExtension.use { + // Arrange + setContentWithTheme { + testDnsDialog(defaultState.copy(isAllowLanEnabled = false, isLocal = true)) + } - // Assert - composeTestRule.onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertExists() - } + // Assert + onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertExists() + } @Test - fun testDnsDialogLanWarningNotShownWhenLanTrafficEnabledAndLocalAddressUsed() { - // Arrange - composeTestRule.setContentWithTheme { - testDnsDialog(defaultState.copy(isAllowLanEnabled = true, isLocal = true)) - } + fun testDnsDialogLanWarningNotShownWhenLanTrafficEnabledAndLocalAddressUsed() = + composeExtension.use { + // Arrange + setContentWithTheme { + testDnsDialog(defaultState.copy(isAllowLanEnabled = true, isLocal = true)) + } - // Assert - composeTestRule.onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() - } + // Assert + onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() + } @Test - fun testDnsDialogLanWarningNotShownWhenLanTrafficEnabledAndNonLocalAddressUsed() { - // Arrange - composeTestRule.setContentWithTheme { - testDnsDialog(defaultState.copy(isAllowLanEnabled = true, isLocal = false)) - } + fun testDnsDialogLanWarningNotShownWhenLanTrafficEnabledAndNonLocalAddressUsed() = + composeExtension.use { + // Arrange + setContentWithTheme { + testDnsDialog(defaultState.copy(isAllowLanEnabled = true, isLocal = false)) + } - // Assert - composeTestRule.onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() - } + // Assert + onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() + } @Test - fun testDnsDialogLanWarningNotShownWhenLanTrafficDisabledAndNonLocalAddressUsed() { - // Arrange - composeTestRule.setContentWithTheme { - testDnsDialog(defaultState.copy(isAllowLanEnabled = false, isLocal = false)) - } + fun testDnsDialogLanWarningNotShownWhenLanTrafficDisabledAndNonLocalAddressUsed() = + composeExtension.use { + // Arrange + setContentWithTheme { + testDnsDialog(defaultState.copy(isAllowLanEnabled = false, isLocal = false)) + } - // Assert - composeTestRule.onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() - } + // Assert + onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() + } @Test - fun testDnsDialogSubmitButtonDisabledOnInvalidDnsAddress() { - // Arrange - composeTestRule.setContentWithTheme { - testDnsDialog( - defaultState.copy( - ipAddress = invalidIpAddress, - validationResult = DnsDialogViewState.ValidationResult.InvalidAddress, + fun testDnsDialogSubmitButtonDisabledOnInvalidDnsAddress() = + composeExtension.use { + // Arrange + setContentWithTheme { + testDnsDialog( + defaultState.copy( + ipAddress = invalidIpAddress, + validationResult = DnsDialogViewState.ValidationResult.InvalidAddress, + ) ) - ) - } + } - // Assert - composeTestRule.onNodeWithText("Submit").assertIsNotEnabled() - } + // Assert + onNodeWithText("Submit").assertIsNotEnabled() + } @Test - fun testDnsDialogSubmitButtonDisabledOnDuplicateDnsAddress() { - // Arrange - composeTestRule.setContentWithTheme { - testDnsDialog( - defaultState.copy( - ipAddress = "192.168.0.1", - validationResult = DnsDialogViewState.ValidationResult.DuplicateAddress, + fun testDnsDialogSubmitButtonDisabledOnDuplicateDnsAddress() = + composeExtension.use { + // Arrange + setContentWithTheme { + testDnsDialog( + defaultState.copy( + ipAddress = "192.168.0.1", + validationResult = DnsDialogViewState.ValidationResult.DuplicateAddress, + ) ) - ) - } + } - // Assert - composeTestRule.onNodeWithText("Submit").assertIsNotEnabled() - } + // Assert + onNodeWithText("Submit").assertIsNotEnabled() + } companion object { private const val LOCAL_DNS_SERVER_WARNING = diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialogTest.kt index 38a3bd170d..28d089cc7e 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialogTest.kt @@ -2,24 +2,28 @@ package net.mullvad.mullvadvpn.compose.dialog import android.annotation.SuppressLint import androidx.compose.runtime.Composable +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsEnabled import androidx.compose.ui.test.assertIsNotEnabled -import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify import net.mullvad.mullvadvpn.compose.setContentWithTheme -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension class MtuDialogTest { - @get:Rule val composeTestRule = createComposeRule() + @OptIn(ExperimentalTestApi::class) + @JvmField + @RegisterExtension + val composeExtension = createComposeExtension() - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) } @@ -41,109 +45,114 @@ class MtuDialogTest { } @Test - fun testMtuDialogWithDefaultValue() { - // Arrange - composeTestRule.setContentWithTheme { testMtuDialog() } + fun testMtuDialogWithDefaultValue() = + composeExtension.use { + // Arrange + setContentWithTheme { testMtuDialog() } - // Assert - composeTestRule.onNodeWithText(EMPTY_STRING).assertExists() - } + // Assert + onNodeWithText(EMPTY_STRING).assertExists() + } @Test - fun testMtuDialogWithEditValue() { - // Arrange - composeTestRule.setContentWithTheme { - testMtuDialog( - mtuInitial = VALID_DUMMY_MTU_VALUE, - ) - } + fun testMtuDialogWithEditValue() = + composeExtension.use { + // Arrange + setContentWithTheme { + testMtuDialog( + mtuInitial = VALID_DUMMY_MTU_VALUE, + ) + } - // Assert - composeTestRule.onNodeWithText(VALID_DUMMY_MTU_VALUE.toString()).assertExists() - } + // Assert + onNodeWithText(VALID_DUMMY_MTU_VALUE.toString()).assertExists() + } @Test - fun testMtuDialogTextInput() { - // Arrange - composeTestRule.setContentWithTheme { - testMtuDialog( - null, - ) - } + fun testMtuDialogTextInput() = + composeExtension.use { + // Arrange + setContentWithTheme { + testMtuDialog( + null, + ) + } - // Act - composeTestRule - .onNodeWithText(EMPTY_STRING) - .performTextInput(VALID_DUMMY_MTU_VALUE.toString()) + // Act + onNodeWithText(EMPTY_STRING).performTextInput(VALID_DUMMY_MTU_VALUE.toString()) - // Assert - composeTestRule.onNodeWithText(VALID_DUMMY_MTU_VALUE.toString()).assertExists() - } + // Assert + onNodeWithText(VALID_DUMMY_MTU_VALUE.toString()).assertExists() + } @Test - fun testMtuDialogSubmitOfValidValue() { - // Arrange - val mockedSubmitHandler: (Int) -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - testMtuDialog( - VALID_DUMMY_MTU_VALUE, - onSaveMtu = mockedSubmitHandler, - ) - } + fun testMtuDialogSubmitOfValidValue() = + composeExtension.use { + // Arrange + val mockedSubmitHandler: (Int) -> Unit = mockk(relaxed = true) + setContentWithTheme { + testMtuDialog( + VALID_DUMMY_MTU_VALUE, + onSaveMtu = mockedSubmitHandler, + ) + } - // Act - composeTestRule.onNodeWithText("Submit").assertIsEnabled().performClick() + // Act + onNodeWithText("Submit").assertIsEnabled().performClick() - // Assert - verify { mockedSubmitHandler.invoke(VALID_DUMMY_MTU_VALUE) } - } + // Assert + verify { mockedSubmitHandler.invoke(VALID_DUMMY_MTU_VALUE) } + } @Test - fun testMtuDialogSubmitButtonDisabledWhenInvalidInput() { - // Arrange - composeTestRule.setContentWithTheme { - testMtuDialog( - INVALID_DUMMY_MTU_VALUE, - ) - } + fun testMtuDialogSubmitButtonDisabledWhenInvalidInput() = + composeExtension.use { + // Arrange + setContentWithTheme { + testMtuDialog( + INVALID_DUMMY_MTU_VALUE, + ) + } - // Assert - composeTestRule.onNodeWithText("Submit").assertIsNotEnabled() - } + // Assert + onNodeWithText("Submit").assertIsNotEnabled() + } @Test - fun testMtuDialogResetClick() { - // Arrange - val mockedClickHandler: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - testMtuDialog( - onResetMtu = mockedClickHandler, - ) - } + fun testMtuDialogResetClick() = + composeExtension.use { + // Arrange + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + testMtuDialog( + onResetMtu = mockedClickHandler, + ) + } - // Act - composeTestRule.onNodeWithText("Reset to default").performClick() + // Act + onNodeWithText("Reset to default").performClick() - // Assert - verify { mockedClickHandler.invoke() } - } + // Assert + verify { mockedClickHandler.invoke() } + } @Test - fun testMtuDialogCancelClick() { - // Arrange - val mockedClickHandler: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - testMtuDialog( - onDismiss = mockedClickHandler, - ) - } + fun testMtuDialogCancelClick() = + composeExtension.use { + // Arrange + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + testMtuDialog( + onDismiss = mockedClickHandler, + ) + } - // Assert - composeTestRule.onNodeWithText("Cancel").performClick() + // Assert + onNodeWithText("Cancel").performClick() - // Assert - verify { mockedClickHandler.invoke() } - } + // Assert + verify { mockedClickHandler.invoke() } + } companion object { private const val EMPTY_STRING = "" diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/PaymentDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/PaymentDialogTest.kt index 1a626ecf19..9012b3144f 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/PaymentDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/PaymentDialogTest.kt @@ -1,57 +1,64 @@ package net.mullvad.mullvadvpn.compose.dialog -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText +import de.mannodermaus.junit5.compose.createComposeExtension import net.mullvad.mullvadvpn.compose.dialog.payment.PaymentDialog import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.lib.payment.model.ProductId import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult import net.mullvad.mullvadvpn.util.toPaymentDialogData -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension class PaymentDialogTest { - @get:Rule val composeTestRule = createComposeRule() + @OptIn(ExperimentalTestApi::class) + @JvmField + @RegisterExtension + val composeExtension = createComposeExtension() @Test - fun testShowPurchaseCompleteDialog() { - // Arrange - composeTestRule.setContentWithTheme { - PaymentDialog( - paymentDialogData = PurchaseResult.Completed.Success.toPaymentDialogData()!! - ) - } + fun testShowPurchaseCompleteDialog() = + composeExtension.use { + // Arrange + setContentWithTheme { + PaymentDialog( + paymentDialogData = PurchaseResult.Completed.Success.toPaymentDialogData()!! + ) + } - // Assert - composeTestRule.onNodeWithText("Time was successfully added").assertExists() - } + // Assert + onNodeWithText("Time was successfully added").assertExists() + } @Test - fun testShowVerificationErrorDialog() { - // Arrange - composeTestRule.setContentWithTheme { - PaymentDialog( - paymentDialogData = - PurchaseResult.Error.VerificationError(null).toPaymentDialogData()!! - ) - } + fun testShowVerificationErrorDialog() = + composeExtension.use { + // Arrange + setContentWithTheme { + PaymentDialog( + paymentDialogData = + PurchaseResult.Error.VerificationError(null).toPaymentDialogData()!! + ) + } - // Assert - composeTestRule.onNodeWithText("Verifying purchase").assertExists() - } + // Assert + onNodeWithText("Verifying purchase").assertExists() + } @Test - fun testShowFetchProductsErrorDialog() { - // Arrange - composeTestRule.setContentWithTheme { - PaymentDialog( - paymentDialogData = - PurchaseResult.Error.FetchProductsError(ProductId(""), null) - .toPaymentDialogData()!! - ) - } + fun testShowFetchProductsErrorDialog() = + composeExtension.use { + // Arrange + setContentWithTheme { + PaymentDialog( + paymentDialogData = + PurchaseResult.Error.FetchProductsError(ProductId(""), null) + .toPaymentDialogData()!! + ) + } - // Assert - composeTestRule.onNodeWithText("Google Play unavailable").assertExists() - } + // Assert + onNodeWithText("Google Play unavailable").assertExists() + } } 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 3b42cc1c3b..bead0a02e5 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,10 +1,11 @@ package net.mullvad.mullvadvpn.compose.screen import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.mockk @@ -20,252 +21,272 @@ import net.mullvad.mullvadvpn.lib.payment.model.ProductId import net.mullvad.mullvadvpn.lib.payment.model.ProductPrice import net.mullvad.mullvadvpn.viewmodel.AccountUiState import net.mullvad.mullvadvpn.viewmodel.AccountViewModel -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +@ExperimentalTestApi @OptIn(ExperimentalMaterial3Api::class) class AccountScreenTest { - @get:Rule val composeTestRule = createComposeRule() + @JvmField @RegisterExtension val composeExtension = createComposeExtension() - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) } @Test - fun testDefaultState() { - // Arrange - composeTestRule.setContentWithTheme { - AccountScreen( - uiState = - AccountUiState( - deviceName = DUMMY_DEVICE_NAME, - accountNumber = DUMMY_ACCOUNT_NUMBER, - accountExpiry = null, - showSitePayment = false - ), - uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), - ) - } + fun testDefaultState() = + composeExtension.use { + // Arrange + setContentWithTheme { + AccountScreen( + uiState = + AccountUiState( + deviceName = DUMMY_DEVICE_NAME, + accountNumber = DUMMY_ACCOUNT_NUMBER, + accountExpiry = null, + showSitePayment = false + ), + uiSideEffect = + MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("Redeem voucher").assertExists() onNodeWithText("Log out").assertExists() } - } @Test - fun testManageAccountClick() { - // Arrange - val mockedClickHandler: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - AccountScreen( - uiState = - AccountUiState( - showSitePayment = true, - deviceName = DUMMY_DEVICE_NAME, - accountNumber = DUMMY_ACCOUNT_NUMBER, - accountExpiry = null, - ), - uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), - onManageAccountClick = mockedClickHandler - ) - } + fun testManageAccountClick() = + composeExtension.use { + // Arrange + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + AccountScreen( + uiState = + AccountUiState( + showSitePayment = true, + deviceName = DUMMY_DEVICE_NAME, + accountNumber = DUMMY_ACCOUNT_NUMBER, + accountExpiry = null, + ), + uiSideEffect = + MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), + onManageAccountClick = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithText("Manage account").performClick() + // Act + onNodeWithText("Manage account").performClick() - // Assert - verify { mockedClickHandler.invoke() } - } + // Assert + verify(exactly = 1) { mockedClickHandler.invoke() } + } @Test - fun testRedeemVoucherClick() { - // Arrange - val mockedClickHandler: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - AccountScreen( - uiState = - AccountUiState( - deviceName = DUMMY_DEVICE_NAME, - accountNumber = DUMMY_ACCOUNT_NUMBER, - accountExpiry = null, - showSitePayment = false - ), - uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), - onRedeemVoucherClick = mockedClickHandler - ) - } + fun testRedeemVoucherClick() = + composeExtension.use { + // Arrange + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + AccountScreen( + uiState = + AccountUiState( + deviceName = DUMMY_DEVICE_NAME, + accountNumber = DUMMY_ACCOUNT_NUMBER, + accountExpiry = null, + showSitePayment = false + ), + uiSideEffect = + MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), + onRedeemVoucherClick = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithText("Redeem voucher").performClick() + // Act + onNodeWithText("Redeem voucher").performClick() - // Assert - verify { mockedClickHandler.invoke() } - } + // Assert + verify { mockedClickHandler.invoke() } + } @Test - fun testLogoutClick() { - // Arrange - val mockedClickHandler: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - AccountScreen( - uiState = - AccountUiState( - deviceName = DUMMY_DEVICE_NAME, - accountNumber = DUMMY_ACCOUNT_NUMBER, - accountExpiry = null, - showSitePayment = false - ), - uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), - onLogoutClick = mockedClickHandler - ) - } + fun testLogoutClick() = + composeExtension.use { + // Arrange + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + AccountScreen( + uiState = + AccountUiState( + deviceName = DUMMY_DEVICE_NAME, + accountNumber = DUMMY_ACCOUNT_NUMBER, + accountExpiry = null, + showSitePayment = false + ), + uiSideEffect = + MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), + onLogoutClick = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithText("Log out").performClick() + // Act + onNodeWithText("Log out").performClick() - // Assert - verify { mockedClickHandler.invoke() } - } + // Assert + verify { mockedClickHandler.invoke() } + } @Test - fun testShowBillingErrorPaymentButton() { - // Arrange - composeTestRule.setContentWithTheme { - AccountScreen( - uiState = - AccountUiState.default().copy(billingPaymentState = PaymentState.Error.Billing), - uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), - ) - } + fun testShowBillingErrorPaymentButton() = + composeExtension.use { + // Arrange + setContentWithTheme { + AccountScreen( + uiState = + AccountUiState.default() + .copy(billingPaymentState = PaymentState.Error.Billing), + uiSideEffect = + MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), + ) + } - // Assert - composeTestRule.onNodeWithText("Add 30 days time").assertExists() - } + // Assert + 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( - uiState = - AccountUiState.default() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), - ) - } + fun testShowBillingPaymentAvailable() = + composeExtension.use { + // Arrange + val mockPaymentProduct: PaymentProduct = mockk() + every { mockPaymentProduct.price } returns ProductPrice("$10") + every { mockPaymentProduct.status } returns null + setContentWithTheme { + AccountScreen( + uiState = + AccountUiState.default() + .copy( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + uiSideEffect = + MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), + ) + } - // Assert - composeTestRule.onNodeWithText("Add 30 days time ($10)").assertExists() - } + // Assert + 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( - uiState = - AccountUiState.default() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), - ) - } + fun testShowPendingPayment() = + composeExtension.use { + // Arrange + val mockPaymentProduct: PaymentProduct = mockk() + every { mockPaymentProduct.price } returns ProductPrice("$10") + every { mockPaymentProduct.status } returns PaymentStatus.PENDING + setContentWithTheme { + AccountScreen( + uiState = + AccountUiState.default() + .copy( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + uiSideEffect = + MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), + ) + } - // Assert - composeTestRule.onNodeWithText("Google Play payment pending").assertExists() - } + // Assert + 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 - val mockNavigateToVerificationPending: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - AccountScreen( - uiState = - AccountUiState.default() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), - navigateToVerificationPendingDialog = mockNavigateToVerificationPending - ) - } + fun testShowPendingPaymentInfoDialog() = + composeExtension.use { + // Arrange + val mockPaymentProduct: PaymentProduct = mockk() + every { mockPaymentProduct.price } returns ProductPrice("$10") + every { mockPaymentProduct.status } returns PaymentStatus.PENDING + val mockNavigateToVerificationPending: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + AccountScreen( + uiState = + AccountUiState.default() + .copy( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + uiSideEffect = + MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), + navigateToVerificationPendingDialog = mockNavigateToVerificationPending + ) + } - // Act - composeTestRule.onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).performClick() + // Act + onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).performClick() - // Assert - verify(exactly = 1) { mockNavigateToVerificationPending.invoke() } - } + // Assert + verify(exactly = 1) { mockNavigateToVerificationPending.invoke() } + } @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( - uiState = - AccountUiState.default() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), - ) - } + fun testShowVerificationInProgress() = + composeExtension.use { + // Arrange + val mockPaymentProduct: PaymentProduct = mockk() + every { mockPaymentProduct.price } returns ProductPrice("$10") + every { mockPaymentProduct.status } returns PaymentStatus.VERIFICATION_IN_PROGRESS + setContentWithTheme { + AccountScreen( + uiState = + AccountUiState.default() + .copy( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + uiSideEffect = + MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), + ) + } - // Assert - composeTestRule.onNodeWithText("Verifying purchase").assertExists() - } + // Assert + onNodeWithText("Verifying purchase").assertExists() + } @Test - fun testOnPurchaseBillingProductClick() { - // Arrange - val clickHandler: (ProductId) -> 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( - uiState = - AccountUiState.default() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - onPurchaseBillingProductClick = clickHandler, - uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), - ) - } + fun testOnPurchaseBillingProductClick() = + composeExtension.use { + // Arrange + val clickHandler: (ProductId) -> 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 + setContentWithTheme { + AccountScreen( + uiState = + AccountUiState.default() + .copy( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + onPurchaseBillingProductClick = clickHandler, + uiSideEffect = + MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(), + ) + } - // Act - composeTestRule.onNodeWithText("Add 30 days time ($10)").performClick() + // Act + onNodeWithText("Add 30 days time ($10)").performClick() - // Assert - verify { clickHandler.invoke(ProductId("PRODUCT_ID")) } - } + // Assert + verify { clickHandler.invoke(ProductId("PRODUCT_ID")) } + } companion object { private const val DUMMY_DEVICE_NAME = "fake_name" diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt index 4e34fe0825..66ed6d25f6 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt @@ -1,8 +1,9 @@ package net.mullvad.mullvadvpn.compose.screen -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.MockKAnnotations import io.mockk.Runs import io.mockk.every @@ -13,44 +14,47 @@ import net.mullvad.mullvadvpn.compose.dialog.ChangelogDialog import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.viewmodel.Changelog import net.mullvad.mullvadvpn.viewmodel.ChangelogViewModel -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +@OptIn(ExperimentalTestApi::class) class ChangelogDialogTest { - @get:Rule val composeTestRule = createComposeRule() + @JvmField @RegisterExtension val composeExtension = createComposeExtension() @MockK lateinit var mockedViewModel: ChangelogViewModel - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) } @Test - fun testShowChangelogWhenNeeded() { - // Arrange - every { mockedViewModel.markChangelogAsRead() } just Runs + fun testShowChangeLogWhenNeeded() = + composeExtension.use { + // Arrange + // Arrange + every { mockedViewModel.markChangelogAsRead() } just Runs - composeTestRule.setContentWithTheme { - ChangelogDialog( - Changelog( - changes = listOf(CHANGELOG_ITEM), - version = CHANGELOG_VERSION, - ), - onDismiss = { mockedViewModel.markChangelogAsRead() } - ) - } + setContentWithTheme { + ChangelogDialog( + Changelog( + changes = listOf(CHANGELOG_ITEM), + version = CHANGELOG_VERSION, + ), + onDismiss = { mockedViewModel.markChangelogAsRead() } + ) + } - // Check changelog content showed within dialog - composeTestRule.onNodeWithText(CHANGELOG_ITEM).assertExists() + // Check changelog content showed within dialog + onNodeWithText(CHANGELOG_ITEM).assertExists() - // perform click on Got It button to check if dismiss occur - composeTestRule.onNodeWithText(CHANGELOG_BUTTON_TEXT).performClick() + // perform click on Got It button to check if dismiss occur + onNodeWithText(CHANGELOG_BUTTON_TEXT).performClick() - // Assert - verify { mockedViewModel.markChangelogAsRead() } - } + // Assert + verify { mockedViewModel.markChangelogAsRead() } + } companion object { private const val CHANGELOG_BUTTON_TEXT = "Got it!" diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt index 3838bdc7a0..39e11a3c14 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt @@ -1,9 +1,10 @@ package net.mullvad.mullvadvpn.compose.screen -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.mockk @@ -30,35 +31,38 @@ import net.mullvad.talpid.tunnel.ActionAfterDisconnect import net.mullvad.talpid.tunnel.ErrorState import net.mullvad.talpid.tunnel.ErrorStateCause import org.joda.time.DateTime -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension class ConnectScreenTest { - @get:Rule val composeTestRule = createComposeRule() + @OptIn(ExperimentalTestApi::class) + @JvmField + @RegisterExtension + val composeExtension = createComposeExtension() - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) } - @After + @AfterEach fun teardown() { unmockkAll() } @Test fun testDefaultState() { - // Arrange - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = ConnectUiState.INITIAL, - ) - } + composeExtension.use { + // Arrange + setContentWithTheme { + ConnectScreen( + uiState = ConnectUiState.INITIAL, + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithTag(SCROLLABLE_COLUMN_TEST_TAG).assertExists() onNodeWithText("UNSECURED CONNECTION").assertExists() onNodeWithText("Secure my connection").assertExists() @@ -67,28 +71,28 @@ class ConnectScreenTest { @Test fun testConnectingState() { - // Arrange - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Connecting(null, null), - tunnelRealState = TunnelState.Connecting(null, null), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.TunnelStateBlocked, - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = TunnelState.Connecting(null, null), + tunnelRealState = TunnelState.Connecting(null, null), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.TunnelStateBlocked, + isPlayBuild = false + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithTag(CIRCULAR_PROGRESS_INDICATOR).assertExists() onNodeWithText("CREATING SECURE CONNECTION").assertExists() onNodeWithText("Switch location").assertExists() @@ -99,31 +103,32 @@ class ConnectScreenTest { @Test fun testConnectingStateQuantumSecured() { - // Arrange - val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) - every { mockTunnelEndpoint.quantumResistant } returns true - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Connecting(endpoint = mockTunnelEndpoint, null), - tunnelRealState = - TunnelState.Connecting(endpoint = mockTunnelEndpoint, null), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.TunnelStateBlocked, - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) + every { mockTunnelEndpoint.quantumResistant } returns true + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = + TunnelState.Connecting(endpoint = mockTunnelEndpoint, null), + tunnelRealState = + TunnelState.Connecting(endpoint = mockTunnelEndpoint, null), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.TunnelStateBlocked, + isPlayBuild = false + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithTag(CIRCULAR_PROGRESS_INDICATOR).assertExists() onNodeWithText("CREATING QUANTUM SECURE CONNECTION").assertExists() onNodeWithText("Switch location").assertExists() @@ -134,29 +139,29 @@ class ConnectScreenTest { @Test fun testConnectedState() { - // Arrange - val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Connected(mockTunnelEndpoint, null), - tunnelRealState = TunnelState.Connected(mockTunnelEndpoint, null), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = TunnelState.Connected(mockTunnelEndpoint, null), + tunnelRealState = TunnelState.Connected(mockTunnelEndpoint, null), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("SECURE CONNECTION").assertExists() onNodeWithText("Switch location").assertExists() onNodeWithText("Disconnect").assertExists() @@ -165,30 +170,30 @@ class ConnectScreenTest { @Test fun testConnectedStateQuantumSecured() { - // Arrange - val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) - every { mockTunnelEndpoint.quantumResistant } returns true - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Connected(mockTunnelEndpoint, null), - tunnelRealState = TunnelState.Connected(mockTunnelEndpoint, null), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) + every { mockTunnelEndpoint.quantumResistant } returns true + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = TunnelState.Connected(mockTunnelEndpoint, null), + tunnelRealState = TunnelState.Connected(mockTunnelEndpoint, null), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("QUANTUM SECURE CONNECTION").assertExists() onNodeWithText("Switch location").assertExists() onNodeWithText("Disconnect").assertExists() @@ -197,31 +202,33 @@ class ConnectScreenTest { @Test fun testDisconnectingState() { - // Arrange - val mockRelayLocation: RelayItem = mockk(relaxed = true) - val mockLocationName = "Home" - every { mockRelayLocation.locationName } returns mockLocationName - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = mockRelayLocation, - tunnelUiState = TunnelState.Disconnecting(ActionAfterDisconnect.Nothing), - tunnelRealState = TunnelState.Disconnecting(ActionAfterDisconnect.Nothing), - inAddress = null, - outAddress = "", - showLocation = true, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val mockRelayLocation: RelayItem = mockk(relaxed = true) + val mockLocationName = "Home" + every { mockRelayLocation.locationName } returns mockLocationName + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = mockRelayLocation, + tunnelUiState = + TunnelState.Disconnecting(ActionAfterDisconnect.Nothing), + tunnelRealState = + TunnelState.Disconnecting(ActionAfterDisconnect.Nothing), + inAddress = null, + outAddress = "", + showLocation = true, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("UNSECURED CONNECTION").assertExists() onNodeWithText(mockLocationName).assertExists() onNodeWithText("Disconnect").assertExists() @@ -230,31 +237,31 @@ class ConnectScreenTest { @Test fun testDisconnectedState() { - // Arrange - val mockRelayLocation: RelayItem = mockk(relaxed = true) - val mockLocationName = "Home" - every { mockRelayLocation.locationName } returns mockLocationName - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = mockRelayLocation, - tunnelUiState = TunnelState.Disconnected(), - tunnelRealState = TunnelState.Disconnected(), - inAddress = null, - outAddress = "", - showLocation = true, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val mockRelayLocation: RelayItem = mockk(relaxed = true) + val mockLocationName = "Home" + every { mockRelayLocation.locationName } returns mockLocationName + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = mockRelayLocation, + tunnelUiState = TunnelState.Disconnected(), + tunnelRealState = TunnelState.Disconnected(), + inAddress = null, + outAddress = "", + showLocation = true, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("UNSECURED CONNECTION").assertExists() onNodeWithText(mockLocationName).assertExists() onNodeWithText("Secure my connection").assertExists() @@ -263,36 +270,40 @@ class ConnectScreenTest { @Test fun testErrorStateBlocked() { - // Arrange - val mockRelayLocation: RelayItem = mockk(relaxed = true) - val mockLocationName = "Home" - every { mockRelayLocation.locationName } returns mockLocationName - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = mockRelayLocation, - tunnelUiState = - TunnelState.Error(ErrorState(ErrorStateCause.StartTunnelError, true)), - tunnelRealState = - TunnelState.Error(ErrorState(ErrorStateCause.StartTunnelError, true)), - inAddress = null, - outAddress = "", - showLocation = true, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = - InAppNotification.TunnelStateError( - ErrorState(ErrorStateCause.StartTunnelError, true) - ), - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val mockRelayLocation: RelayItem = mockk(relaxed = true) + val mockLocationName = "Home" + every { mockRelayLocation.locationName } returns mockLocationName + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = mockRelayLocation, + tunnelUiState = + TunnelState.Error( + ErrorState(ErrorStateCause.StartTunnelError, true) + ), + tunnelRealState = + TunnelState.Error( + ErrorState(ErrorStateCause.StartTunnelError, true) + ), + inAddress = null, + outAddress = "", + showLocation = true, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = + InAppNotification.TunnelStateError( + ErrorState(ErrorStateCause.StartTunnelError, true) + ), + isPlayBuild = false + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("BLOCKED CONNECTION").assertExists() onNodeWithText(mockLocationName).assertExists() onNodeWithText("Disconnect").assertExists() @@ -302,36 +313,40 @@ class ConnectScreenTest { @Test fun testErrorStateNotBlocked() { - // Arrange - val mockRelayLocation: RelayItem = mockk(relaxed = true) - val mockLocationName = "Home" - every { mockRelayLocation.locationName } returns mockLocationName - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = mockRelayLocation, - tunnelUiState = - TunnelState.Error(ErrorState(ErrorStateCause.StartTunnelError, false)), - tunnelRealState = - TunnelState.Error(ErrorState(ErrorStateCause.StartTunnelError, false)), - inAddress = null, - outAddress = "", - showLocation = true, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = - InAppNotification.TunnelStateError( - ErrorState(ErrorStateCause.StartTunnelError, false) - ), - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val mockRelayLocation: RelayItem = mockk(relaxed = true) + val mockLocationName = "Home" + every { mockRelayLocation.locationName } returns mockLocationName + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = mockRelayLocation, + tunnelUiState = + TunnelState.Error( + ErrorState(ErrorStateCause.StartTunnelError, false) + ), + tunnelRealState = + TunnelState.Error( + ErrorState(ErrorStateCause.StartTunnelError, false) + ), + inAddress = null, + outAddress = "", + showLocation = true, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = + InAppNotification.TunnelStateError( + ErrorState(ErrorStateCause.StartTunnelError, false) + ), + isPlayBuild = false + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("FAILED TO SECURE CONNECTION").assertExists() onNodeWithText(mockLocationName).assertExists() onNodeWithText("Dismiss").assertExists() @@ -342,29 +357,30 @@ class ConnectScreenTest { @Test fun testReconnectingState() { - // Arrange - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Disconnecting(ActionAfterDisconnect.Reconnect), - tunnelRealState = - TunnelState.Disconnecting(ActionAfterDisconnect.Reconnect), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.TunnelStateBlocked, - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = + TunnelState.Disconnecting(ActionAfterDisconnect.Reconnect), + tunnelRealState = + TunnelState.Disconnecting(ActionAfterDisconnect.Reconnect), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.TunnelStateBlocked, + isPlayBuild = false + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithTag(CIRCULAR_PROGRESS_INDICATOR).assertExists() onNodeWithText("CREATING SECURE CONNECTION").assertExists() onNodeWithText("Switch location").assertExists() @@ -375,31 +391,32 @@ class ConnectScreenTest { @Test fun testDisconnectingBlockState() { - // Arrange - val mockRelayLocation: RelayItem = mockk(relaxed = true) - val mockLocationName = "Home" - every { mockRelayLocation.locationName } returns mockLocationName - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = mockRelayLocation, - tunnelUiState = TunnelState.Disconnecting(ActionAfterDisconnect.Block), - tunnelRealState = TunnelState.Disconnecting(ActionAfterDisconnect.Block), - inAddress = null, - outAddress = "", - showLocation = true, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.TunnelStateBlocked, - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val mockRelayLocation: RelayItem = mockk(relaxed = true) + val mockLocationName = "Home" + every { mockRelayLocation.locationName } returns mockLocationName + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = mockRelayLocation, + tunnelUiState = TunnelState.Disconnecting(ActionAfterDisconnect.Block), + tunnelRealState = + TunnelState.Disconnecting(ActionAfterDisconnect.Block), + inAddress = null, + outAddress = "", + showLocation = true, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.TunnelStateBlocked, + isPlayBuild = false + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("SECURE CONNECTION").assertExists() onNodeWithText(mockLocationName).assertExists() onNodeWithText("Disconnect").assertExists() @@ -409,199 +426,210 @@ class ConnectScreenTest { @Test fun testClickSelectLocationButton() { - // Arrange - val mockRelayLocation: RelayItem = mockk(relaxed = true) - val mockLocationName = "Home" - every { mockRelayLocation.locationName } returns mockLocationName - val mockedClickHandler: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = mockRelayLocation, - tunnelUiState = TunnelState.Disconnected(), - tunnelRealState = TunnelState.Disconnected(), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false - ), - onSwitchLocationClick = mockedClickHandler - ) - } + composeExtension.use { + // Arrange + val mockRelayLocation: RelayItem = mockk(relaxed = true) + val mockLocationName = "Home" + every { mockRelayLocation.locationName } returns mockLocationName + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = mockRelayLocation, + tunnelUiState = TunnelState.Disconnected(), + tunnelRealState = TunnelState.Disconnected(), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false + ), + onSwitchLocationClick = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithTag(SELECT_LOCATION_BUTTON_TEST_TAG).performClick() + // Act + onNodeWithTag(SELECT_LOCATION_BUTTON_TEST_TAG).performClick() - // Assert - verify { mockedClickHandler.invoke() } + // Assert + verify { mockedClickHandler.invoke() } + } } @Test fun testOnDisconnectClick() { - // Arrange - val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) - val mockedClickHandler: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Connected(mockTunnelEndpoint, null), - tunnelRealState = TunnelState.Connected(mockTunnelEndpoint, null), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false - ), - onDisconnectClick = mockedClickHandler - ) - } + composeExtension.use { + // Arrange + val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = TunnelState.Connected(mockTunnelEndpoint, null), + tunnelRealState = TunnelState.Connected(mockTunnelEndpoint, null), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false + ), + onDisconnectClick = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithTag(CONNECT_BUTTON_TEST_TAG).performClick() + // Act + onNodeWithTag(CONNECT_BUTTON_TEST_TAG).performClick() - // Assert - verify { mockedClickHandler.invoke() } + // Assert + verify { mockedClickHandler.invoke() } + } } @Test fun testOnReconnectClick() { - // Arrange - val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) - val mockedClickHandler: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Connected(mockTunnelEndpoint, null), - tunnelRealState = TunnelState.Connected(mockTunnelEndpoint, null), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false - ), - onReconnectClick = mockedClickHandler - ) - } + composeExtension.use { + // Arrange + val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = TunnelState.Connected(mockTunnelEndpoint, null), + tunnelRealState = TunnelState.Connected(mockTunnelEndpoint, null), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false + ), + onReconnectClick = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithTag(RECONNECT_BUTTON_TEST_TAG).performClick() + // Act + onNodeWithTag(RECONNECT_BUTTON_TEST_TAG).performClick() - // Assert - verify { mockedClickHandler.invoke() } + // Assert + verify { mockedClickHandler.invoke() } + } } @Test fun testOnConnectClick() { - // Arrange - val mockedClickHandler: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Disconnected(), - tunnelRealState = TunnelState.Disconnected(), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false - ), - onConnectClick = mockedClickHandler - ) - } + composeExtension.use { + // Arrange + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = TunnelState.Disconnected(), + tunnelRealState = TunnelState.Disconnected(), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false + ), + onConnectClick = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithTag(CONNECT_BUTTON_TEST_TAG).performClick() + // Act + onNodeWithTag(CONNECT_BUTTON_TEST_TAG).performClick() - // Assert - verify { mockedClickHandler.invoke() } + // Assert + verify { mockedClickHandler.invoke() } + } } @Test fun testOnCancelClick() { - // Arrange - val mockedClickHandler: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Connecting(null, null), - tunnelRealState = TunnelState.Connecting(null, null), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false - ), - onCancelClick = mockedClickHandler - ) - } + composeExtension.use { + // Arrange + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = TunnelState.Connecting(null, null), + tunnelRealState = TunnelState.Connecting(null, null), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false + ), + onCancelClick = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithTag(CONNECT_BUTTON_TEST_TAG).performClick() + // Act + onNodeWithTag(CONNECT_BUTTON_TEST_TAG).performClick() - // Assert - verify { mockedClickHandler.invoke() } + // Assert + verify { mockedClickHandler.invoke() } + } } @Test fun showLocationInfo() { - // Arrange - val mockLocation: GeoIpLocation = mockk(relaxed = true) - val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) - val mockHostName = "Host-Name" - val mockPort = 99 - val mockHost = "Host" - val mockProtocol = TransportProtocol.Udp - val mockInAddress = Triple(mockHost, mockPort, mockProtocol) - val mockOutAddress = "HostAddressV4 / HostAddressV4" - every { mockLocation.hostname } returns mockHostName - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = mockLocation, - relayLocation = null, - tunnelUiState = TunnelState.Connected(mockTunnelEndpoint, null), - tunnelRealState = TunnelState.Connected(mockTunnelEndpoint, null), - inAddress = mockInAddress, - outAddress = mockOutAddress, - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val mockLocation: GeoIpLocation = mockk(relaxed = true) + val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) + val mockHostName = "Host-Name" + val mockPort = 99 + val mockHost = "Host" + val mockProtocol = TransportProtocol.Udp + val mockInAddress = Triple(mockHost, mockPort, mockProtocol) + val mockOutAddress = "HostAddressV4 / HostAddressV4" + every { mockLocation.hostname } returns mockHostName + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = mockLocation, + relayLocation = null, + tunnelUiState = TunnelState.Connected(mockTunnelEndpoint, null), + tunnelRealState = TunnelState.Connected(mockTunnelEndpoint, null), + inAddress = mockInAddress, + outAddress = mockOutAddress, + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false + ), + ) + } - composeTestRule.onNodeWithTag(LOCATION_INFO_TEST_TAG).performClick() + // Act + onNodeWithTag(LOCATION_INFO_TEST_TAG).performClick() - // Assert - composeTestRule.apply { + // Assert onNodeWithText(mockHostName).assertExists() onNodeWithText("WireGuard").assertExists() onNodeWithText("In $mockHost:$mockPort UDP").assertExists() @@ -611,35 +639,35 @@ class ConnectScreenTest { @Test fun testOutdatedVersionNotification() { - // Arrange - val versionInfo = - VersionInfo( - currentVersion = "1.0", - upgradeVersion = "1.1", - isOutdated = true, - isSupported = true - ) - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Connecting(null, null), - tunnelRealState = TunnelState.Connecting(null, null), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.UpdateAvailable(versionInfo), - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val versionInfo = + VersionInfo( + currentVersion = "1.0", + upgradeVersion = "1.1", + isOutdated = true, + isSupported = true + ) + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = TunnelState.Connecting(null, null), + tunnelRealState = TunnelState.Connecting(null, null), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.UpdateAvailable(versionInfo), + isPlayBuild = false + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("UPDATE AVAILABLE").assertExists() onNodeWithText("Install Mullvad VPN (1.1) to stay up to date").assertExists() } @@ -647,35 +675,35 @@ class ConnectScreenTest { @Test fun testUnsupportedVersionNotification() { - // Arrange - val versionInfo = - VersionInfo( - currentVersion = "1.0", - upgradeVersion = "1.1", - isOutdated = true, - isSupported = false - ) - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Connecting(null, null), - tunnelRealState = TunnelState.Connecting(null, null), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.UnsupportedVersion(versionInfo), - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val versionInfo = + VersionInfo( + currentVersion = "1.0", + upgradeVersion = "1.1", + isOutdated = true, + isSupported = false + ) + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = TunnelState.Connecting(null, null), + tunnelRealState = TunnelState.Connecting(null, null), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.UnsupportedVersion(versionInfo), + isPlayBuild = false + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("UNSUPPORTED VERSION").assertExists() onNodeWithText( "Your privacy might be at risk with this unsupported app version. Please update now." @@ -686,29 +714,29 @@ class ConnectScreenTest { @Test fun testAccountExpiredNotification() { - // Arrange - val expiryDate = DateTime(2020, 11, 11, 10, 10) - composeTestRule.setContentWithTheme { - ConnectScreen( - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Connecting(null, null), - tunnelRealState = TunnelState.Connecting(null, null), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.AccountExpiry(expiryDate), - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val expiryDate = DateTime(2020, 11, 11, 10, 10) + setContentWithTheme { + ConnectScreen( + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = TunnelState.Connecting(null, null), + tunnelRealState = TunnelState.Connecting(null, null), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.AccountExpiry(expiryDate), + isPlayBuild = false + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("ACCOUNT CREDIT EXPIRES SOON").assertExists() onNodeWithText("Out of time").assertExists() } @@ -716,85 +744,94 @@ class ConnectScreenTest { @Test fun testOnUpdateVersionClick() { - // Arrange - val mockedClickHandler: () -> Unit = mockk(relaxed = true) - val versionInfo = - VersionInfo( - currentVersion = "1.0", - upgradeVersion = "1.1", - isOutdated = true, - isSupported = false - ) - composeTestRule.setContentWithTheme { - ConnectScreen( - onUpdateVersionClick = mockedClickHandler, - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Connecting(null, null), - tunnelRealState = TunnelState.Connecting(null, null), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.UnsupportedVersion(versionInfo), - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + val versionInfo = + VersionInfo( + currentVersion = "1.0", + upgradeVersion = "1.1", + isOutdated = true, + isSupported = false + ) + setContentWithTheme { + ConnectScreen( + onUpdateVersionClick = mockedClickHandler, + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = TunnelState.Connecting(null, null), + tunnelRealState = TunnelState.Connecting(null, null), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.UnsupportedVersion(versionInfo), + isPlayBuild = false + ), + ) + } - // Act - composeTestRule.onNodeWithTag(NOTIFICATION_BANNER_ACTION).performClick() + // Act + onNodeWithTag(NOTIFICATION_BANNER_ACTION).performClick() - // Assert - verify { mockedClickHandler.invoke() } + // Assert + verify { mockedClickHandler.invoke() } + } } @Test fun testOnShowAccountClick() { - // Arrange - val mockedClickHandler: () -> Unit = mockk(relaxed = true) - val expiryDate = DateTime(2020, 11, 11, 10, 10) - composeTestRule.setContentWithTheme { - ConnectScreen( - onManageAccountClick = mockedClickHandler, - uiState = - ConnectUiState( - location = null, - relayLocation = null, - tunnelUiState = TunnelState.Connecting(null, null), - tunnelRealState = TunnelState.Connecting(null, null), - inAddress = null, - outAddress = "", - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.AccountExpiry(expiryDate), - isPlayBuild = false - ), - ) - } + composeExtension.use { + // Arrange + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + val expiryDate = DateTime(2020, 11, 11, 10, 10) + setContentWithTheme { + ConnectScreen( + onManageAccountClick = mockedClickHandler, + uiState = + ConnectUiState( + location = null, + relayLocation = null, + tunnelUiState = TunnelState.Connecting(null, null), + tunnelRealState = TunnelState.Connecting(null, null), + inAddress = null, + outAddress = "", + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.AccountExpiry(expiryDate), + isPlayBuild = false + ), + ) + } - // Act - composeTestRule.onNodeWithTag(NOTIFICATION_BANNER_ACTION).performClick() + // Act + onNodeWithTag(NOTIFICATION_BANNER_ACTION).performClick() - // Assert - verify { mockedClickHandler.invoke() } + // Assert + verify { mockedClickHandler.invoke() } + } } @Test fun testOpenAccountView() { - // Arrange - val onAccountClickMockk: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - ConnectScreen(uiState = ConnectUiState.INITIAL, onAccountClick = onAccountClickMockk) - } + composeExtension.use { + // Arrange + val onAccountClickMockk: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + ConnectScreen( + uiState = ConnectUiState.INITIAL, + onAccountClick = onAccountClickMockk + ) + } - // Assert - composeTestRule.onNodeWithTag(TOP_BAR_ACCOUNT_BUTTON).performClick() + // Assert + onNodeWithTag(TOP_BAR_ACCOUNT_BUTTON).performClick() - verify(exactly = 1) { onAccountClickMockk() } + verify(exactly = 1) { onAccountClickMockk() } + } } } diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt index 56e24b9a08..f65a23fa9c 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt @@ -1,64 +1,69 @@ package net.mullvad.mullvadvpn.compose.screen -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.compose.state.DeviceRevokedUiState -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +@OptIn(ExperimentalTestApi::class) class DeviceRevokedScreenTest { - @get:Rule val composeTestRule = createComposeRule() + @JvmField @RegisterExtension val composeExtension = createComposeExtension() - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) } @Test - fun testUnblockWarningShowingWhenSecured() { - // Arrange - val state = DeviceRevokedUiState.SECURED + fun testUnblockWarningShowingWhenSecured() = + composeExtension.use { + // Arrange + val state = DeviceRevokedUiState.SECURED - // Act - composeTestRule.setContentWithTheme { DeviceRevokedScreen(state) } + // Act + setContentWithTheme { DeviceRevokedScreen(state) } - // Assert - composeTestRule.onNodeWithText(UNBLOCK_WARNING).assertExists() - } + // Assert + onNodeWithText(UNBLOCK_WARNING).assertExists() + } @Test - fun testUnblockWarningNotShowingWhenNotSecured() { - // Arrange - val state = DeviceRevokedUiState.UNSECURED + fun testUnblockWarningNotShowingWhenNotSecured() = + composeExtension.use { + // Arrange + val state = DeviceRevokedUiState.UNSECURED - // Act - composeTestRule.setContentWithTheme { DeviceRevokedScreen(state) } + // Act + setContentWithTheme { DeviceRevokedScreen(state) } - // Assert - composeTestRule.onNodeWithText(UNBLOCK_WARNING).assertDoesNotExist() - } + // Assert + onNodeWithText(UNBLOCK_WARNING).assertDoesNotExist() + } @Test - fun testGoToLogin() { - // Arrange - val state = DeviceRevokedUiState.UNSECURED - val mockOnGoToLoginClicked: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - DeviceRevokedScreen(state = state, onGoToLoginClicked = mockOnGoToLoginClicked) - } + fun testGoToLogin() = + composeExtension.use { + // Arrange + val state = DeviceRevokedUiState.UNSECURED + val mockOnGoToLoginClicked: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + DeviceRevokedScreen(state = state, onGoToLoginClicked = mockOnGoToLoginClicked) + } - // Act - composeTestRule.onNodeWithText(GO_TO_LOGIN_BUTTON_TEXT).performClick() + // Act + onNodeWithText(GO_TO_LOGIN_BUTTON_TEXT).performClick() - // Assert - verify { mockOnGoToLoginClicked.invoke() } - } + // Assert + verify { mockOnGoToLoginClicked.invoke() } + } companion object { private const val GO_TO_LOGIN_BUTTON_TEXT = "Go to login" diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreenTest.kt index 32fd727329..11f15b7823 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreenTest.kt @@ -1,8 +1,9 @@ package net.mullvad.mullvadvpn.compose.screen -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -10,132 +11,128 @@ import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.compose.state.RelayFilterState import net.mullvad.mullvadvpn.model.Ownership import net.mullvad.mullvadvpn.relaylist.Provider -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +@OptIn(ExperimentalTestApi::class) class FilterScreenTest { - @get:Rule val composeTestRule = createComposeRule() + @JvmField @RegisterExtension val composeExtension = createComposeExtension() fun setup() { MockKAnnotations.init(this) } @Test - fun testDefaultState() { - composeTestRule.setContentWithTheme { - FilterScreen( - uiState = - RelayFilterState( - allProviders = DUMMY_RELAY_ALL_PROVIDERS, - selectedOwnership = null, - selectedProviders = DUMMY_SELECTED_PROVIDERS, - ), - onSelectedProvider = { _, _ -> } - ) - } - composeTestRule.apply { + fun testDefaultState() = + composeExtension.use { + setContentWithTheme { + FilterScreen( + uiState = + RelayFilterState( + allProviders = DUMMY_RELAY_ALL_PROVIDERS, + selectedOwnership = null, + selectedProviders = DUMMY_SELECTED_PROVIDERS, + ), + onSelectedProvider = { _, _ -> } + ) + } onNodeWithText("Ownership").assertExists() onNodeWithText("Providers").assertExists() } - } @Test - fun testIsAnyCellShowing() { - composeTestRule.setContentWithTheme { - FilterScreen( - uiState = - RelayFilterState( - allProviders = DUMMY_RELAY_ALL_PROVIDERS, - selectedOwnership = null, - selectedProviders = DUMMY_SELECTED_PROVIDERS - ), - onSelectedProvider = { _, _ -> } - ) - } - composeTestRule.apply { + fun testIsAnyCellShowing() = + composeExtension.use { + setContentWithTheme { + FilterScreen( + uiState = + RelayFilterState( + allProviders = DUMMY_RELAY_ALL_PROVIDERS, + selectedOwnership = null, + selectedProviders = DUMMY_SELECTED_PROVIDERS + ), + onSelectedProvider = { _, _ -> } + ) + } onNodeWithText("Ownership").performClick() onNodeWithText("Any").assertExists() } - } @Test - fun testIsMullvadCellShowing() { - composeTestRule.setContentWithTheme { - FilterScreen( - uiState = - RelayFilterState( - allProviders = DUMMY_RELAY_ALL_PROVIDERS, - selectedOwnership = Ownership.MullvadOwned, - selectedProviders = DUMMY_SELECTED_PROVIDERS - ), - onSelectedProvider = { _, _ -> } - ) - } - composeTestRule.apply { + fun testIsMullvadCellShowing() = + composeExtension.use { + setContentWithTheme { + FilterScreen( + uiState = + RelayFilterState( + allProviders = DUMMY_RELAY_ALL_PROVIDERS, + selectedOwnership = Ownership.MullvadOwned, + selectedProviders = DUMMY_SELECTED_PROVIDERS + ), + onSelectedProvider = { _, _ -> } + ) + } onNodeWithText("Ownership").performClick() onNodeWithText("Mullvad owned only").assertExists() } - } @Test - fun testIsRentedCellShowing() { - composeTestRule.setContentWithTheme { - FilterScreen( - uiState = - RelayFilterState( - allProviders = DUMMY_RELAY_ALL_PROVIDERS, - selectedOwnership = Ownership.Rented, - selectedProviders = DUMMY_SELECTED_PROVIDERS - ), - onSelectedProvider = { _, _ -> } - ) - } - composeTestRule.apply { + fun testIsRentedCellShowing() = + composeExtension.use { + setContentWithTheme { + FilterScreen( + uiState = + RelayFilterState( + allProviders = DUMMY_RELAY_ALL_PROVIDERS, + selectedOwnership = Ownership.Rented, + selectedProviders = DUMMY_SELECTED_PROVIDERS + ), + onSelectedProvider = { _, _ -> } + ) + } onNodeWithText("Ownership").performClick() onNodeWithText("Rented only").assertExists() } - } @Test - fun testShowProviders() { - composeTestRule.setContentWithTheme { - FilterScreen( - uiState = - RelayFilterState( - allProviders = DUMMY_RELAY_ALL_PROVIDERS, - selectedOwnership = null, - selectedProviders = DUMMY_SELECTED_PROVIDERS - ), - onSelectedProvider = { _, _ -> } - ) - } + fun testShowProviders() = + composeExtension.use { + setContentWithTheme { + FilterScreen( + uiState = + RelayFilterState( + allProviders = DUMMY_RELAY_ALL_PROVIDERS, + selectedOwnership = null, + selectedProviders = DUMMY_SELECTED_PROVIDERS + ), + onSelectedProvider = { _, _ -> } + ) + } - composeTestRule.apply { onNodeWithText("Providers").performClick() onNodeWithText("Creanova").assertExists() - onNodeWithText("Creanova").assertExists() onNodeWithText("100TB").assertExists() } - } @Test - fun testApplyButtonClick() { - val mockClickListener: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - FilterScreen( - uiState = - RelayFilterState( - allProviders = listOf(), - selectedOwnership = null, - selectedProviders = listOf(Provider("31173", true)) - ), - onSelectedProvider = { _, _ -> }, - onApplyClick = mockClickListener - ) + fun testApplyButtonClick() = + composeExtension.use { + val mockClickListener: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + FilterScreen( + uiState = + RelayFilterState( + allProviders = listOf(), + selectedOwnership = null, + selectedProviders = listOf(Provider("31173", true)) + ), + onSelectedProvider = { _, _ -> }, + onApplyClick = mockClickListener + ) + } + onNodeWithText("Apply").performClick() + verify { mockClickListener() } } - composeTestRule.onNodeWithText("Apply").performClick() - verify { mockClickListener() } - } companion object { 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 d43a0931a1..f3f1d0cba5 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,9 +1,10 @@ package net.mullvad.mullvadvpn.compose.screen -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.mockk @@ -17,34 +18,35 @@ 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.model.TunnelState -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +@OptIn(ExperimentalTestApi::class) class OutOfTimeScreenTest { - @get:Rule val composeTestRule = createComposeRule() + @JvmField @RegisterExtension val composeExtension = createComposeExtension() - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) } @Test - fun testDisableSitePayment() { - // Arrange - composeTestRule.setContentWithTheme { - OutOfTimeScreen( - uiState = OutOfTimeUiState(deviceName = ""), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onDisconnectClick = {} - ) - } + fun testDisableSitePayment() = + composeExtension.use { + // Arrange + setContentWithTheme { + OutOfTimeScreen( + uiState = OutOfTimeUiState(deviceName = ""), + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onDisconnectClick = {} + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText( "Either buy credit on our website or redeem a voucher.", substring = true @@ -52,241 +54,251 @@ class OutOfTimeScreenTest { .assertDoesNotExist() onNodeWithText("Buy credit").assertDoesNotExist() } - } @Test - fun testOpenAccountView() { - // Arrange - composeTestRule.setContentWithTheme { - OutOfTimeScreen( - uiState = OutOfTimeUiState(deviceName = "", showSitePayment = true), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onDisconnectClick = {} - ) - } + fun testOpenAccountView() = + composeExtension.use { + // Arrange + setContentWithTheme { + OutOfTimeScreen( + uiState = OutOfTimeUiState(deviceName = "", showSitePayment = true), + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onDisconnectClick = {} + ) + } - // Assert - composeTestRule.apply { onNodeWithText("Congrats!").assertDoesNotExist() } - } + // Assert + onNodeWithText("Congrats!").assertDoesNotExist() + } @Test - fun testClickSitePaymentButton() { - // Arrange - val mockClickListener: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - OutOfTimeScreen( - uiState = OutOfTimeUiState(deviceName = "", showSitePayment = true), - onSitePaymentClick = mockClickListener, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onDisconnectClick = {} - ) - } + fun testClickSitePaymentButton() = + composeExtension.use { + // Arrange + val mockClickListener: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + OutOfTimeScreen( + uiState = OutOfTimeUiState(deviceName = "", showSitePayment = true), + onSitePaymentClick = mockClickListener, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onDisconnectClick = {} + ) + } - // Act - composeTestRule.apply { onNodeWithText("Buy credit").performClick() } + // Act + onNodeWithText("Buy credit").performClick() - // Assert - verify(exactly = 1) { mockClickListener.invoke() } - } + // Assert + verify(exactly = 1) { mockClickListener.invoke() } + } @Test - fun testClickRedeemVoucher() { - // Arrange - val mockClickListener: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - OutOfTimeScreen( - uiState = OutOfTimeUiState(deviceName = "", showSitePayment = true), - onSitePaymentClick = {}, - onRedeemVoucherClick = mockClickListener, - onSettingsClick = {}, - onAccountClick = {}, - onDisconnectClick = {} - ) - } + fun testClickRedeemVoucher() = + composeExtension.use { + // Arrange + val mockClickListener: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + OutOfTimeScreen( + uiState = OutOfTimeUiState(deviceName = "", showSitePayment = true), + onSitePaymentClick = {}, + onRedeemVoucherClick = mockClickListener, + onSettingsClick = {}, + onAccountClick = {}, + onDisconnectClick = {} + ) + } - // Act - composeTestRule.apply { onNodeWithText("Redeem voucher").performClick() } + // Act + onNodeWithText("Redeem voucher").performClick() - // Assert - verify(exactly = 1) { mockClickListener.invoke() } - } + // Assert + verify(exactly = 1) { mockClickListener.invoke() } + } @Test - fun testClickDisconnect() { - // Arrange - val mockClickListener: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - OutOfTimeScreen( - uiState = - OutOfTimeUiState( - tunnelState = TunnelState.Connecting(null, null), - deviceName = "", - showSitePayment = true - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onDisconnectClick = mockClickListener - ) - } + fun testClickDisconnect() = + composeExtension.use { + // Arrange + val mockClickListener: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + OutOfTimeScreen( + uiState = + OutOfTimeUiState( + tunnelState = TunnelState.Connecting(null, null), + deviceName = "", + showSitePayment = true + ), + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onDisconnectClick = mockClickListener + ) + } - // Act - composeTestRule.apply { onNodeWithText("Disconnect").performClick() } + // Act + onNodeWithText("Disconnect").performClick() - // Assert - verify(exactly = 1) { mockClickListener.invoke() } - } + // Assert + verify(exactly = 1) { mockClickListener.invoke() } + } @Test - fun testShowBillingErrorPaymentButton() { - // Arrange - composeTestRule.setContentWithTheme { - OutOfTimeScreen( - uiState = - OutOfTimeUiState( - showSitePayment = true, - billingPaymentState = PaymentState.Error.Billing - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> } - ) - } + fun testShowBillingErrorPaymentButton() = + composeExtension.use { + // Arrange + setContentWithTheme { + OutOfTimeScreen( + uiState = + OutOfTimeUiState( + showSitePayment = true, + billingPaymentState = PaymentState.Error.Billing + ), + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = { _ -> } + ) + } - // Assert - composeTestRule.onNodeWithText("Add 30 days time").assertExists() - } + // Assert + 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( - uiState = - OutOfTimeUiState( - showSitePayment = true, - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> } - ) - } + fun testShowBillingPaymentAvailable() = + composeExtension.use { + // Arrange + val mockPaymentProduct: PaymentProduct = mockk() + every { mockPaymentProduct.price } returns ProductPrice("$10") + every { mockPaymentProduct.status } returns null + setContentWithTheme { + OutOfTimeScreen( + uiState = + OutOfTimeUiState( + showSitePayment = true, + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = { _ -> } + ) + } - // Assert - composeTestRule.onNodeWithText("Add 30 days time ($10)").assertExists() - } + // Assert + 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( - uiState = - OutOfTimeUiState( - showSitePayment = true, - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - ) - } + fun testShowPendingPayment() = + composeExtension.use { + // Arrange + val mockPaymentProduct: PaymentProduct = mockk() + every { mockPaymentProduct.price } returns ProductPrice("$10") + every { mockPaymentProduct.status } returns PaymentStatus.PENDING + setContentWithTheme { + OutOfTimeScreen( + uiState = + OutOfTimeUiState( + showSitePayment = true, + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + ) + } - // Assert - composeTestRule.onNodeWithText("Google Play payment pending").assertExists() - } + // Assert + 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 - val mockNavigateToVerificationPending: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - OutOfTimeScreen( - uiState = - OutOfTimeUiState( - showSitePayment = true, - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - navigateToVerificationPendingDialog = mockNavigateToVerificationPending - ) - } + fun testShowPendingPaymentInfoDialog() = + composeExtension.use { + // Arrange + val mockPaymentProduct: PaymentProduct = mockk() + every { mockPaymentProduct.price } returns ProductPrice("$10") + every { mockPaymentProduct.status } returns PaymentStatus.PENDING + val mockNavigateToVerificationPending: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + OutOfTimeScreen( + uiState = + OutOfTimeUiState( + showSitePayment = true, + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + navigateToVerificationPendingDialog = mockNavigateToVerificationPending + ) + } - // Act - composeTestRule.onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).performClick() - composeTestRule.onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).assertExists() + // Act + onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).performClick() + onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).assertExists() - verify(exactly = 1) { mockNavigateToVerificationPending.invoke() } - } + // Assert + verify(exactly = 1) { mockNavigateToVerificationPending.invoke() } + } @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( - uiState = - OutOfTimeUiState( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), - showSitePayment = true, - ) - ) - } + fun testShowVerificationInProgress() = + composeExtension.use { + // Arrange + val mockPaymentProduct: PaymentProduct = mockk() + every { mockPaymentProduct.price } returns ProductPrice("$10") + every { mockPaymentProduct.status } returns PaymentStatus.VERIFICATION_IN_PROGRESS + setContentWithTheme { + OutOfTimeScreen( + uiState = + OutOfTimeUiState( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), + showSitePayment = true, + ) + ) + } - // Assert - composeTestRule.onNodeWithText("Verifying purchase").assertExists() - } + // Assert + onNodeWithText("Verifying purchase").assertExists() + } @Test - fun testOnPurchaseBillingProductClick() { - // Arrange - val clickHandler: (ProductId) -> 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( - uiState = - OutOfTimeUiState( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), - showSitePayment = true, - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = clickHandler - ) - } + fun testOnPurchaseBillingProductClick() = + composeExtension.use { + // Arrange + val clickHandler: (ProductId) -> 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 + setContentWithTheme { + OutOfTimeScreen( + uiState = + OutOfTimeUiState( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), + showSitePayment = true, + ), + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = clickHandler + ) + } - // Act - composeTestRule.onNodeWithText("Add 30 days time ($10)").performClick() + // Act + onNodeWithText("Add 30 days time ($10)").performClick() - // Assert - verify { clickHandler(ProductId("PRODUCT_ID")) } - } + // Assert + verify { clickHandler(ProductId("PRODUCT_ID")) } + } } diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/RedeemVoucherDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/RedeemVoucherDialogTest.kt index 5a51a8f885..9a2a9ce86d 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/RedeemVoucherDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/RedeemVoucherDialogTest.kt @@ -1,10 +1,11 @@ package net.mullvad.mullvadvpn.compose.screen -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.mockk import io.mockk.mockkObject import io.mockk.verify @@ -14,131 +15,138 @@ import net.mullvad.mullvadvpn.compose.state.VoucherDialogState import net.mullvad.mullvadvpn.compose.state.VoucherDialogUiState import net.mullvad.mullvadvpn.compose.test.VOUCHER_INPUT_TEST_TAG import net.mullvad.mullvadvpn.util.VoucherRegexHelper -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +@OptIn(ExperimentalTestApi::class) class RedeemVoucherDialogTest { - @get:Rule val composeTestRule = createComposeRule() + @JvmField @RegisterExtension val composeExtension = createComposeExtension() - @Before + @BeforeEach fun setup() { mockkObject(VoucherRegexHelper) } @Test - fun testDismissDialog() { - // Arrange - val mockedClickHandler: (Boolean) -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - RedeemVoucherDialog( - uiState = VoucherDialogUiState.INITIAL, - onVoucherInputChange = {}, - onRedeem = {}, - onDismiss = mockedClickHandler - ) - } + fun testDismissDialog() = + composeExtension.use { + // Arrange + val mockedClickHandler: (Boolean) -> Unit = mockk(relaxed = true) + setContentWithTheme { + RedeemVoucherDialog( + uiState = VoucherDialogUiState.INITIAL, + onVoucherInputChange = {}, + onRedeem = {}, + onDismiss = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithText(CANCEL_BUTTON_TEXT).performClick() + // Act + onNodeWithText(CANCEL_BUTTON_TEXT).performClick() - // Assert - verify { mockedClickHandler.invoke(false) } - } + // Assert + verify { mockedClickHandler.invoke(false) } + } @Test - fun testDismissDialogAfterSuccessfulRedeem() { - // Arrange - val mockedClickHandler: (Boolean) -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - RedeemVoucherDialog( - uiState = - VoucherDialogUiState(voucherViewModelState = VoucherDialogState.Success(0)), - onVoucherInputChange = {}, - onRedeem = {}, - onDismiss = mockedClickHandler - ) - } + fun testDismissDialogAfterSuccessfulRedeem() = + composeExtension.use { + // Arrange + val mockedClickHandler: (Boolean) -> Unit = mockk(relaxed = true) + setContentWithTheme { + RedeemVoucherDialog( + uiState = + VoucherDialogUiState(voucherViewModelState = VoucherDialogState.Success(0)), + onVoucherInputChange = {}, + onRedeem = {}, + onDismiss = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithText(GOT_IT_BUTTON_TEXT).performClick() + // Act + onNodeWithText(GOT_IT_BUTTON_TEXT).performClick() - // Assert - verify { mockedClickHandler.invoke(true) } - } + // Assert + verify { mockedClickHandler.invoke(true) } + } @Test - fun testInsertVoucher() { - // Arrange - val mockedClickHandler: (String) -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - RedeemVoucherDialog( - uiState = VoucherDialogUiState(), - onVoucherInputChange = mockedClickHandler, - onRedeem = {}, - onDismiss = {} - ) - } + fun testInsertVoucher() = + composeExtension.use { + // Arrange + val mockedClickHandler: (String) -> Unit = mockk(relaxed = true) + setContentWithTheme { + RedeemVoucherDialog( + uiState = VoucherDialogUiState(), + onVoucherInputChange = mockedClickHandler, + onRedeem = {}, + onDismiss = {} + ) + } - // Act - composeTestRule.onNodeWithTag(VOUCHER_INPUT_TEST_TAG).performTextInput(DUMMY_VOUCHER) + // Act + onNodeWithTag(VOUCHER_INPUT_TEST_TAG).performTextInput(DUMMY_VOUCHER) - // Assert - verify { mockedClickHandler.invoke(DUMMY_VOUCHER) } - } + // Assert + verify { mockedClickHandler.invoke(DUMMY_VOUCHER) } + } @Test - fun testVerifyingState() { - // Arrange - composeTestRule.setContentWithTheme { - RedeemVoucherDialog( - uiState = - VoucherDialogUiState(voucherViewModelState = VoucherDialogState.Verifying), - onVoucherInputChange = {}, - onRedeem = {}, - onDismiss = {} - ) - } + fun testVerifyingState() = + composeExtension.use { + // Arrange + setContentWithTheme { + RedeemVoucherDialog( + uiState = + VoucherDialogUiState(voucherViewModelState = VoucherDialogState.Verifying), + onVoucherInputChange = {}, + onRedeem = {}, + onDismiss = {} + ) + } - // Assert - composeTestRule.onNodeWithText("Verifying voucher…").assertExists() - } + // Assert + onNodeWithText("Verifying voucher…").assertExists() + } @Test - fun testSuccessState() { - // Arrange - composeTestRule.setContentWithTheme { - RedeemVoucherDialog( - uiState = - VoucherDialogUiState(voucherViewModelState = VoucherDialogState.Success(0)), - onVoucherInputChange = {}, - onRedeem = {}, - onDismiss = {} - ) - } + fun testSuccessState() = + composeExtension.use { + // Arrange + setContentWithTheme { + RedeemVoucherDialog( + uiState = + VoucherDialogUiState(voucherViewModelState = VoucherDialogState.Success(0)), + onVoucherInputChange = {}, + onRedeem = {}, + onDismiss = {} + ) + } - // Assert - composeTestRule.onNodeWithText("Voucher was successfully redeemed.").assertExists() - } + // Assert + onNodeWithText("Voucher was successfully redeemed.").assertExists() + } @Test - fun testErrorState() { - // Arrange - composeTestRule.setContentWithTheme { - RedeemVoucherDialog( - uiState = - VoucherDialogUiState( - voucherViewModelState = VoucherDialogState.Error(ERROR_MESSAGE) - ), - onVoucherInputChange = {}, - onRedeem = {}, - onDismiss = {} - ) - } + fun testErrorState() = + composeExtension.use { + // Arrange + setContentWithTheme { + RedeemVoucherDialog( + uiState = + VoucherDialogUiState( + voucherViewModelState = VoucherDialogState.Error(ERROR_MESSAGE) + ), + onVoucherInputChange = {}, + onRedeem = {}, + onDismiss = {} + ) + } - // Assert - composeTestRule.onNodeWithText(ERROR_MESSAGE).assertExists() - } + // Assert + onNodeWithText(ERROR_MESSAGE).assertExists() + } companion object { private const val REDEEM_BUTTON_TEXT = "Redeem" diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreenTest.kt index ea1d261689..fbc8b046fd 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreenTest.kt @@ -1,9 +1,10 @@ package net.mullvad.mullvadvpn.compose.screen -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performTextInput +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -19,49 +20,51 @@ import net.mullvad.mullvadvpn.model.RelayListCountry import net.mullvad.mullvadvpn.model.WireguardEndpointData import net.mullvad.mullvadvpn.model.WireguardRelayEndpointData import net.mullvad.mullvadvpn.relaylist.toRelayCountries -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +@OptIn(ExperimentalTestApi::class) class SelectLocationScreenTest { - @get:Rule val composeTestRule = createComposeRule() + @JvmField @RegisterExtension val composeExtension = createComposeExtension() - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) } @Test - fun testDefaultState() { - // Arrange - composeTestRule.setContentWithTheme { - SelectLocationScreen( - uiState = SelectLocationUiState.Loading, - ) - } + fun testDefaultState() = + composeExtension.use { + // Arrange + setContentWithTheme { + SelectLocationScreen( + uiState = SelectLocationUiState.Loading, + ) + } - // Assert - composeTestRule.apply { onNodeWithTag(CIRCULAR_PROGRESS_INDICATOR).assertExists() } - } + // Assert + onNodeWithTag(CIRCULAR_PROGRESS_INDICATOR).assertExists() + } @Test - fun testShowRelayListState() { - // Arrange - composeTestRule.setContentWithTheme { - SelectLocationScreen( - uiState = - SelectLocationUiState.ShowData( - countries = DUMMY_RELAY_COUNTRIES, - selectedRelay = null, - selectedOwnership = null, - selectedProvidersCount = 0, - searchTerm = "" - ), - ) - } + fun testShowRelayListState() = + composeExtension.use { + // Arrange + setContentWithTheme { + SelectLocationScreen( + uiState = + SelectLocationUiState.ShowData( + countries = DUMMY_RELAY_COUNTRIES, + selectedRelay = null, + selectedOwnership = null, + selectedProvidersCount = 0, + searchTerm = "" + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("Relay Country 1").assertExists() onNodeWithText("Relay City 1").assertDoesNotExist() onNodeWithText("Relay host 1").assertDoesNotExist() @@ -69,37 +72,36 @@ class SelectLocationScreenTest { onNodeWithText("Relay City 2").assertDoesNotExist() onNodeWithText("Relay host 2").assertDoesNotExist() } - } @Test - fun testShowRelayListStateSelected() { - val updatedDummyList = - DUMMY_RELAY_COUNTRIES.let { - val cities = it[0].cities.toMutableList() - val city = cities.removeAt(0) - cities.add(0, city.copy(expanded = true)) + fun testShowRelayListStateSelected() = + composeExtension.use { + val updatedDummyList = + DUMMY_RELAY_COUNTRIES.let { + val cities = it[0].cities.toMutableList() + val city = cities.removeAt(0) + cities.add(0, city.copy(expanded = true)) - val mutableRelayList = it.toMutableList() - mutableRelayList[0] = it[0].copy(expanded = true, cities = cities.toList()) - mutableRelayList - } + val mutableRelayList = it.toMutableList() + mutableRelayList[0] = it[0].copy(expanded = true, cities = cities.toList()) + mutableRelayList + } - // Arrange - composeTestRule.setContentWithTheme { - SelectLocationScreen( - uiState = - SelectLocationUiState.ShowData( - countries = updatedDummyList, - selectedRelay = updatedDummyList[0].cities[0].relays[0], - selectedOwnership = null, - selectedProvidersCount = 0, - searchTerm = "" - ), - ) - } + // Arrange + setContentWithTheme { + SelectLocationScreen( + uiState = + SelectLocationUiState.ShowData( + countries = updatedDummyList, + selectedRelay = updatedDummyList[0].cities[0].relays[0], + selectedOwnership = null, + selectedProvidersCount = 0, + searchTerm = "" + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("Relay Country 1").assertExists() onNodeWithText("Relay City 1").assertExists() onNodeWithText("Relay host 1").assertExists() @@ -107,59 +109,58 @@ class SelectLocationScreenTest { onNodeWithText("Relay City 2").assertDoesNotExist() onNodeWithText("Relay host 2").assertDoesNotExist() } - } @Test - fun testSearchInput() { - // Arrange - val mockedSearchTermInput: (String) -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - SelectLocationScreen( - uiState = - SelectLocationUiState.ShowData( - countries = emptyList(), - selectedRelay = null, - selectedOwnership = null, - selectedProvidersCount = 0, - searchTerm = "" - ), - onSearchTermInput = mockedSearchTermInput - ) - } - val mockSearchString = "SEARCH" + fun testSearchInput() = + composeExtension.use { + // Arrange + val mockedSearchTermInput: (String) -> Unit = mockk(relaxed = true) + setContentWithTheme { + SelectLocationScreen( + uiState = + SelectLocationUiState.ShowData( + countries = emptyList(), + selectedRelay = null, + selectedOwnership = null, + selectedProvidersCount = 0, + searchTerm = "" + ), + onSearchTermInput = mockedSearchTermInput + ) + } + val mockSearchString = "SEARCH" - // Act - composeTestRule.apply { onNodeWithText("Search for...").performTextInput(mockSearchString) } + // Act + onNodeWithText("Search for...").performTextInput(mockSearchString) - // Assert - verify { mockedSearchTermInput.invoke(mockSearchString) } - } + // Assert + verify { mockedSearchTermInput.invoke(mockSearchString) } + } @Test - fun testSearchTermNotFound() { - // Arrange - val mockedSearchTermInput: (String) -> Unit = mockk(relaxed = true) - val mockSearchString = "SEARCH" - composeTestRule.setContentWithTheme { - SelectLocationScreen( - uiState = - SelectLocationUiState.ShowData( - countries = emptyList(), - selectedRelay = null, - selectedOwnership = null, - selectedProvidersCount = 0, - searchTerm = mockSearchString - ), - onSearchTermInput = mockedSearchTermInput - ) - } + fun testSearchTermNotFound() = + composeExtension.use { + // Arrange + val mockedSearchTermInput: (String) -> Unit = mockk(relaxed = true) + val mockSearchString = "SEARCH" + setContentWithTheme { + SelectLocationScreen( + uiState = + SelectLocationUiState.ShowData( + countries = emptyList(), + selectedRelay = null, + selectedOwnership = null, + selectedProvidersCount = 0, + searchTerm = mockSearchString + ), + onSearchTermInput = mockedSearchTermInput + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("No result for $mockSearchString.", substring = true).assertExists() onNodeWithText("Try a different search", substring = true).assertExists() } - } companion object { private val DUMMY_RELAY_1 = diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreenTest.kt index f5daf29bb2..1c47082b14 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreenTest.kt @@ -1,66 +1,66 @@ package net.mullvad.mullvadvpn.compose.screen import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.MockKAnnotations import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.compose.state.SettingsUiState -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +@OptIn(ExperimentalTestApi::class) class SettingsScreenTest { - @get:Rule val composeTestRule = createComposeRule() + @JvmField @RegisterExtension val composeExtension = createComposeExtension() - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) } @Test @OptIn(ExperimentalMaterial3Api::class) - fun testLoggedInState() { - // Arrange - composeTestRule.setContentWithTheme { - SettingsScreen( - uiState = - SettingsUiState( - appVersion = "", - isLoggedIn = true, - isUpdateAvailable = true, - isPlayBuild = false - ), - ) - } - // Assert - composeTestRule.apply { + fun testLoggedInState() = + composeExtension.use { + // Arrange + setContentWithTheme { + SettingsScreen( + uiState = + SettingsUiState( + appVersion = "", + isLoggedIn = true, + isUpdateAvailable = true, + isPlayBuild = false + ), + ) + } + // Assert onNodeWithText("VPN settings").assertExists() onNodeWithText("Split tunneling").assertExists() onNodeWithText("App version").assertExists() } - } @Test @OptIn(ExperimentalMaterial3Api::class) - fun testLoggedOutState() { - // Arrange - composeTestRule.setContentWithTheme { - SettingsScreen( - uiState = - SettingsUiState( - appVersion = "", - isLoggedIn = false, - isUpdateAvailable = true, - isPlayBuild = false - ), - ) - } - // Assert - composeTestRule.apply { + fun testLoggedOutState() = + composeExtension.use { + // Arrange + setContentWithTheme { + SettingsScreen( + uiState = + SettingsUiState( + appVersion = "", + isLoggedIn = false, + isUpdateAvailable = true, + isPlayBuild = false + ), + ) + } + // Assert onNodeWithText("VPN settings").assertDoesNotExist() onNodeWithText("Split tunneling").assertDoesNotExist() onNodeWithText("App version").assertExists() } - } } diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreenTest.kt index 7f4ee36d47..dc1362ad37 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreenTest.kt @@ -1,9 +1,10 @@ package net.mullvad.mullvadvpn.compose.screen import android.graphics.Bitmap -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.mockk @@ -14,15 +15,16 @@ import net.mullvad.mullvadvpn.applist.ApplicationsIconManager import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.compose.state.SplitTunnelingUiState import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules import org.koin.dsl.module +@OptIn(ExperimentalTestApi::class) class SplitTunnelingScreenTest { - @get:Rule val composeTestRule = createComposeRule() + @JvmField @RegisterExtension val composeExtension = createComposeExtension() private val mockBitmap: Bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888) private val testModule = module { @@ -33,7 +35,7 @@ class SplitTunnelingScreenTest { } } - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) loadKoinModules(testModule) @@ -46,42 +48,47 @@ class SplitTunnelingScreenTest { } @Test - fun testLoadingState() { - // Arrange - composeTestRule.setContentWithTheme { - SplitTunnelingScreen(uiState = SplitTunnelingUiState.Loading) - } + fun testLoadingState() = + composeExtension.use { + // Arrange + setContentWithTheme { SplitTunnelingScreen(uiState = SplitTunnelingUiState.Loading) } - // Assert - composeTestRule.apply { + // Assert onNodeWithText(TITLE).assertExists() onNodeWithText(DESCRIPTION).assertExists() onNodeWithText(EXCLUDED_APPLICATIONS).assertDoesNotExist() onNodeWithText(SHOW_SYSTEM_APPS).assertDoesNotExist() onNodeWithText(ALL_APPLICATIONS).assertDoesNotExist() } - } @Test - fun testListDisplayed() { - // Arrange - val excludedApp = - AppData(packageName = EXCLUDED_APP_PACKAGE_NAME, iconRes = 0, name = EXCLUDED_APP_NAME) - val includedApp = - AppData(packageName = INCLUDED_APP_PACKAGE_NAME, iconRes = 0, name = INCLUDED_APP_NAME) - composeTestRule.setContentWithTheme { - SplitTunnelingScreen( - uiState = - SplitTunnelingUiState.ShowAppList( - excludedApps = listOf(excludedApp), - includedApps = listOf(includedApp), - showSystemApps = false - ) - ) - } + fun testListDisplayed() = + composeExtension.use { + // Arrange + val excludedApp = + AppData( + packageName = EXCLUDED_APP_PACKAGE_NAME, + iconRes = 0, + name = EXCLUDED_APP_NAME + ) + val includedApp = + AppData( + packageName = INCLUDED_APP_PACKAGE_NAME, + iconRes = 0, + name = INCLUDED_APP_NAME + ) + setContentWithTheme { + SplitTunnelingScreen( + uiState = + SplitTunnelingUiState.ShowAppList( + excludedApps = listOf(excludedApp), + includedApps = listOf(includedApp), + showSystemApps = false + ) + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText(TITLE).assertExists() onNodeWithText(DESCRIPTION).assertExists() onNodeWithText(EXCLUDED_APPLICATIONS).assertExists() @@ -90,26 +97,29 @@ class SplitTunnelingScreenTest { onNodeWithText(ALL_APPLICATIONS).assertExists() onNodeWithText(INCLUDED_APP_NAME).assertExists() } - } @Test - fun testNoExcludedApps() { - // Arrange - val includedApp = - AppData(packageName = INCLUDED_APP_PACKAGE_NAME, iconRes = 0, name = INCLUDED_APP_NAME) - composeTestRule.setContentWithTheme { - SplitTunnelingScreen( - uiState = - SplitTunnelingUiState.ShowAppList( - excludedApps = emptyList(), - includedApps = listOf(includedApp), - showSystemApps = false - ) - ) - } + fun testNoExcludedApps() = + composeExtension.use { + // Arrange + val includedApp = + AppData( + packageName = INCLUDED_APP_PACKAGE_NAME, + iconRes = 0, + name = INCLUDED_APP_NAME + ) + setContentWithTheme { + SplitTunnelingScreen( + uiState = + SplitTunnelingUiState.ShowAppList( + excludedApps = emptyList(), + includedApps = listOf(includedApp), + showSystemApps = false + ) + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText(TITLE).assertExists() onNodeWithText(DESCRIPTION).assertExists() onNodeWithText(EXCLUDED_APPLICATIONS).assertDoesNotExist() @@ -118,88 +128,114 @@ class SplitTunnelingScreenTest { onNodeWithText(ALL_APPLICATIONS).assertExists() onNodeWithText(INCLUDED_APP_NAME).assertExists() } - } @Test - fun testClickIncludedItem() { - // Arrange - val excludedApp = - AppData(packageName = EXCLUDED_APP_PACKAGE_NAME, iconRes = 0, name = EXCLUDED_APP_NAME) - val includedApp = - AppData(packageName = INCLUDED_APP_PACKAGE_NAME, iconRes = 0, name = INCLUDED_APP_NAME) - val mockedClickHandler: (String) -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - SplitTunnelingScreen( - uiState = - SplitTunnelingUiState.ShowAppList( - excludedApps = listOf(excludedApp), - includedApps = listOf(includedApp), - showSystemApps = false - ), - onExcludeAppClick = mockedClickHandler - ) - } + fun testClickIncludedItem() = + composeExtension.use { + // Arrange + val excludedApp = + AppData( + packageName = EXCLUDED_APP_PACKAGE_NAME, + iconRes = 0, + name = EXCLUDED_APP_NAME + ) + val includedApp = + AppData( + packageName = INCLUDED_APP_PACKAGE_NAME, + iconRes = 0, + name = INCLUDED_APP_NAME + ) + val mockedClickHandler: (String) -> Unit = mockk(relaxed = true) + setContentWithTheme { + SplitTunnelingScreen( + uiState = + SplitTunnelingUiState.ShowAppList( + excludedApps = listOf(excludedApp), + includedApps = listOf(includedApp), + showSystemApps = false + ), + onExcludeAppClick = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithText(INCLUDED_APP_NAME).performClick() + // Act + onNodeWithText(INCLUDED_APP_NAME).performClick() - // Assert - verify { mockedClickHandler.invoke(INCLUDED_APP_PACKAGE_NAME) } - } + // Assert + verify { mockedClickHandler.invoke(INCLUDED_APP_PACKAGE_NAME) } + } @Test - fun testClickExcludedItem() { - // Arrange - val excludedApp = - AppData(packageName = EXCLUDED_APP_PACKAGE_NAME, iconRes = 0, name = EXCLUDED_APP_NAME) - val includedApp = - AppData(packageName = INCLUDED_APP_PACKAGE_NAME, iconRes = 0, name = INCLUDED_APP_NAME) - val mockedClickHandler: (String) -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - SplitTunnelingScreen( - uiState = - SplitTunnelingUiState.ShowAppList( - excludedApps = listOf(excludedApp), - includedApps = listOf(includedApp), - showSystemApps = false - ), - onIncludeAppClick = mockedClickHandler - ) - } + fun testClickExcludedItem() = + composeExtension.use { + // Arrange + val excludedApp = + AppData( + packageName = EXCLUDED_APP_PACKAGE_NAME, + iconRes = 0, + name = EXCLUDED_APP_NAME + ) + val includedApp = + AppData( + packageName = INCLUDED_APP_PACKAGE_NAME, + iconRes = 0, + name = INCLUDED_APP_NAME + ) + val mockedClickHandler: (String) -> Unit = mockk(relaxed = true) + setContentWithTheme { + SplitTunnelingScreen( + uiState = + SplitTunnelingUiState.ShowAppList( + excludedApps = listOf(excludedApp), + includedApps = listOf(includedApp), + showSystemApps = false + ), + onIncludeAppClick = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithText(EXCLUDED_APP_NAME).performClick() + // Act + onNodeWithText(EXCLUDED_APP_NAME).performClick() - // Assert - verify { mockedClickHandler.invoke(EXCLUDED_APP_PACKAGE_NAME) } - } + // Assert + verify { mockedClickHandler.invoke(EXCLUDED_APP_PACKAGE_NAME) } + } @Test - fun testClickShowSystemApps() { - // Arrange - val excludedApp = - AppData(packageName = EXCLUDED_APP_PACKAGE_NAME, iconRes = 0, name = EXCLUDED_APP_NAME) - val includedApp = - AppData(packageName = INCLUDED_APP_PACKAGE_NAME, iconRes = 0, name = INCLUDED_APP_NAME) - val mockedClickHandler: (Boolean) -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - SplitTunnelingScreen( - uiState = - SplitTunnelingUiState.ShowAppList( - excludedApps = listOf(excludedApp), - includedApps = listOf(includedApp), - showSystemApps = false - ), - onShowSystemAppsClick = mockedClickHandler - ) - } + fun testClickShowSystemApps() = + composeExtension.use { + // Arrange + val excludedApp = + AppData( + packageName = EXCLUDED_APP_PACKAGE_NAME, + iconRes = 0, + name = EXCLUDED_APP_NAME + ) + val includedApp = + AppData( + packageName = INCLUDED_APP_PACKAGE_NAME, + iconRes = 0, + name = INCLUDED_APP_NAME + ) + val mockedClickHandler: (Boolean) -> Unit = mockk(relaxed = true) + setContentWithTheme { + SplitTunnelingScreen( + uiState = + SplitTunnelingUiState.ShowAppList( + excludedApps = listOf(excludedApp), + includedApps = listOf(includedApp), + showSystemApps = false + ), + onShowSystemAppsClick = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithText(SHOW_SYSTEM_APPS).performClick() + // Act + onNodeWithText(SHOW_SYSTEM_APPS).performClick() - // Assert - verify { mockedClickHandler.invoke(true) } - } + // Assert + verify { mockedClickHandler.invoke(true) } + } companion object { private const val EXCLUDED_APP_PACKAGE_NAME = "excluded-pkg" diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt index 1ca2b3e1f7..74c73553b2 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt @@ -1,12 +1,13 @@ package net.mullvad.mullvadvpn.compose.screen +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.hasTestTag -import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollToNode +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -25,483 +26,483 @@ import net.mullvad.mullvadvpn.model.PortRange import net.mullvad.mullvadvpn.model.QuantumResistantState import net.mullvad.mullvadvpn.onNodeWithTagAndText import net.mullvad.mullvadvpn.viewmodel.CustomDnsItem -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +@OptIn(ExperimentalTestApi::class) class VpnSettingsScreenTest { - @get:Rule val composeTestRule = createComposeRule() + @JvmField @RegisterExtension val composeExtension = createComposeExtension() - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) } @Test - fun testDefaultState() { - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = VpnSettingsUiState.createDefault(), - ) - } + fun testDefaultState() = + composeExtension.use { + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = VpnSettingsUiState.createDefault(), + ) + } - composeTestRule.apply { onNodeWithText("Auto-connect").assertExists() } + apply { onNodeWithText("Auto-connect").assertExists() } - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) - // Assert - composeTestRule.apply { - onNodeWithText("WireGuard MTU").assertExists() - onNodeWithText("Default").assertExists() + // Assert + apply { + onNodeWithText("WireGuard MTU").assertExists() + onNodeWithText("Default").assertExists() + } } - } @Test - fun testMtuCustomValue() { - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = VpnSettingsUiState.createDefault(mtu = VALID_DUMMY_MTU_VALUE), - ) - } + fun testMtuCustomValue() = + composeExtension.use { + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = VpnSettingsUiState.createDefault(mtu = VALID_DUMMY_MTU_VALUE), + ) + } - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) - // Assert - composeTestRule.onNodeWithText(VALID_DUMMY_MTU_VALUE).assertExists() - } + // Assert + onNodeWithText(VALID_DUMMY_MTU_VALUE).assertExists() + } @Test - fun testCustomDnsAddressesAndAddButtonVisibleWhenCustomDnsEnabled() { - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = - VpnSettingsUiState.createDefault( - isCustomDnsEnabled = true, - customDnsItems = - listOf( - CustomDnsItem(address = DUMMY_DNS_ADDRESS, false), - CustomDnsItem(address = DUMMY_DNS_ADDRESS_2, false), - CustomDnsItem(address = DUMMY_DNS_ADDRESS_3, false) - ) - ), - ) - } + fun testCustomDnsAddressesAndAddButtonVisibleWhenCustomDnsEnabled() = + composeExtension.use { + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.createDefault( + isCustomDnsEnabled = true, + customDnsItems = + listOf( + CustomDnsItem(address = DUMMY_DNS_ADDRESS, false), + CustomDnsItem(address = DUMMY_DNS_ADDRESS_2, false), + CustomDnsItem(address = DUMMY_DNS_ADDRESS_3, false) + ) + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText(DUMMY_DNS_ADDRESS).assertExists() onNodeWithText(DUMMY_DNS_ADDRESS_2).assertExists() onNodeWithText(DUMMY_DNS_ADDRESS_3).assertExists() onNodeWithText("Add a server").assertExists() } - } @Test - fun testCustomDnsAddressesAndAddButtonNotVisibleWhenCustomDnsDisabled() { - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = - VpnSettingsUiState.createDefault( - isCustomDnsEnabled = false, - customDnsItems = listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, false)) - ), - ) + fun testCustomDnsAddressesAndAddButtonNotVisibleWhenCustomDnsDisabled() = + composeExtension.use { + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.createDefault( + isCustomDnsEnabled = false, + customDnsItems = + listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, false)) + ), + ) + } + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) + // Assert + onNodeWithText(DUMMY_DNS_ADDRESS).assertDoesNotExist() + onNodeWithText("Add a server").assertDoesNotExist() } - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) - // Assert - composeTestRule.onNodeWithText(DUMMY_DNS_ADDRESS).assertDoesNotExist() - composeTestRule.onNodeWithText("Add a server").assertDoesNotExist() - } @Test - fun testLanWarningNotShownWhenLanTrafficEnabledAndLocalAddressIsUsed() { - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = - VpnSettingsUiState.createDefault( - isCustomDnsEnabled = true, - isLocalNetworkSharingEnabled = true, - customDnsItems = - listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)) - ), - ) - } + fun testLanWarningNotShownWhenLanTrafficEnabledAndLocalAddressIsUsed() = + composeExtension.use { + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.createDefault( + isCustomDnsEnabled = true, + isLocalNetworkSharingEnabled = true, + customDnsItems = + listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)) + ), + ) + } - // Assert - composeTestRule.onNodeWithContentDescription(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() - } + // Assert + onNodeWithContentDescription(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() + } @Test - fun testLanWarningNotShowedWhenLanTrafficDisabledAndLocalAddressIsNotUsed() { - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = - VpnSettingsUiState.createDefault( - isCustomDnsEnabled = true, - customDnsItems = - listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)) - ), - ) - } + fun testLanWarningNotShowedWhenLanTrafficDisabledAndLocalAddressIsNotUsed() = + composeExtension.use { + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.createDefault( + isCustomDnsEnabled = true, + customDnsItems = + listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)) + ), + ) + } - // Assert - composeTestRule.onNodeWithContentDescription(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() - } + // Assert + onNodeWithContentDescription(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() + } @Test - fun testLanWarningNotShowedWhenLanTrafficEnabledAndLocalAddressIsNotUsed() { - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = - VpnSettingsUiState.createDefault( - isCustomDnsEnabled = true, - customDnsItems = - listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)) - ), - ) - } + fun testLanWarningNotShowedWhenLanTrafficEnabledAndLocalAddressIsNotUsed() = + composeExtension.use { + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.createDefault( + isCustomDnsEnabled = true, + customDnsItems = + listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)) + ), + ) + } - // Assert - composeTestRule.onNodeWithContentDescription(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() - } + // Assert + onNodeWithContentDescription(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() + } @Test - fun testLanWarningShowedWhenAllowLanEnabledAndLocalDnsAddressIsUsed() { - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = - VpnSettingsUiState.createDefault( - isCustomDnsEnabled = true, - customDnsItems = - listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)) - ), - ) - } + fun testLanWarningShowedWhenAllowLanEnabledAndLocalDnsAddressIsUsed() = + composeExtension.use { + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.createDefault( + isCustomDnsEnabled = true, + customDnsItems = + listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)) + ), + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithContentDescription(LOCAL_DNS_SERVER_WARNING).assertExists() } - } @Test - fun testShowSelectedTunnelQuantumOption() { - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = - VpnSettingsUiState.createDefault(quantumResistant = QuantumResistantState.On), - ) - } - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode(hasTestTag(LAZY_LIST_QUANTUM_ITEM_OFF_TEST_TAG)) + fun testShowSelectedTunnelQuantumOption() = + composeExtension.use { + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.createDefault( + quantumResistant = QuantumResistantState.On + ), + ) + } + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_QUANTUM_ITEM_OFF_TEST_TAG)) - // Assert - composeTestRule - .onNodeWithTagAndText(testTag = LAZY_LIST_QUANTUM_ITEM_ON_TEST_TAG, text = "On") - .assertExists() - } + // Assert + onNodeWithTagAndText(testTag = LAZY_LIST_QUANTUM_ITEM_ON_TEST_TAG, text = "On") + .assertExists() + } @Test - fun testSelectTunnelQuantumOption() { - // Arrange - val mockSelectQuantumResistantSettingListener: (QuantumResistantState) -> Unit = - mockk(relaxed = true) - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = - VpnSettingsUiState.createDefault( - quantumResistant = QuantumResistantState.Auto, - ), - onSelectQuantumResistanceSetting = mockSelectQuantumResistantSettingListener - ) - } - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode(hasTestTag(LAZY_LIST_QUANTUM_ITEM_OFF_TEST_TAG)) + fun testSelectTunnelQuantumOption() = + composeExtension.use { + // Arrange + val mockSelectQuantumResistantSettingListener: (QuantumResistantState) -> Unit = + mockk(relaxed = true) + setContentWithTheme { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.createDefault( + quantumResistant = QuantumResistantState.Auto, + ), + onSelectQuantumResistanceSetting = mockSelectQuantumResistantSettingListener, + ) + } + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_QUANTUM_ITEM_OFF_TEST_TAG)) - // Assert - composeTestRule - .onNodeWithTagAndText(testTag = LAZY_LIST_QUANTUM_ITEM_ON_TEST_TAG, text = "On") - .performClick() - verify(exactly = 1) { - mockSelectQuantumResistantSettingListener.invoke(QuantumResistantState.On) + // Assert + onNodeWithTagAndText(testTag = LAZY_LIST_QUANTUM_ITEM_ON_TEST_TAG, text = "On") + .performClick() + verify(exactly = 1) { + mockSelectQuantumResistantSettingListener.invoke(QuantumResistantState.On) + } } - } @Test - fun testShowWireguardPortOptions() { - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = - VpnSettingsUiState.createDefault( - selectedWireguardPort = Constraint.Only(Port(53)) - ), - ) - } + fun testShowWireguardPortOptions() = + composeExtension.use { + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.createDefault( + selectedWireguardPort = Constraint.Only(Port(53)) + ), + ) + } - // Act - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode( - hasTestTag(String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 53)) - ) + // Act + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode( + hasTestTag(String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 53)) + ) - // Assert - composeTestRule - .onNodeWithTagAndText( - testTag = String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 51820), - text = "51820" - ) - .assertExists() - } + // Assert + onNodeWithTagAndText( + testTag = String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 51820), + text = "51820" + ) + .assertExists() + } @Test - fun testSelectWireguardPortOption() { - // Arrange - val mockSelectWireguardPortSelectionListener: (Constraint<Port>) -> Unit = - mockk(relaxed = true) - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = - VpnSettingsUiState.createDefault( - selectedWireguardPort = Constraint.Only(Port(53)) - ), - onWireguardPortSelected = mockSelectWireguardPortSelectionListener - ) - } + fun testSelectWireguardPortOption() = + composeExtension.use { + // Arrange + val mockSelectWireguardPortSelectionListener: (Constraint<Port>) -> Unit = + mockk(relaxed = true) + setContentWithTheme { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.createDefault( + selectedWireguardPort = Constraint.Only(Port(53)) + ), + onWireguardPortSelected = mockSelectWireguardPortSelectionListener, + ) + } - // Act - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode( - hasTestTag(String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 53)) - ) - composeTestRule - .onNodeWithTagAndText( - testTag = String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 51820), - text = "51820" - ) - .performClick() + // Act + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode( + hasTestTag(String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 53)) + ) + onNodeWithTagAndText( + testTag = String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 51820), + text = "51820" + ) + .performClick() - // Assert - verify(exactly = 1) { - mockSelectWireguardPortSelectionListener.invoke(Constraint.Only(Port(51820))) + // Assert + verify(exactly = 1) { + mockSelectWireguardPortSelectionListener.invoke(Constraint.Only(Port(51820))) + } } - } @Test - fun testShowWireguardCustomPort() { - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = - VpnSettingsUiState.createDefault( - customWireguardPort = Constraint.Only(Port(4000)) - ), - ) - } + fun testShowWireguardCustomPort() = + composeExtension.use { + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.createDefault( + customWireguardPort = Constraint.Only(Port(4000)) + ), + ) + } - // Act - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG)) + // Act + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG)) - // Assert - composeTestRule.onNodeWithText("4000").assertExists() - } + // Assert + onNodeWithText("4000").assertExists() + } @Test - fun testSelectWireguardCustomPort() { - // Arrange - val onWireguardPortSelected: (Constraint<Port>) -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = - VpnSettingsUiState.createDefault( - selectedWireguardPort = Constraint.Only(Port(4000)), - customWireguardPort = Constraint.Only(Port(4000)) - ), - onWireguardPortSelected = onWireguardPortSelected - ) - } + fun testSelectWireguardCustomPort() = + composeExtension.use { + // Arrange + val onWireguardPortSelected: (Constraint<Port>) -> Unit = mockk(relaxed = true) + setContentWithTheme { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.createDefault( + selectedWireguardPort = Constraint.Only(Port(4000)), + customWireguardPort = Constraint.Only(Port(4000)) + ), + onWireguardPortSelected = onWireguardPortSelected + ) + } - // Act - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG)) - composeTestRule - .onNodeWithTag(testTag = LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG) - .performClick() + // Act + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG)) + onNodeWithTag(testTag = LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG).performClick() - // Assert - verify { onWireguardPortSelected.invoke(Constraint.Only(Port(4000))) } - } + // Assert + verify { onWireguardPortSelected.invoke(Constraint.Only(Port(4000))) } + } // Navigation Tests @Test - fun testMtuClick() { - // Arrange - val mockedClickHandler: (Int?) -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = VpnSettingsUiState.createDefault(), - navigateToMtuDialog = mockedClickHandler - ) - } + fun testMtuClick() = + composeExtension.use { + // Arrange + val mockedClickHandler: (Int?) -> Unit = mockk(relaxed = true) + setContentWithTheme { + VpnSettingsScreen( + uiState = VpnSettingsUiState.createDefault(), + navigateToMtuDialog = mockedClickHandler + ) + } - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) - // Act - composeTestRule.onNodeWithText("WireGuard MTU").performClick() + // Act + onNodeWithText("WireGuard MTU").performClick() - // Assert - verify { mockedClickHandler.invoke(null) } - } + // Assert + verify { mockedClickHandler.invoke(null) } + } @Test - fun testClickAddDns() { - // Arrange - val mockedClickHandler: (Int?, String?) -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = VpnSettingsUiState.createDefault(isCustomDnsEnabled = true), - navigateToDns = mockedClickHandler - ) - } + fun testClickAddDns() = + composeExtension.use { + // Arrange + val mockedClickHandler: (Int?, String?) -> Unit = mockk(relaxed = true) + setContentWithTheme { + VpnSettingsScreen( + uiState = VpnSettingsUiState.createDefault(isCustomDnsEnabled = true), + navigateToDns = mockedClickHandler + ) + } - // Act - composeTestRule.onNodeWithText("Add a server").performClick() + // Act + onNodeWithText("Add a server").performClick() - // Assert - verify { mockedClickHandler.invoke(null, null) } - } + // Assert + verify { mockedClickHandler.invoke(null, null) } + } @Test - fun testShowTunnelQuantumInfo() { - val mockedShowTunnelQuantumInfoClick: () -> Unit = mockk(relaxed = true) + fun testShowTunnelQuantumInfo() = + composeExtension.use { + val mockedShowTunnelQuantumInfoClick: () -> Unit = mockk(relaxed = true) - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = VpnSettingsUiState.createDefault(), - navigateToQuantumResistanceInfo = mockedShowTunnelQuantumInfoClick - ) - } + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = VpnSettingsUiState.createDefault(), + navigateToQuantumResistanceInfo = mockedShowTunnelQuantumInfoClick + ) + } - // Act - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode(hasTestTag(LAZY_LIST_QUANTUM_ITEM_ON_TEST_TAG)) - composeTestRule.onNodeWithText("Quantum-resistant tunnel").performClick() + // Act - // Assert - verify(exactly = 1) { mockedShowTunnelQuantumInfoClick() } - } + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_QUANTUM_ITEM_ON_TEST_TAG)) + onNodeWithText("Quantum-resistant tunnel").performClick() + + // Assert + verify(exactly = 1) { mockedShowTunnelQuantumInfoClick() } + } @Test - fun testShowWireguardPortInfo() { - val mockedClickHandler: (List<PortRange>) -> Unit = mockk(relaxed = true) + fun testShowWireguardPortInfo() = + composeExtension.use { + val mockedClickHandler: (List<PortRange>) -> Unit = mockk(relaxed = true) - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = VpnSettingsUiState.createDefault(), - navigateToWireguardPortInfo = mockedClickHandler - ) - } + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = VpnSettingsUiState.createDefault(), + navigateToWireguardPortInfo = mockedClickHandler + ) + } - composeTestRule.onNodeWithText("WireGuard port").performClick() + onNodeWithText("WireGuard port").performClick() - verify(exactly = 1) { mockedClickHandler.invoke(any()) } - } + verify(exactly = 1) { mockedClickHandler.invoke(any()) } + } @Test - fun testShowWireguardCustomPortDialog() { - val mockedClickHandler: () -> Unit = mockk(relaxed = true) + fun testShowWireguardCustomPortDialog() = + composeExtension.use { + val mockedClickHandler: () -> Unit = mockk(relaxed = true) - // Arrange - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = VpnSettingsUiState.createDefault(), - navigateToWireguardPortDialog = mockedClickHandler - ) - } + // Arrange + setContentWithTheme { + VpnSettingsScreen( + uiState = VpnSettingsUiState.createDefault(), + navigateToWireguardPortDialog = mockedClickHandler + ) + } - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_NUMBER_TEST_TAG)) - composeTestRule.onNodeWithText("Custom").performClick() + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_NUMBER_TEST_TAG)) + onNodeWithText("Custom").performClick() - // Assert - verify(exactly = 1) { mockedClickHandler.invoke() } - } + // Assert + verify(exactly = 1) { mockedClickHandler.invoke() } + } @Test - fun testClickWireguardCustomPortMainCell() { - // Arrange - val mockOnShowCustomPortDialog: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = VpnSettingsUiState.createDefault(), - navigateToWireguardPortDialog = mockOnShowCustomPortDialog - ) - } + fun testClickWireguardCustomPortMainCell() = + composeExtension.use { + // Arrange + val mockOnShowCustomPortDialog: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + VpnSettingsScreen( + uiState = VpnSettingsUiState.createDefault(), + navigateToWireguardPortDialog = mockOnShowCustomPortDialog + ) + } - // Act - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG)) - composeTestRule.onNodeWithTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG).performClick() + // Act + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG)) + onNodeWithTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG).performClick() - // Assert - verify { mockOnShowCustomPortDialog.invoke() } - } + // Assert + verify { mockOnShowCustomPortDialog.invoke() } + } @Test - fun testClickWireguardCustomPortNumberCell() { - // Arrange - val mockOnShowCustomPortDialog: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - VpnSettingsScreen( - uiState = - VpnSettingsUiState.createDefault( - selectedWireguardPort = Constraint.Only(Port(4000)) - ), - navigateToWireguardPortDialog = mockOnShowCustomPortDialog - ) - } + fun testClickWireguardCustomPortNumberCell() = + composeExtension.use { + // Arrange + val mockOnShowCustomPortDialog: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.createDefault( + selectedWireguardPort = Constraint.Only(Port(4000)) + ), + navigateToWireguardPortDialog = mockOnShowCustomPortDialog + ) + } - // Act - composeTestRule - .onNodeWithTag(LAZY_LIST_TEST_TAG) - .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG)) - composeTestRule - .onNodeWithTag(testTag = LAZY_LIST_WIREGUARD_CUSTOM_PORT_NUMBER_TEST_TAG) - .performClick() + // Act + onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG)) + onNodeWithTag(testTag = LAZY_LIST_WIREGUARD_CUSTOM_PORT_NUMBER_TEST_TAG).performClick() - // Assert - verify { mockOnShowCustomPortDialog.invoke() } - } + // Assert + verify { mockOnShowCustomPortDialog.invoke() } + } companion object { private const val LOCAL_DNS_SERVER_WARNING = 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 e62b1a399b..b1c9e5f817 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,9 +1,10 @@ package net.mullvad.mullvadvpn.compose.screen -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.createComposeExtension import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.mockk @@ -16,59 +17,59 @@ 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 org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +@OptIn(ExperimentalTestApi::class) class WelcomeScreenTest { - @get:Rule val composeTestRule = createComposeRule() + @JvmField @RegisterExtension val composeExtension = createComposeExtension() - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) } @Test - fun testDefaultState() { - // Arrange - composeTestRule.setContentWithTheme { - WelcomeScreen( - uiState = WelcomeUiState(), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - navigateToDeviceInfoDialog = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToVerificationPendingDialog = {} - ) - } + fun testDefaultState() = + composeExtension.use { + // Arrange + setContentWithTheme { + WelcomeScreen( + uiState = WelcomeUiState(), + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = { _ -> }, + navigateToDeviceInfoDialog = {}, + navigateToVerificationPendingDialog = {} + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText("Congrats!").assertExists() onNodeWithText("Here’s your account number. Save it!").assertExists() } - } @Test - fun testDisableSitePayment() { - // Arrange - composeTestRule.setContentWithTheme { - WelcomeScreen( - uiState = WelcomeUiState(), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - navigateToDeviceInfoDialog = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToVerificationPendingDialog = {} - ) - } + fun testDisableSitePayment() = + composeExtension.use { + // Arrange + setContentWithTheme { + WelcomeScreen( + uiState = WelcomeUiState(), + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = { _ -> }, + navigateToDeviceInfoDialog = {}, + navigateToVerificationPendingDialog = {} + ) + } - // Assert - composeTestRule.apply { + // Assert onNodeWithText( "Either buy credit on our website or redeem a voucher.", substring = true @@ -76,242 +77,251 @@ class WelcomeScreenTest { .assertDoesNotExist() onNodeWithText("Buy credit").assertDoesNotExist() } - } @Test - fun testShowAccountNumber() { - // Arrange - val rawAccountNumber = "1111222233334444" - val expectedAccountNumber = "1111 2222 3333 4444" - composeTestRule.setContentWithTheme { - WelcomeScreen( - uiState = WelcomeUiState(accountNumber = rawAccountNumber), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToDeviceInfoDialog = {}, - navigateToVerificationPendingDialog = {} - ) - } + fun testShowAccountNumber() = + composeExtension.use { + // Arrange + val rawAccountNumber = "1111222233334444" + val expectedAccountNumber = "1111 2222 3333 4444" + setContentWithTheme { + WelcomeScreen( + uiState = WelcomeUiState(accountNumber = rawAccountNumber), + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = { _ -> }, + navigateToDeviceInfoDialog = {}, + navigateToVerificationPendingDialog = {} + ) + } - // Assert - composeTestRule.apply { onNodeWithText(expectedAccountNumber).assertExists() } - } + // Assert + onNodeWithText(expectedAccountNumber).assertExists() + } @Test - fun testClickSitePaymentButton() { - // Arrange - val mockClickListener: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - WelcomeScreen( - uiState = WelcomeUiState(showSitePayment = true), - onSitePaymentClick = mockClickListener, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToVerificationPendingDialog = {}, - navigateToDeviceInfoDialog = {} - ) - } + fun testClickSitePaymentButton() = + composeExtension.use { + // Arrange + val mockClickListener: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + WelcomeScreen( + uiState = WelcomeUiState(showSitePayment = true), + onSitePaymentClick = mockClickListener, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = { _ -> }, + navigateToDeviceInfoDialog = {}, + navigateToVerificationPendingDialog = {} + ) + } - // Act - composeTestRule.apply { onNodeWithText("Buy credit").performClick() } + // Act + onNodeWithText("Buy credit").performClick() - // Assert - verify(exactly = 1) { mockClickListener.invoke() } - } - - @Test - fun testClickRedeemVoucher() { - // Arrange - val mockClickListener: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - WelcomeScreen( - uiState = WelcomeUiState(), - onSitePaymentClick = {}, - onRedeemVoucherClick = mockClickListener, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToVerificationPendingDialog = {}, - navigateToDeviceInfoDialog = {} - ) + // Assert + verify(exactly = 1) { mockClickListener.invoke() } } - // Act - composeTestRule.apply { onNodeWithText("Redeem voucher").performClick() } + @Test + fun testClickRedeemVoucher() = + composeExtension.use { + // Arrange + val mockClickListener: () -> Unit = mockk(relaxed = true) + setContentWithTheme { + WelcomeScreen( + uiState = WelcomeUiState(), + onSitePaymentClick = {}, + onRedeemVoucherClick = mockClickListener, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = { _ -> }, + navigateToDeviceInfoDialog = {}, + navigateToVerificationPendingDialog = {} + ) + } - // Assert - verify(exactly = 1) { mockClickListener.invoke() } - } + // Act + onNodeWithText("Redeem voucher").performClick() - @Test - fun testShowBillingErrorPaymentButton() { - // Arrange - composeTestRule.setContentWithTheme { - WelcomeScreen( - uiState = WelcomeUiState().copy(billingPaymentState = PaymentState.Error.Billing), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToVerificationPendingDialog = {}, - navigateToDeviceInfoDialog = {} - ) + // Assert + verify(exactly = 1) { mockClickListener.invoke() } } - // 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( - uiState = - WelcomeUiState( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToVerificationPendingDialog = {}, - navigateToDeviceInfoDialog = {} - ) - } + fun testShowBillingErrorPaymentButton() = + composeExtension.use { + // Arrange + setContentWithTheme { + WelcomeScreen( + uiState = + WelcomeUiState().copy(billingPaymentState = PaymentState.Error.Billing), + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = { _ -> }, + navigateToDeviceInfoDialog = {}, + navigateToVerificationPendingDialog = {} + ) + } - // Assert - composeTestRule.onNodeWithText("Add 30 days time ($10)").assertExists() - } + // Assert + onNodeWithText("Add 30 days time").assertExists() + } @Test - fun testShowPendingPayment() { - // Arrange - val mockPaymentProduct: PaymentProduct = mockk() - every { mockPaymentProduct.price } returns ProductPrice("$10") - every { mockPaymentProduct.status } returns PaymentStatus.PENDING - composeTestRule.setContentWithTheme { - WelcomeScreen( - uiState = - WelcomeUiState() - .copy( + fun testShowBillingPaymentAvailable() = + composeExtension.use { + // Arrange + val mockPaymentProduct: PaymentProduct = mockk() + every { mockPaymentProduct.price } returns ProductPrice("$10") + every { mockPaymentProduct.status } returns null + setContentWithTheme { + WelcomeScreen( + uiState = + WelcomeUiState( billingPaymentState = PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToVerificationPendingDialog = {}, - navigateToDeviceInfoDialog = {} - ) + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = { _ -> }, + navigateToDeviceInfoDialog = {}, + navigateToVerificationPendingDialog = {} + ) + } + + // Assert + onNodeWithText("Add 30 days time ($10)").assertExists() } - // Assert - composeTestRule.onNodeWithText("Google Play payment pending").assertExists() - } + @Test + fun testShowPendingPayment() = + composeExtension.use { + // Arrange + val mockPaymentProduct: PaymentProduct = mockk() + every { mockPaymentProduct.price } returns ProductPrice("$10") + every { mockPaymentProduct.status } returns PaymentStatus.PENDING + setContentWithTheme { + WelcomeScreen( + uiState = + WelcomeUiState() + .copy( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = { _ -> }, + navigateToDeviceInfoDialog = {}, + navigateToVerificationPendingDialog = {} + ) + } + + // Assert + 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 - val mockShowPendingInfo = mockk<() -> Unit>(relaxed = true) - composeTestRule.setContentWithTheme { - WelcomeScreen( - uiState = - WelcomeUiState() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToVerificationPendingDialog = mockShowPendingInfo, - navigateToDeviceInfoDialog = {} - ) + fun testShowPendingPaymentInfoDialog() = + composeExtension.use { + // Arrange + val mockPaymentProduct: PaymentProduct = mockk() + every { mockPaymentProduct.price } returns ProductPrice("$10") + every { mockPaymentProduct.status } returns PaymentStatus.PENDING + val mockShowPendingInfo = mockk<() -> Unit>(relaxed = true) + setContentWithTheme { + WelcomeScreen( + uiState = + WelcomeUiState() + .copy( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = { _ -> }, + navigateToVerificationPendingDialog = mockShowPendingInfo, + navigateToDeviceInfoDialog = {} + ) + } + + // Act + onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).performClick() + + // Assert + verify(exactly = 1) { mockShowPendingInfo() } } - // Act - composeTestRule.onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).performClick() + @Test + fun testShowVerificationInProgress() = + composeExtension.use { + // Arrange + val mockPaymentProduct: PaymentProduct = mockk() + every { mockPaymentProduct.price } returns ProductPrice("$10") + every { mockPaymentProduct.status } returns PaymentStatus.VERIFICATION_IN_PROGRESS + setContentWithTheme { + WelcomeScreen( + uiState = + WelcomeUiState() + .copy( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = { _ -> }, + navigateToDeviceInfoDialog = {}, + navigateToVerificationPendingDialog = {} + ) + } - // Assert - verify(exactly = 1) { mockShowPendingInfo() } - } + // Assert + onNodeWithText("Verifying purchase").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( - uiState = - WelcomeUiState() - .copy( + fun testOnPurchaseBillingProductClick() = + composeExtension.use { + // Arrange + val clickHandler: (ProductId) -> 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 + setContentWithTheme { + WelcomeScreen( + uiState = + WelcomeUiState( billingPaymentState = PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToVerificationPendingDialog = {}, - navigateToDeviceInfoDialog = {} - ) - } + onSitePaymentClick = {}, + onRedeemVoucherClick = {}, + onSettingsClick = {}, + onAccountClick = {}, + onPurchaseBillingProductClick = clickHandler, + navigateToDeviceInfoDialog = {}, + navigateToVerificationPendingDialog = {} + ) + } - // Assert - composeTestRule.onNodeWithText("Verifying purchase").assertExists() - } + // Act + onNodeWithText("Add 30 days time ($10)").performClick() - @Test - fun testOnPurchaseBillingProductClick() { - // Arrange - val clickHandler: (ProductId) -> 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( - uiState = - WelcomeUiState( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = clickHandler, - navigateToVerificationPendingDialog = {}, - navigateToDeviceInfoDialog = {} - ) + // Assert + verify { clickHandler(ProductId("PRODUCT_ID")) } } - - // Act - composeTestRule.onNodeWithText("Add 30 days time ($10)").performClick() - - // Assert - verify { clickHandler(ProductId("PRODUCT_ID")) } - } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreen.kt index b9db62d620..864f909421 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreen.kt @@ -96,7 +96,7 @@ fun FilterScreen( onApplyClick: () -> Unit = {}, onSelectedOwnership: (ownership: Ownership?) -> Unit = {}, onAllProviderCheckChange: (isChecked: Boolean) -> Unit = {}, - onSelectedProvider: (checked: Boolean, provider: Provider) -> Unit + onSelectedProvider: (checked: Boolean, provider: Provider) -> Unit = { _, _ -> } ) { var providerExpanded by rememberSaveable { mutableStateOf(false) } var ownershipExpanded by rememberSaveable { mutableStateOf(false) } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt index 30b54cea11..9767d3930a 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt @@ -9,6 +9,7 @@ import kotlin.test.assertEquals import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.repository.InAppNotification @@ -19,13 +20,13 @@ import net.mullvad.mullvadvpn.usecase.TunnelStateNotificationUseCase import net.mullvad.mullvadvpn.usecase.VersionNotificationUseCase import net.mullvad.talpid.tunnel.ErrorState import org.joda.time.DateTime -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class InAppNotificationControllerTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private lateinit var inAppNotificationController: InAppNotificationController private val accountExpiryNotifications = MutableStateFlow(emptyList<InAppNotification>()) @@ -35,7 +36,7 @@ class InAppNotificationControllerTest { private lateinit var job: Job - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) @@ -56,11 +57,11 @@ class InAppNotificationControllerTest { newDeviceNotificationUseCase, versionNotificationUseCase, tunnelStateNotificationUseCase, - CoroutineScope(job + testCoroutineRule.testDispatcher) + CoroutineScope(job + UnconfinedTestDispatcher()) ) } - @After + @AfterEach fun teardown() { job.cancel() unmockkAll() diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt index 3e26a37c2c..3296243c56 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt @@ -12,23 +12,23 @@ import io.mockk.unmockkAll import io.mockk.verify import kotlin.test.assertEquals import kotlin.test.assertFails -import org.junit.After -import org.junit.Before -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test class ApplicationsIconManagerTest { private val mockedPackageManager = mockk<PackageManager>() private val mockedMainLooper = mockk<Looper>() private val testSubject = ApplicationsIconManager(mockedPackageManager) - @Before + @BeforeEach fun setUp() { mockkStatic(Looper::class) mockkStatic(DRAWABLE_EXTENSION_CLASS) every { Looper.getMainLooper() } returns mockedMainLooper } - @After + @AfterEach fun tearDown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt index a74ab85fc4..1b37184915 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt @@ -8,8 +8,8 @@ import io.mockk.mockk import io.mockk.unmockkAll import io.mockk.verifyAll import net.mullvad.mullvadvpn.lib.common.test.assertLists -import org.junit.After -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Test class ApplicationsProviderTest { private val mockedPackageManager = mockk<PackageManager>() @@ -17,7 +17,7 @@ class ApplicationsProviderTest { private val testSubject = ApplicationsProvider(mockedPackageManager, selfPackageName) private val internet = Manifest.permission.INTERNET - @After + @AfterEach fun tearDown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparatorTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparatorTest.kt index 2ef1d1f02c..dc9c141f28 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparatorTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparatorTest.kt @@ -2,13 +2,13 @@ package net.mullvad.mullvadvpn.relaylist import io.mockk.mockk import io.mockk.unmockkAll -import org.junit.After -import org.junit.Assert.assertTrue -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test class RelayNameComparatorTest { - @After + @AfterEach fun tearDown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxyTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxyTest.kt index 52bbbee0a6..4da6c4fdaf 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxyTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxyTest.kt @@ -19,9 +19,9 @@ import kotlin.test.assertEquals import net.mullvad.mullvadvpn.lib.ipc.Event import net.mullvad.mullvadvpn.lib.ipc.EventDispatcher import net.mullvad.mullvadvpn.lib.ipc.Request -import org.junit.After -import org.junit.Before -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test class ConnectionProxyTest { @@ -32,7 +32,7 @@ class ConnectionProxyTest { @MockK private lateinit var mockedDispatchingHandler: EventDispatcher lateinit var connectionProxy: ConnectionProxy - @Before + @BeforeEach fun setup() { mockkStatic(Looper::class) mockkStatic(Log::class) @@ -44,7 +44,7 @@ class ConnectionProxyTest { every { Log.e(any(), any()) } returns mockk(relaxed = true) } - @After + @AfterEach fun tearDown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionDeviceDataSourceTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionDeviceDataSourceTest.kt index 50f930d2a5..d4fd349438 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionDeviceDataSourceTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionDeviceDataSourceTest.kt @@ -17,9 +17,9 @@ import net.mullvad.mullvadvpn.lib.common.util.JobTracker import net.mullvad.mullvadvpn.lib.ipc.Event import net.mullvad.mullvadvpn.lib.ipc.EventDispatcher import net.mullvad.mullvadvpn.lib.ipc.Request -import org.junit.After -import org.junit.Before -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test class ServiceConnectionDeviceDataSourceTest { private val tracker = JobTracker() @@ -32,7 +32,7 @@ class ServiceConnectionDeviceDataSourceTest { lateinit var serviceConnectionDeviceDataSource: ServiceConnectionDeviceDataSource - @Before + @BeforeEach fun setup() { mockkStatic(Looper::class) mockkStatic(android.util.Log::class) @@ -44,7 +44,7 @@ class ServiceConnectionDeviceDataSourceTest { every { android.util.Log.e(any(), any()) } returns mockk(relaxed = true) } - @After + @AfterEach fun tearDown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryNotificationUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryNotificationUseCaseTest.kt index 5341708d3b..f068642bec 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryNotificationUseCaseTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryNotificationUseCaseTest.kt @@ -14,18 +14,18 @@ import net.mullvad.mullvadvpn.model.AccountExpiry import net.mullvad.mullvadvpn.repository.AccountRepository import net.mullvad.mullvadvpn.repository.InAppNotification import org.joda.time.DateTime -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class AccountExpiryNotificationUseCaseTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val accountExpiry = MutableStateFlow<AccountExpiry>(AccountExpiry.Missing) private lateinit var accountExpiryNotificationUseCase: AccountExpiryNotificationUseCase - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) @@ -35,7 +35,7 @@ class AccountExpiryNotificationUseCaseTest { accountExpiryNotificationUseCase = AccountExpiryNotificationUseCase(accountRepository) } - @After + @AfterEach fun teardown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/NewDeviceUseNotificationCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/NewDeviceUseNotificationCaseTest.kt index bd375d729a..5e4403c38d 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/NewDeviceUseNotificationCaseTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/NewDeviceUseNotificationCaseTest.kt @@ -15,13 +15,13 @@ import net.mullvad.mullvadvpn.model.Device import net.mullvad.mullvadvpn.model.DeviceState import net.mullvad.mullvadvpn.repository.DeviceRepository import net.mullvad.mullvadvpn.repository.InAppNotification -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class NewDeviceUseNotificationCaseTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val deviceName = "Frank Zebra" private val deviceState = @@ -32,7 +32,7 @@ class NewDeviceUseNotificationCaseTest { ) private lateinit var newDeviceNotificationUseCase: NewDeviceNotificationUseCase - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) @@ -42,7 +42,7 @@ class NewDeviceUseNotificationCaseTest { NewDeviceNotificationUseCase(deviceRepository = mockDeviceRepository) } - @After + @AfterEach fun teardown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt index bdc5ea49b8..6563f7e6e9 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt @@ -16,8 +16,8 @@ import net.mullvad.mullvadvpn.repository.AccountRepository import net.mullvad.talpid.tunnel.ErrorState import net.mullvad.talpid.tunnel.ErrorStateCause import org.joda.time.DateTime -import org.junit.Before -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test class OutOfTimeUseCaseTest { private val mockAccountRepository: AccountRepository = mockk() @@ -28,7 +28,7 @@ class OutOfTimeUseCaseTest { lateinit var outOfTimeUseCase: OutOfTimeUseCase - @Before + @BeforeEach fun setup() { every { mockAccountRepository.accountExpiryState } returns expiry every { mockMessageHandler.events<Event.TunnelStateChange>() } returns events diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/PlayPaymentUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/PlayPaymentUseCaseTest.kt index a1d8bee37a..fb85629a9c 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/PlayPaymentUseCaseTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/PlayPaymentUseCaseTest.kt @@ -12,7 +12,7 @@ import net.mullvad.mullvadvpn.lib.payment.PaymentRepository import net.mullvad.mullvadvpn.lib.payment.model.PaymentAvailability import net.mullvad.mullvadvpn.lib.payment.model.ProductId import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult -import org.junit.Test +import org.junit.jupiter.api.Test class PlayPaymentUseCaseTest { diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/TunnelStateNotificationUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/TunnelStateNotificationUseCaseTest.kt index e67630642d..244c735ee9 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/TunnelStateNotificationUseCaseTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/TunnelStateNotificationUseCaseTest.kt @@ -19,13 +19,13 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState import net.mullvad.talpid.tunnel.ActionAfterDisconnect import net.mullvad.talpid.tunnel.ErrorState import net.mullvad.talpid.util.EventNotifier -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class TunnelStateNotificationUseCaseTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val mockServiceConnectionManager: ServiceConnectionManager = mockk() private val mockServiceConnectionContainer: ServiceConnectionContainer = mockk() @@ -37,7 +37,7 @@ class TunnelStateNotificationUseCaseTest { private val eventNotifierTunnelUiState = EventNotifier<TunnelState>(TunnelState.Disconnected()) - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) every { mockConnectionProxy.onUiStateChange } returns eventNotifierTunnelUiState @@ -49,7 +49,7 @@ class TunnelStateNotificationUseCaseTest { TunnelStateNotificationUseCase(serviceConnectionManager = mockServiceConnectionManager) } - @After + @AfterEach fun teardown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/VersionNotificationUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/VersionNotificationUseCaseTest.kt index 5aba70c938..12f7207280 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/VersionNotificationUseCaseTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/VersionNotificationUseCaseTest.kt @@ -18,13 +18,13 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState import net.mullvad.mullvadvpn.util.appVersionCallbackFlow -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class VersionNotificationUseCaseTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val mockServiceConnectionManager: ServiceConnectionManager = mockk() private lateinit var mockAppVersionInfoCache: AppVersionInfoCache @@ -43,7 +43,7 @@ class VersionNotificationUseCaseTest { ) private lateinit var versionNotificationUseCase: VersionNotificationUseCase - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) mockkStatic(CACHE_EXTENSION_CLASS) @@ -63,7 +63,7 @@ class VersionNotificationUseCaseTest { ) } - @After + @AfterEach fun teardown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/utils/VoucherRegexHelperParameterizedTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/utils/VoucherRegexHelperParameterizedTest.kt index 60db08b85f..1722e81a9d 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/utils/VoucherRegexHelperParameterizedTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/utils/VoucherRegexHelperParameterizedTest.kt @@ -1,34 +1,29 @@ package net.mullvad.mullvadvpn.utils +import java.util.stream.Stream +import kotlin.test.assertEquals import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.util.VoucherRegexHelper -import org.hamcrest.CoreMatchers.equalTo -import org.hamcrest.MatcherAssert.assertThat -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource private const val IS_ACCEPTED_FORMAT = true private const val IS_UNACCEPTED_FORMAT = false -@RunWith(Parameterized::class) -class VoucherRegexHelperParameterizedTest( - private val isValid: Boolean, - private val voucher: String -) { - @get:Rule val testCoroutineRule = TestCoroutineRule() +@ExtendWith(TestCoroutineRule::class) +class VoucherRegexHelperParameterizedTest { - @Test - fun testVoucherFormat() { - assertThat(VoucherRegexHelper.validate(voucher), equalTo(isValid)) + @ParameterizedTest + @MethodSource("data") + fun testVoucherFormat(isValid: Boolean, voucher: String) { + assertEquals(VoucherRegexHelper.validate(voucher), isValid) } companion object { @JvmStatic - @Parameterized.Parameters - fun data(): Collection<Array<Any>> = - listOf( + fun data(): Stream<Array<Any>> = + Stream.of( arrayOf(IS_ACCEPTED_FORMAT, "1"), arrayOf(IS_ACCEPTED_FORMAT, "a"), arrayOf(IS_ACCEPTED_FORMAT, "A"), diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModelTest.kt index 22acc97986..637c531039 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModelTest.kt @@ -30,13 +30,13 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.AuthTokenCache import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.ui.serviceconnection.authTokenCache import net.mullvad.mullvadvpn.usecase.PaymentUseCase -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class AccountViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val mockAccountRepository: AccountRepository = mockk(relaxUnitFun = true) private val mockServiceConnectionManager: ServiceConnectionManager = mockk() @@ -62,7 +62,7 @@ class AccountViewModelTest { private lateinit var viewModel: AccountViewModel - @Before + @BeforeEach fun setUp() { mockkStatic(CACHE_EXTENSION_CLASS) mockkStatic(PURCHASE_RESULT_EXTENSIONS_CLASS) @@ -82,7 +82,7 @@ class AccountViewModelTest { ) } - @After + @AfterEach fun tearDown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt index 3350178ca3..d9a5ae534e 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt @@ -12,19 +12,19 @@ import kotlin.test.assertNotNull import kotlinx.coroutines.test.runTest import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.repository.ChangelogRepository -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class ChangelogViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() @MockK private lateinit var mockedChangelogRepository: ChangelogRepository private lateinit var viewModel: ChangelogViewModel - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) mockkStatic(EVENT_NOTIFIER_EXTENSION_CLASS) @@ -32,7 +32,7 @@ class ChangelogViewModelTest { Runs } - @After + @AfterEach fun teardown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt index 47364652ce..a1e3ce57e5 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt @@ -43,13 +43,13 @@ import net.mullvad.mullvadvpn.usecase.RelayListUseCase import net.mullvad.mullvadvpn.util.appVersionCallbackFlow import net.mullvad.talpid.tunnel.ErrorState import net.mullvad.talpid.util.EventNotifier -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class ConnectViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val mockServiceConnectionManager: ServiceConnectionManager = mockk() private lateinit var viewModel: ConnectViewModel @@ -102,7 +102,7 @@ class ConnectViewModelTest { private val outOfTimeUseCase: OutOfTimeUseCase = mockk() private val outOfTimeViewFlow = MutableStateFlow(false) - @Before + @BeforeEach fun setup() { mockkStatic(CACHE_EXTENSION_CLASS) mockkStatic(SERVICE_CONNECTION_MANAGER_EXTENSIONS) @@ -148,7 +148,7 @@ class ConnectViewModelTest { ) } - @After + @AfterEach fun teardown() { viewModel.viewModelScope.coroutineContext.cancel() unmockkAll() @@ -160,83 +160,79 @@ class ConnectViewModelTest { } @Test - fun testTunnelRealStateUpdate() = - runTest(testCoroutineRule.testDispatcher) { - val tunnelRealStateTestItem = TunnelState.Connected(mockk(relaxed = true), null) + fun testTunnelRealStateUpdate() = runTest { + val tunnelRealStateTestItem = TunnelState.Connected(mockk(relaxed = true), null) - viewModel.uiState.test { - assertEquals(ConnectUiState.INITIAL, awaitItem()) - serviceConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - eventNotifierTunnelRealState.notify(tunnelRealStateTestItem) - val result = awaitItem() - assertEquals(tunnelRealStateTestItem, result.tunnelRealState) - } + viewModel.uiState.test { + assertEquals(ConnectUiState.INITIAL, awaitItem()) + serviceConnectionState.value = + ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) + eventNotifierTunnelRealState.notify(tunnelRealStateTestItem) + val result = awaitItem() + assertEquals(tunnelRealStateTestItem, result.tunnelRealState) } + } @Test - fun testTunnelUiStateUpdate() = - runTest(testCoroutineRule.testDispatcher) { - val tunnelUiStateTestItem = TunnelState.Connected(mockk(), mockk()) + fun testTunnelUiStateUpdate() = runTest { + val tunnelUiStateTestItem = TunnelState.Connected(mockk(), mockk()) - viewModel.uiState.test { - assertEquals(ConnectUiState.INITIAL, awaitItem()) - serviceConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - eventNotifierTunnelUiState.notify(tunnelUiStateTestItem) - val result = awaitItem() - assertEquals(tunnelUiStateTestItem, result.tunnelUiState) - } + viewModel.uiState.test { + assertEquals(ConnectUiState.INITIAL, awaitItem()) + serviceConnectionState.value = + ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) + eventNotifierTunnelUiState.notify(tunnelUiStateTestItem) + val result = awaitItem() + assertEquals(tunnelUiStateTestItem, result.tunnelUiState) } + } @Test - fun testRelayItemUpdate() = - runTest(testCoroutineRule.testDispatcher) { - val relayTestItem = - RelayCountry(name = "Name", code = "Code", expanded = false, cities = emptyList()) - selectedRelayFlow.value = relayTestItem + fun testRelayItemUpdate() = runTest { + val relayTestItem = + RelayCountry(name = "Name", code = "Code", expanded = false, cities = emptyList()) + selectedRelayFlow.value = relayTestItem - viewModel.uiState.test { - assertEquals(ConnectUiState.INITIAL, awaitItem()) - serviceConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - val result = awaitItem() - assertEquals(relayTestItem, result.relayLocation) - } + viewModel.uiState.test { + assertEquals(ConnectUiState.INITIAL, awaitItem()) + serviceConnectionState.value = + ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) + val result = awaitItem() + assertEquals(relayTestItem, result.relayLocation) } + } @Test - fun testLocationUpdate() = - runTest(testCoroutineRule.testDispatcher) { - val locationTestItem = - GeoIpLocation( - ipv4 = mockk(relaxed = true), - ipv6 = mockk(relaxed = true), - country = "Sweden", - city = "Gothenburg", - hostname = "Host" - ) + fun testLocationUpdate() = runTest { + val locationTestItem = + GeoIpLocation( + ipv4 = mockk(relaxed = true), + ipv6 = mockk(relaxed = true), + country = "Sweden", + city = "Gothenburg", + hostname = "Host" + ) - // Act, Assert - viewModel.uiState.test { - assertEquals(ConnectUiState.INITIAL, awaitItem()) - eventNotifierTunnelRealState.notify(TunnelState.Disconnected(null)) + // Act, Assert + viewModel.uiState.test { + assertEquals(ConnectUiState.INITIAL, awaitItem()) + eventNotifierTunnelRealState.notify(TunnelState.Disconnected(null)) - serviceConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - // Start of with no location - assertNull(awaitItem().location) + serviceConnectionState.value = + ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) + // Start of with no location + assertNull(awaitItem().location) - // After updated we show latest - eventNotifierTunnelRealState.notify(TunnelState.Disconnected(locationTestItem)) - assertEquals(locationTestItem, awaitItem().location) - } + // After updated we show latest + eventNotifierTunnelRealState.notify(TunnelState.Disconnected(locationTestItem)) + assertEquals(locationTestItem, awaitItem().location) } + } @Test fun testLocationUpdateNullLocation() = // Arrange - runTest(testCoroutineRule.testDispatcher) { + runTest { val locationTestItem = null // Act, Assert @@ -251,99 +247,91 @@ class ConnectViewModelTest { } @Test - fun testOnDisconnectClick() = - runTest(testCoroutineRule.testDispatcher) { - val mockConnectionProxy: ConnectionProxy = mockk(relaxed = true) - every { mockServiceConnectionManager.connectionProxy() } returns mockConnectionProxy - viewModel.onDisconnectClick() - verify { mockConnectionProxy.disconnect() } - } + fun testOnDisconnectClick() = runTest { + val mockConnectionProxy: ConnectionProxy = mockk(relaxed = true) + every { mockServiceConnectionManager.connectionProxy() } returns mockConnectionProxy + viewModel.onDisconnectClick() + verify { mockConnectionProxy.disconnect() } + } @Test - fun testOnReconnectClick() = - runTest(testCoroutineRule.testDispatcher) { - val mockConnectionProxy: ConnectionProxy = mockk(relaxed = true) - every { mockServiceConnectionManager.connectionProxy() } returns mockConnectionProxy - viewModel.onReconnectClick() - verify { mockConnectionProxy.reconnect() } - } + fun testOnReconnectClick() = runTest { + val mockConnectionProxy: ConnectionProxy = mockk(relaxed = true) + every { mockServiceConnectionManager.connectionProxy() } returns mockConnectionProxy + viewModel.onReconnectClick() + verify { mockConnectionProxy.reconnect() } + } @Test - fun testOnConnectClick() = - runTest(testCoroutineRule.testDispatcher) { - val mockConnectionProxy: ConnectionProxy = mockk(relaxed = true) - every { mockServiceConnectionManager.connectionProxy() } returns mockConnectionProxy - viewModel.onConnectClick() - verify { mockConnectionProxy.connect() } - } + fun testOnConnectClick() = runTest { + val mockConnectionProxy: ConnectionProxy = mockk(relaxed = true) + every { mockServiceConnectionManager.connectionProxy() } returns mockConnectionProxy + viewModel.onConnectClick() + verify { mockConnectionProxy.connect() } + } @Test - fun testOnCancelClick() = - runTest(testCoroutineRule.testDispatcher) { - val mockConnectionProxy: ConnectionProxy = mockk(relaxed = true) - every { mockServiceConnectionManager.connectionProxy() } returns mockConnectionProxy - viewModel.onCancelClick() - verify { mockConnectionProxy.disconnect() } - } + fun testOnCancelClick() = runTest { + val mockConnectionProxy: ConnectionProxy = mockk(relaxed = true) + every { mockServiceConnectionManager.connectionProxy() } returns mockConnectionProxy + viewModel.onCancelClick() + verify { mockConnectionProxy.disconnect() } + } @Test - fun testErrorNotificationState() = - runTest(testCoroutineRule.testDispatcher) { - // Arrange - val mockErrorState: ErrorState = mockk() - val expectedConnectNotificationState = - InAppNotification.TunnelStateError(mockErrorState) - val tunnelUiState = TunnelState.Error(mockErrorState) - notifications.value = listOf(expectedConnectNotificationState) + fun testErrorNotificationState() = runTest { + // Arrange + val mockErrorState: ErrorState = mockk() + val expectedConnectNotificationState = InAppNotification.TunnelStateError(mockErrorState) + val tunnelUiState = TunnelState.Error(mockErrorState) + notifications.value = listOf(expectedConnectNotificationState) - // Act, Assert - viewModel.uiState.test { - assertEquals(ConnectUiState.INITIAL, awaitItem()) - serviceConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - eventNotifierTunnelUiState.notify(tunnelUiState) - val result = awaitItem() - assertEquals(expectedConnectNotificationState, result.inAppNotification) - } + // Act, Assert + viewModel.uiState.test { + assertEquals(ConnectUiState.INITIAL, awaitItem()) + serviceConnectionState.value = + ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) + eventNotifierTunnelUiState.notify(tunnelUiState) + val result = awaitItem() + assertEquals(expectedConnectNotificationState, result.inAppNotification) } + } @Test - fun testOnShowAccountClick() = - runTest(testCoroutineRule.testDispatcher) { - // Arrange - val mockToken = "4444 5555 6666 7777" - val mockAuthTokenCache: AuthTokenCache = mockk(relaxed = true) - every { mockServiceConnectionManager.authTokenCache() } returns mockAuthTokenCache - coEvery { mockAuthTokenCache.fetchAuthToken() } returns mockToken + fun testOnShowAccountClick() = runTest { + // Arrange + val mockToken = "4444 5555 6666 7777" + val mockAuthTokenCache: AuthTokenCache = mockk(relaxed = true) + every { mockServiceConnectionManager.authTokenCache() } returns mockAuthTokenCache + coEvery { mockAuthTokenCache.fetchAuthToken() } returns mockToken - // Act, Assert - viewModel.uiSideEffect.test { - viewModel.onManageAccountClick() - val action = awaitItem() - assertIs<ConnectViewModel.UiSideEffect.OpenAccountManagementPageInBrowser>(action) - assertEquals(mockToken, action.token) - } + // Act, Assert + viewModel.uiSideEffect.test { + viewModel.onManageAccountClick() + val action = awaitItem() + assertIs<ConnectViewModel.UiSideEffect.OpenAccountManagementPageInBrowser>(action) + assertEquals(mockToken, action.token) } + } @Test - fun testOutOfTimeUiSideEffect() = - runTest(testCoroutineRule.testDispatcher) { - // Arrange - val deferred = async { viewModel.uiSideEffect.first() } - - // Act - viewModel.uiState.test { - awaitItem() - serviceConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - outOfTimeViewFlow.value = true - awaitItem() - } + fun testOutOfTimeUiSideEffect() = runTest { + // Arrange + val deferred = async { viewModel.uiSideEffect.first() } - // Assert - assertIs<ConnectViewModel.UiSideEffect.OutOfTime>(deferred.await()) + // Act + viewModel.uiState.test { + awaitItem() + serviceConnectionState.value = + ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) + outOfTimeViewFlow.value = true + awaitItem() } + // Assert + assertIs<ConnectViewModel.UiSideEffect.OutOfTime>(deferred.await()) + } + companion object { private const val CACHE_EXTENSION_CLASS = "net.mullvad.mullvadvpn.util.CacheExtensionsKt" private const val SERVICE_CONNECTION_MANAGER_EXTENSIONS = diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt index 0fcf684afc..3f7966e8bd 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt @@ -11,7 +11,6 @@ import io.mockk.mockkStatic import io.mockk.unmockkAll import io.mockk.verify import io.mockk.verifyOrder -import junit.framework.Assert.assertEquals import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest @@ -25,13 +24,14 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState import net.mullvad.talpid.util.EventNotifier import net.mullvad.talpid.util.callbackFlowFromSubscription -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class DeviceRevokedViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() @MockK private lateinit var mockedAccountRepository: AccountRepository @@ -42,7 +42,7 @@ class DeviceRevokedViewModelTest { private lateinit var viewModel: DeviceRevokedViewModel - @Before + @BeforeEach fun setup() { MockKAnnotations.init(this) mockkStatic(EVENT_NOTIFIER_EXTENSION_CLASS) @@ -55,7 +55,7 @@ class DeviceRevokedViewModelTest { ) } - @After + @AfterEach fun teardown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/FilterViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/FilterViewModelTest.kt index 9f61ea4a91..210ed88666 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/FilterViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/FilterViewModelTest.kt @@ -20,13 +20,13 @@ import net.mullvad.mullvadvpn.model.Ownership import net.mullvad.mullvadvpn.model.Providers import net.mullvad.mullvadvpn.relaylist.Provider import net.mullvad.mullvadvpn.usecase.RelayListFilterUseCase -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class FilterViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val mockRelayListFilterUseCase: RelayListFilterUseCase = mockk(relaxed = true) private lateinit var viewModel: FilterViewModel private val selectedOwnership = @@ -54,7 +54,7 @@ class FilterViewModelTest { private val mockSelectedProviders: List<Provider> = listOf(Provider("31173", true), Provider("Blix", true), Provider("Creanova", true)) - @Before + @BeforeEach fun setup() { every { mockRelayListFilterUseCase.selectedOwnership() } returns selectedOwnership every { mockRelayListFilterUseCase.availableProviders() } returns @@ -64,7 +64,7 @@ class FilterViewModelTest { viewModel = FilterViewModel(mockRelayListFilterUseCase) } - @After + @AfterEach fun teardown() { viewModel.viewModelScope.coroutineContext.cancel() unmockkAll() diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt index c402a3103e..f677615c24 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt @@ -30,13 +30,13 @@ import net.mullvad.mullvadvpn.repository.DeviceRepository import net.mullvad.mullvadvpn.usecase.ConnectivityUseCase import net.mullvad.mullvadvpn.usecase.NewDeviceNotificationUseCase import org.joda.time.DateTime -import org.junit.Assert.assertEquals -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class LoginViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() @MockK private lateinit var connectivityUseCase: ConnectivityUseCase @MockK private lateinit var mockedAccountRepository: AccountRepository @@ -46,7 +46,7 @@ class LoginViewModelTest { private lateinit var loginViewModel: LoginViewModel private val accountHistoryTestEvents = MutableStateFlow<AccountHistory>(AccountHistory.Missing) - @Before + @BeforeEach fun setup() { Dispatchers.setMain(UnconfinedTestDispatcher()) diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt index 50a9d8fb98..0df34e0747 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt @@ -38,13 +38,13 @@ import net.mullvad.mullvadvpn.usecase.PaymentUseCase import net.mullvad.talpid.util.EventNotifier import org.joda.time.DateTime import org.joda.time.ReadableInstant -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class OutOfTimeViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val serviceConnectionStateFlow = MutableStateFlow<ServiceConnectionState>(ServiceConnectionState.Disconnected) @@ -70,7 +70,7 @@ class OutOfTimeViewModelTest { private lateinit var viewModel: OutOfTimeViewModel - @Before + @BeforeEach fun setUp() { mockkStatic(SERVICE_CONNECTION_MANAGER_EXTENSIONS) mockkStatic(PURCHASE_RESULT_EXTENSIONS_CLASS) @@ -103,75 +103,71 @@ class OutOfTimeViewModelTest { ) } - @After + @AfterEach fun tearDown() { viewModel.viewModelScope.coroutineContext.cancel() unmockkAll() } @Test - fun testSitePaymentClick() = - runTest(testCoroutineRule.testDispatcher) { - // Arrange - val mockToken = "4444 5555 6666 7777" - val mockAuthTokenCache: AuthTokenCache = mockk(relaxed = true) - every { mockServiceConnectionManager.authTokenCache() } returns mockAuthTokenCache - coEvery { mockAuthTokenCache.fetchAuthToken() } returns mockToken + fun testSitePaymentClick() = runTest { + // Arrange + val mockToken = "4444 5555 6666 7777" + val mockAuthTokenCache: AuthTokenCache = mockk(relaxed = true) + every { mockServiceConnectionManager.authTokenCache() } returns mockAuthTokenCache + coEvery { mockAuthTokenCache.fetchAuthToken() } returns mockToken - // Act, Assert - viewModel.uiSideEffect.test { - viewModel.onSitePaymentClick() - val action = awaitItem() - assertIs<OutOfTimeViewModel.UiSideEffect.OpenAccountView>(action) - assertEquals(mockToken, action.token) - } + // Act, Assert + viewModel.uiSideEffect.test { + viewModel.onSitePaymentClick() + val action = awaitItem() + assertIs<OutOfTimeViewModel.UiSideEffect.OpenAccountView>(action) + assertEquals(mockToken, action.token) } + } @Test - fun testUpdateTunnelState() = - runTest(testCoroutineRule.testDispatcher) { - // Arrange - val tunnelRealStateTestItem = TunnelState.Connected(mockk(), mockk()) + fun testUpdateTunnelState() = runTest { + // Arrange + val tunnelRealStateTestItem = TunnelState.Connected(mockk(), mockk()) - // Act, Assert - viewModel.uiState.test { - assertEquals(OutOfTimeUiState(deviceName = ""), awaitItem()) - eventNotifierTunnelRealState.notify(tunnelRealStateTestItem) - serviceConnectionStateFlow.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - val result = awaitItem() - assertEquals(tunnelRealStateTestItem, result.tunnelState) - } + // Act, Assert + viewModel.uiState.test { + assertEquals(OutOfTimeUiState(deviceName = ""), awaitItem()) + eventNotifierTunnelRealState.notify(tunnelRealStateTestItem) + serviceConnectionStateFlow.value = + ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) + val result = awaitItem() + assertEquals(tunnelRealStateTestItem, result.tunnelState) } + } @Test - fun testOpenConnectScreen() = - runTest(testCoroutineRule.testDispatcher) { - // Arrange - val mockExpiryDate: DateTime = mockk() - every { mockExpiryDate.isAfter(any<ReadableInstant>()) } returns true + fun testOpenConnectScreen() = runTest { + // Arrange + val mockExpiryDate: DateTime = mockk() + every { mockExpiryDate.isAfter(any<ReadableInstant>()) } returns true - // Act, Assert - viewModel.uiSideEffect.test { - outOfTimeFlow.value = false - val action = awaitItem() - assertIs<OutOfTimeViewModel.UiSideEffect.OpenConnectScreen>(action) - } + // Act, Assert + viewModel.uiSideEffect.test { + outOfTimeFlow.value = false + val action = awaitItem() + assertIs<OutOfTimeViewModel.UiSideEffect.OpenConnectScreen>(action) } + } @Test - fun testOnDisconnectClick() = - runTest(testCoroutineRule.testDispatcher) { - // Arrange - val mockProxy: ConnectionProxy = mockk(relaxed = true) - every { mockServiceConnectionManager.connectionProxy() } returns mockProxy + fun testOnDisconnectClick() = runTest { + // Arrange + val mockProxy: ConnectionProxy = mockk(relaxed = true) + every { mockServiceConnectionManager.connectionProxy() } returns mockProxy - // Act - viewModel.onDisconnectClick() + // Act + viewModel.onDisconnectClick() - // Assert - verify { mockProxy.disconnect() } - } + // Assert + verify { mockProxy.disconnect() } + } @Test fun testBillingProductsUnavailableState() = runTest { diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/PaymentViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/PaymentViewModelTest.kt index 665e23c3d4..1e2fba3c96 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/PaymentViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/PaymentViewModelTest.kt @@ -12,13 +12,13 @@ import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult import net.mullvad.mullvadvpn.usecase.PaymentUseCase import net.mullvad.mullvadvpn.util.toPaymentDialogData -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class PaymentViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val mockPaymentUseCase: PaymentUseCase = mockk(relaxed = true) @@ -26,14 +26,14 @@ class PaymentViewModelTest { private lateinit var viewModel: PaymentViewModel - @Before + @BeforeEach fun setUp() { coEvery { mockPaymentUseCase.purchaseResult } returns purchaseResult viewModel = PaymentViewModel(paymentUseCase = mockPaymentUseCase) } - @After + @AfterEach fun tearDown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModelTest.kt index 5726c6249c..6ea1a85ed2 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModelTest.kt @@ -15,13 +15,13 @@ import net.mullvad.mullvadvpn.dataproxy.SendProblemReportResult import net.mullvad.mullvadvpn.dataproxy.UserReport import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.repository.ProblemReportRepository -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class ReportProblemViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() @MockK private lateinit var mockMullvadProblemReport: MullvadProblemReport @@ -31,7 +31,7 @@ class ReportProblemViewModelTest { private lateinit var viewModel: ReportProblemViewModel - @Before + @BeforeEach fun setUp() { MockKAnnotations.init(this) coEvery { mockMullvadProblemReport.collectLogs() } returns true @@ -39,7 +39,7 @@ class ReportProblemViewModelTest { viewModel = ReportProblemViewModel(mockMullvadProblemReport, mockProblemReportRepository) } - @After + @AfterEach fun tearDown() { viewModel.viewModelScope.coroutineContext.cancel() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt index 5ad1af1182..46ea0bf3fb 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt @@ -29,13 +29,13 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.ui.serviceconnection.connectionProxy import net.mullvad.mullvadvpn.usecase.RelayListFilterUseCase import net.mullvad.mullvadvpn.usecase.RelayListUseCase -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class SelectLocationViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val mockRelayListFilterUseCase: RelayListFilterUseCase = mockk(relaxed = true) private val mockServiceConnectionManager: ServiceConnectionManager = mockk() @@ -46,7 +46,7 @@ class SelectLocationViewModelTest { private val selectedProvider = MutableStateFlow<Constraint<Providers>>(Constraint.Any()) private val allProvider = MutableStateFlow<List<Provider>>(emptyList()) - @Before + @BeforeEach fun setup() { every { mockRelayListFilterUseCase.selectedOwnership() } returns selectedOwnership @@ -64,7 +64,7 @@ class SelectLocationViewModelTest { ) } - @After + @AfterEach fun teardown() { viewModel.viewModelScope.coroutineContext.cancel() unmockkAll() diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SettingsViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SettingsViewModelTest.kt index ff6804c953..b52ab53ef6 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SettingsViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SettingsViewModelTest.kt @@ -19,13 +19,13 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState import net.mullvad.mullvadvpn.util.appVersionCallbackFlow -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class SettingsViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val mockDeviceRepository: DeviceRepository = mockk() private val mockServiceConnectionManager: ServiceConnectionManager = mockk() @@ -46,7 +46,7 @@ class SettingsViewModelTest { private lateinit var viewModel: SettingsViewModel - @Before + @BeforeEach fun setUp() { mockkStatic(CACHE_EXTENSION_CLASS) val deviceState = MutableStateFlow<DeviceState>(DeviceState.LoggedOut) @@ -68,7 +68,7 @@ class SettingsViewModelTest { ) } - @After + @AfterEach fun tearDown() { viewModel.viewModelScope.coroutineContext.cancel() unmockkAll() diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt index 5409b7f736..7b2b4cacd5 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt @@ -15,6 +15,7 @@ import java.util.concurrent.TimeUnit import kotlin.test.assertEquals import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import net.mullvad.mullvadvpn.applist.AppData import net.mullvad.mullvadvpn.applist.ApplicationsProvider @@ -24,174 +25,169 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState import net.mullvad.mullvadvpn.ui.serviceconnection.SplitTunneling -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.rules.Timeout +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Timeout +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) +@Timeout(3000L, unit = TimeUnit.MILLISECONDS) class SplitTunnelingViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() - @get:Rule val timeout = Timeout(3000L, TimeUnit.MILLISECONDS) private val mockedApplicationsProvider = mockk<ApplicationsProvider>() private val mockedSplitTunneling = mockk<SplitTunneling>() private val mockedServiceConnectionManager = mockk<ServiceConnectionManager>() private val mockedServiceConnectionContainer = mockk<ServiceConnectionContainer>() private lateinit var testSubject: SplitTunnelingViewModel - @Before + @BeforeEach fun setup() { every { mockedSplitTunneling.enabled } returns true } - @After + @AfterEach fun tearDown() { testSubject.viewModelScope.coroutineContext.cancel() unmockkAll() } @Test - fun test_has_progress_on_start() = - runTest(testCoroutineRule.testDispatcher) { - initTestSubject(emptyList()) - val actualState: SplitTunnelingUiState = testSubject.uiState.value + fun test_has_progress_on_start() = runTest { + initTestSubject(emptyList()) + val actualState: SplitTunnelingUiState = testSubject.uiState.value - val initialExpectedState = SplitTunnelingUiState.Loading + val initialExpectedState = SplitTunnelingUiState.Loading - assertEquals(initialExpectedState, actualState) + assertEquals(initialExpectedState, actualState) - verify(exactly = 1) { mockedApplicationsProvider.getAppsList() } - } + verify(exactly = 1) { mockedApplicationsProvider.getAppsList() } + } @Test - fun test_empty_app_list() = - runTest(testCoroutineRule.testDispatcher) { - every { mockedSplitTunneling.excludedAppsChange = captureLambda() } answers - { - lambda<(Set<String>) -> Unit>().invoke(emptySet()) - } - initTestSubject(emptyList()) - val expectedState = - SplitTunnelingUiState.ShowAppList( - excludedApps = emptyList(), - includedApps = emptyList(), - showSystemApps = false - ) - testSubject.uiState.test { assertEquals(expectedState, awaitItem()) } - } + fun test_empty_app_list() = runTest { + every { mockedSplitTunneling.excludedAppsChange = captureLambda() } answers + { + lambda<(Set<String>) -> Unit>().invoke(emptySet()) + } + initTestSubject(emptyList()) + val expectedState = + SplitTunnelingUiState.ShowAppList( + excludedApps = emptyList(), + includedApps = emptyList(), + showSystemApps = false + ) + testSubject.uiState.test { assertEquals(expectedState, awaitItem()) } + } @Test - fun test_apps_list_delivered() = - runTest(testCoroutineRule.testDispatcher) { - val appExcluded = AppData("test.excluded", 0, "testName1") - val appNotExcluded = AppData("test.not.excluded", 0, "testName2") - every { mockedSplitTunneling.excludedAppsChange = captureLambda() } answers - { - lambda<(Set<String>) -> Unit>().invoke(setOf(appExcluded.packageName)) - } + fun test_apps_list_delivered() = runTest { + val appExcluded = AppData("test.excluded", 0, "testName1") + val appNotExcluded = AppData("test.not.excluded", 0, "testName2") + every { mockedSplitTunneling.excludedAppsChange = captureLambda() } answers + { + lambda<(Set<String>) -> Unit>().invoke(setOf(appExcluded.packageName)) + } - initTestSubject(listOf(appExcluded, appNotExcluded)) + initTestSubject(listOf(appExcluded, appNotExcluded)) - val expectedState = - SplitTunnelingUiState.ShowAppList( - excludedApps = listOf(appExcluded), - includedApps = listOf(appNotExcluded), - showSystemApps = false - ) + val expectedState = + SplitTunnelingUiState.ShowAppList( + excludedApps = listOf(appExcluded), + includedApps = listOf(appNotExcluded), + showSystemApps = false + ) - testSubject.uiState.test { - val actualState = awaitItem() - assertEquals(expectedState, actualState) - verifyAll { - mockedSplitTunneling.enabled - mockedSplitTunneling.excludedAppsChange = any() - } + testSubject.uiState.test { + val actualState = awaitItem() + assertEquals(expectedState, actualState) + verifyAll { + mockedSplitTunneling.enabled + mockedSplitTunneling.excludedAppsChange = any() } } + } @Test - fun test_include_app() = - runTest(testCoroutineRule.testDispatcher) { - var excludedAppsCallback = slot<(Set<String>) -> Unit>() - val app = AppData("test", 0, "testName") - every { mockedSplitTunneling.includeApp(app.packageName) } just runs - every { mockedSplitTunneling.excludedAppsChange = captureLambda() } answers - { - excludedAppsCallback = lambda() - excludedAppsCallback.invoke(setOf(app.packageName)) - } + fun test_include_app() = runTest { + var excludedAppsCallback = slot<(Set<String>) -> Unit>() + val app = AppData("test", 0, "testName") + every { mockedSplitTunneling.includeApp(app.packageName) } just runs + every { mockedSplitTunneling.excludedAppsChange = captureLambda() } answers + { + excludedAppsCallback = lambda() + excludedAppsCallback.invoke(setOf(app.packageName)) + } - initTestSubject(listOf(app)) + initTestSubject(listOf(app)) - val expectedStateBeforeAction = - SplitTunnelingUiState.ShowAppList( - excludedApps = listOf(app), - includedApps = emptyList(), - showSystemApps = false - ) - val expectedStateAfterAction = - SplitTunnelingUiState.ShowAppList( - excludedApps = emptyList(), - includedApps = listOf(app), - showSystemApps = false - ) + val expectedStateBeforeAction = + SplitTunnelingUiState.ShowAppList( + excludedApps = listOf(app), + includedApps = emptyList(), + showSystemApps = false + ) + val expectedStateAfterAction = + SplitTunnelingUiState.ShowAppList( + excludedApps = emptyList(), + includedApps = listOf(app), + showSystemApps = false + ) - testSubject.uiState.test { - assertEquals(expectedStateBeforeAction, awaitItem()) - testSubject.onIncludeAppClick(app.packageName) - excludedAppsCallback.invoke(emptySet()) - assertEquals(expectedStateAfterAction, awaitItem()) + testSubject.uiState.test { + assertEquals(expectedStateBeforeAction, awaitItem()) + testSubject.onIncludeAppClick(app.packageName) + excludedAppsCallback.invoke(emptySet()) + assertEquals(expectedStateAfterAction, awaitItem()) - verifyAll { - mockedSplitTunneling.enabled - mockedSplitTunneling.excludedAppsChange = any() - mockedSplitTunneling.includeApp(app.packageName) - } + verifyAll { + mockedSplitTunneling.enabled + mockedSplitTunneling.excludedAppsChange = any() + mockedSplitTunneling.includeApp(app.packageName) } } + } @Test - fun test_add_app_to_excluded() = - runTest(testCoroutineRule.testDispatcher) { - var excludedAppsCallback = slot<(Set<String>) -> Unit>() - val app = AppData("test", 0, "testName") - every { mockedSplitTunneling.excludeApp(app.packageName) } just runs - every { mockedSplitTunneling.excludedAppsChange = captureLambda() } answers - { - excludedAppsCallback = lambda() - excludedAppsCallback.invoke(emptySet()) - } + fun test_add_app_to_excluded() = runTest { + var excludedAppsCallback = slot<(Set<String>) -> Unit>() + val app = AppData("test", 0, "testName") + every { mockedSplitTunneling.excludeApp(app.packageName) } just runs + every { mockedSplitTunneling.excludedAppsChange = captureLambda() } answers + { + excludedAppsCallback = lambda() + excludedAppsCallback.invoke(emptySet()) + } - initTestSubject(listOf(app)) + initTestSubject(listOf(app)) - val expectedStateBeforeAction = - SplitTunnelingUiState.ShowAppList( - excludedApps = emptyList(), - includedApps = listOf(app), - showSystemApps = false - ) + val expectedStateBeforeAction = + SplitTunnelingUiState.ShowAppList( + excludedApps = emptyList(), + includedApps = listOf(app), + showSystemApps = false + ) - val expectedStateAfterAction = - SplitTunnelingUiState.ShowAppList( - excludedApps = listOf(app), - includedApps = emptyList(), - showSystemApps = false - ) + val expectedStateAfterAction = + SplitTunnelingUiState.ShowAppList( + excludedApps = listOf(app), + includedApps = emptyList(), + showSystemApps = false + ) - testSubject.uiState.test { - assertEquals(expectedStateBeforeAction, awaitItem()) - testSubject.onExcludeAppClick(app.packageName) - excludedAppsCallback.invoke(setOf(app.packageName)) - assertEquals(expectedStateAfterAction, awaitItem()) + testSubject.uiState.test { + assertEquals(expectedStateBeforeAction, awaitItem()) + testSubject.onExcludeAppClick(app.packageName) + excludedAppsCallback.invoke(setOf(app.packageName)) + assertEquals(expectedStateAfterAction, awaitItem()) - verifyAll { - mockedSplitTunneling.enabled - mockedSplitTunneling.excludedAppsChange = any() - mockedSplitTunneling.excludeApp(app.packageName) - } + verifyAll { + mockedSplitTunneling.enabled + mockedSplitTunneling.excludedAppsChange = any() + mockedSplitTunneling.excludeApp(app.packageName) } } + } private fun initTestSubject(appList: List<AppData>) { every { mockedApplicationsProvider.getAppsList() } returns appList @@ -204,7 +200,7 @@ class SplitTunnelingViewModelTest { SplitTunnelingViewModel( mockedApplicationsProvider, mockedServiceConnectionManager, - testCoroutineRule.testDispatcher + UnconfinedTestDispatcher() ) } } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VoucherDialogViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VoucherDialogViewModelTest.kt index bfa068a381..7c54cd2c5e 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VoucherDialogViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VoucherDialogViewModelTest.kt @@ -22,13 +22,13 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState import net.mullvad.mullvadvpn.ui.serviceconnection.VoucherRedeemer import net.mullvad.mullvadvpn.ui.serviceconnection.voucherRedeemer -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class VoucherDialogViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val mockServiceConnectionManager: ServiceConnectionManager = mockk() private val mockServiceConnectionContainer: ServiceConnectionContainer = mockk() @@ -41,7 +41,7 @@ class VoucherDialogViewModelTest { private lateinit var viewModel: VoucherDialogViewModel - @Before + @BeforeEach fun setUp() { every { mockServiceConnectionManager.connectionState } returns serviceConnectionState @@ -52,7 +52,7 @@ class VoucherDialogViewModelTest { ) } - @After + @AfterEach fun tearDown() { unmockkAll() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt index 0ac13777cd..51bbe3057c 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt @@ -27,13 +27,13 @@ import net.mullvad.mullvadvpn.model.WireguardTunnelOptions import net.mullvad.mullvadvpn.repository.SettingsRepository import net.mullvad.mullvadvpn.usecase.PortRangeUseCase import net.mullvad.mullvadvpn.usecase.RelayListUseCase -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class VpnSettingsViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val mockSettingsRepository: SettingsRepository = mockk() private val mockResources: Resources = mockk() @@ -45,7 +45,7 @@ class VpnSettingsViewModelTest { private lateinit var viewModel: VpnSettingsViewModel - @Before + @BeforeEach fun setUp() { every { mockSettingsRepository.settingsUpdates } returns mockSettingsUpdate every { mockPortRangeUseCase.portRanges() } returns portRangeFlow @@ -60,7 +60,7 @@ class VpnSettingsViewModelTest { ) } - @After + @AfterEach fun tearDown() { viewModel.viewModelScope.coroutineContext.cancel() unmockkAll() diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt index 29b0dfde75..d0461a4ee5 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt @@ -37,13 +37,13 @@ import net.mullvad.mullvadvpn.usecase.PaymentUseCase import net.mullvad.talpid.util.EventNotifier import org.joda.time.DateTime import org.joda.time.ReadableInstant -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(TestCoroutineRule::class) class WelcomeViewModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() private val serviceConnectionStateFlow = MutableStateFlow<ServiceConnectionState>(ServiceConnectionState.Disconnected) @@ -68,7 +68,7 @@ class WelcomeViewModelTest { private lateinit var viewModel: WelcomeViewModel - @Before + @BeforeEach fun setUp() { mockkStatic(SERVICE_CONNECTION_MANAGER_EXTENSIONS) mockkStatic(PURCHASE_RESULT_EXTENSIONS_CLASS) @@ -101,46 +101,44 @@ class WelcomeViewModelTest { ) } - @After + @AfterEach fun tearDown() { viewModel.viewModelScope.coroutineContext.cancel() unmockkAll() } @Test - fun testSitePaymentClick() = - runTest(testCoroutineRule.testDispatcher) { - // Arrange - val mockToken = "4444 5555 6666 7777" - val mockAuthTokenCache: AuthTokenCache = mockk(relaxed = true) - every { mockServiceConnectionManager.authTokenCache() } returns mockAuthTokenCache - coEvery { mockAuthTokenCache.fetchAuthToken() } returns mockToken + fun testSitePaymentClick() = runTest { + // Arrange + val mockToken = "4444 5555 6666 7777" + val mockAuthTokenCache: AuthTokenCache = mockk(relaxed = true) + every { mockServiceConnectionManager.authTokenCache() } returns mockAuthTokenCache + coEvery { mockAuthTokenCache.fetchAuthToken() } returns mockToken - // Act, Assert - viewModel.uiSideEffect.test { - viewModel.onSitePaymentClick() - val action = awaitItem() - assertIs<WelcomeViewModel.UiSideEffect.OpenAccountView>(action) - assertEquals(mockToken, action.token) - } + // Act, Assert + viewModel.uiSideEffect.test { + viewModel.onSitePaymentClick() + val action = awaitItem() + assertIs<WelcomeViewModel.UiSideEffect.OpenAccountView>(action) + assertEquals(mockToken, action.token) } + } @Test - fun testUpdateTunnelState() = - runTest(testCoroutineRule.testDispatcher) { - // Arrange - val tunnelUiStateTestItem = TunnelState.Connected(mockk(), mockk()) + fun testUpdateTunnelState() = runTest { + // Arrange + val tunnelUiStateTestItem = TunnelState.Connected(mockk(), mockk()) - // Act, Assert - viewModel.uiState.test { - assertEquals(WelcomeUiState(), awaitItem()) - eventNotifierTunnelUiState.notify(tunnelUiStateTestItem) - serviceConnectionStateFlow.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - val result = awaitItem() - assertEquals(tunnelUiStateTestItem, result.tunnelState) - } + // Act, Assert + viewModel.uiState.test { + assertEquals(WelcomeUiState(), awaitItem()) + eventNotifierTunnelUiState.notify(tunnelUiStateTestItem) + serviceConnectionStateFlow.value = + ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) + val result = awaitItem() + assertEquals(tunnelUiStateTestItem, result.tunnelState) } + } @Test fun testUpdateAccountNumber() = runTest { @@ -165,19 +163,18 @@ class WelcomeViewModelTest { } @Test - fun testOpenConnectScreen() = - runTest(testCoroutineRule.testDispatcher) { - // Arrange - val mockExpiryDate: DateTime = mockk() - every { mockExpiryDate.isAfter(any<ReadableInstant>()) } returns true + fun testOpenConnectScreen() = runTest { + // Arrange + val mockExpiryDate: DateTime = mockk() + every { mockExpiryDate.isAfter(any<ReadableInstant>()) } returns true - // Act, Assert - viewModel.uiSideEffect.test { - outOfTimeFlow.value = false - val action = awaitItem() - assertIs<WelcomeViewModel.UiSideEffect.OpenConnectScreen>(action) - } + // Act, Assert + viewModel.uiSideEffect.test { + outOfTimeFlow.value = false + val action = awaitItem() + assertIs<WelcomeViewModel.UiSideEffect.OpenConnectScreen>(action) } + } @Test fun testBillingProductsUnavailableState() = runTest { |
