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 new file mode 100644 index 000000000000..edb86e3414ca --- /dev/null +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreenTest.kt @@ -0,0 +1,191 @@ +package net.mullvad.mullvadvpn.compose.screen + +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import io.mockk.MockKAnnotations +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.flow.MutableSharedFlow +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 + +class FilterScreenTest { + @get:Rule val composeTestRule = createComposeRule() + + fun setup() { + MockKAnnotations.init(this) + } + + @Test + fun testDefaultState() { + composeTestRule.setContentWithTheme { + FilterScreen( + uiState = + RelayFilterState( + allProviders = DUMMY_RELAY_ALL_PROVIDERS, + selectedOwnership = null, + selectedProviders = DUMMY_SELECTED_PROVIDERS, + ), + uiCloseAction = MutableSharedFlow(), + onSelectedProviders = { _, _ -> } + ) + } + composeTestRule.apply { + 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 + ), + uiCloseAction = MutableSharedFlow(), + onSelectedProviders = { _, _ -> } + ) + } + composeTestRule.apply { + 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 + ), + uiCloseAction = MutableSharedFlow(), + onSelectedProviders = { _, _ -> } + ) + } + composeTestRule.apply { + onNodeWithText("Ownership").performClick() + onNodeWithText("Mullvad owned only").performClick() + } + } + + @Test + fun testIsRentedCellShowing() { + composeTestRule.setContentWithTheme { + FilterScreen( + uiState = + RelayFilterState( + allProviders = DUMMY_RELAY_ALL_PROVIDERS, + selectedOwnership = Ownership.Rented, + selectedProviders = DUMMY_SELECTED_PROVIDERS + ), + uiCloseAction = MutableSharedFlow(), + onSelectedProviders = { _, _ -> } + ) + } + composeTestRule.apply { + 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 + ), + uiCloseAction = MutableSharedFlow(), + onSelectedProviders = { _, _ -> } + ) + } + + 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)) + ), + uiCloseAction = MutableSharedFlow(), + onSelectedProviders = { _, _ -> }, + onApplyClick = mockClickListener + ) + } + composeTestRule.onNodeWithText("Apply").performClick() + verify { mockClickListener() } + } + + companion object { + + private val DUMMY_RELAY_ALL_PROVIDERS = + listOf( + Provider("31173", true), + Provider("100TB", false), + Provider("Blix", true), + Provider("Creanova", true), + Provider("DataPacket", false), + Provider("HostRoyale", false), + Provider("hostuniversal", false), + Provider("iRegister", false), + Provider("M247", false), + Provider("Makonix", false), + Provider("PrivateLayer", false), + Provider("ptisp", false), + Provider("Qnax", false), + Provider("Quadranet", false), + Provider("techfutures", false), + Provider("Tzulo", false), + Provider("xtom", false) + ) + + private val DUMMY_SELECTED_PROVIDERS = + listOf( + Provider("31173", true), + Provider("100TB", false), + Provider("Blix", true), + Provider("Creanova", true), + Provider("DataPacket", false), + Provider("HostRoyale", false), + Provider("hostuniversal", false), + Provider("iRegister", false), + Provider("M247", false), + Provider("Makonix", false), + Provider("PrivateLayer", false), + Provider("ptisp", false), + Provider("Qnax", false), + Provider("Quadranet", false), + Provider("techfutures", false), + Provider("Tzulo", false), + Provider("xtom", false) + ) + } +} 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 new file mode 100644 index 000000000000..032e53feecff --- /dev/null +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/FilterViewModelTest.kt @@ -0,0 +1,127 @@ +package net.mullvad.mullvadvpn.viewmodel + +import androidx.lifecycle.viewModelScope +import app.cash.turbine.test +import io.mockk.coVerify +import io.mockk.every +import io.mockk.mockk +import io.mockk.unmockkAll +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import net.mullvad.mullvadvpn.compose.state.toConstraintProviders +import net.mullvad.mullvadvpn.compose.state.toOwnershipConstraint +import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule +import net.mullvad.mullvadvpn.lib.common.test.assertLists +import net.mullvad.mullvadvpn.model.Constraint +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 kotlin.test.assertEquals + +class FilterViewModelTest { + @get:Rule val testCoroutineRule = TestCoroutineRule() + private val mockRelayListFilterUseCase: RelayListFilterUseCase = mockk(relaxed = true) + private lateinit var viewModel: FilterViewModel + private val selectedOwnership = + MutableStateFlow>(Constraint.Only(Ownership.MullvadOwned)) + private val mockAllProviders = + listOf( + Provider("31173", true), + Provider("100TB", false), + Provider("Blix", true), + Provider("Creanova", true), + Provider("DataPacket", false), + Provider("HostRoyale", false), + Provider("hostuniversal", false), + Provider("iRegister", false), + Provider("M247", false), + Provider("Makonix", false), + Provider("PrivateLayer", false), + Provider("ptisp", false), + Provider("Qnax", false), + Provider("Quadranet", false), + Provider("techfutures", false), + Provider("Tzulo", false), + Provider("xtom", false) + ) + private val mockSelectedProviders: List = + listOf(Provider("31173", true), Provider("Blix", true), Provider("Creanova", true)) + + @Before + fun setup() { + every { mockRelayListFilterUseCase.selectedOwnership() } returns selectedOwnership + every { mockRelayListFilterUseCase.availableProviders() } returns flowOf(mockAllProviders) + every { mockRelayListFilterUseCase.selectedProviders() } returns + flowOf(Constraint.Only(Providers(mockSelectedProviders.map { it.name }.toHashSet()))) + viewModel = FilterViewModel(mockRelayListFilterUseCase) + } + + @After + fun teardown() { + viewModel.viewModelScope.coroutineContext.cancel() + unmockkAll() + } + + @Test + fun testSetSelectedOwnership() = runTest { + // Arrange + val mockOwnership = Ownership.Rented + // Assert + viewModel.uiState.test { + assertEquals(awaitItem().selectedOwnership, Ownership.MullvadOwned) + viewModel.setSelectedOwnership(mockOwnership) + assertEquals(mockOwnership, awaitItem().selectedOwnership) + } + } + + @Test + fun testSetSelectedProvider() = runTest { + // Arrange + val mockSelectedProvidersList = Provider("ptisp", false) + // Assert + viewModel.uiState.test { + assertLists(awaitItem().selectedProviders, mockSelectedProviders) + viewModel.setSelectedProvider(true, mockSelectedProvidersList) + assertLists( + listOf(mockSelectedProvidersList) + mockSelectedProviders, + awaitItem().selectedProviders + ) + } + } + + @Test + fun testSetAllProviders() = runTest { + // Arrange + val mockProvidersList = mockAllProviders + // Act + viewModel.setAllProviders(true) + // Assert + viewModel.uiState.test { + val state = awaitItem() + assertEquals(mockProvidersList, state.selectedProviders) + } + } + + @Test + fun testOnApplyButtonClicked() = runTest { + // Arrange + val mockOwnership = Ownership.MullvadOwned.toOwnershipConstraint() + val mockSelectedProviders = mockSelectedProviders.toConstraintProviders(mockAllProviders) + // Act + viewModel.onApplyButtonClicked() + // Assert + coVerify { + mockRelayListFilterUseCase.updateOwnershipAndProviderFilter( + mockOwnership, + mockSelectedProviders + ) + } + } +}