summaryrefslogtreecommitdiffhomepage
path: root/android/app/src
diff options
context:
space:
mode:
authorJonatan Rhodin <jonatan.rhodin@mullvad.net>2023-05-12 13:04:51 +0200
committerAlbin <albin@mullvad.net>2023-05-23 16:27:32 +0200
commita4f34db77fbe413f6c1c6eae396608571c24ca06 (patch)
tree503a9619f7299a5ba8765c57cbeb6c916c8c2565 /android/app/src
parent893b8bd55ac6d926a4eda240956530677782aa3b (diff)
downloadmullvadvpn-a4f34db77fbe413f6c1c6eae396608571c24ca06.tar.xz
mullvadvpn-a4f34db77fbe413f6c1c6eae396608571c24ca06.zip
Migrate Split tunneling instrumental tests
Remove SplitTunnelingFragmentTest Add SplitTunnelingScreenTest
Diffstat (limited to 'android/app/src')
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreenTest.kt220
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragmentTest.kt164
2 files changed, 220 insertions, 164 deletions
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
new file mode 100644
index 0000000000..087c4b72a7
--- /dev/null
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreenTest.kt
@@ -0,0 +1,220 @@
+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.every
+import io.mockk.mockk
+import io.mockk.unmockkAll
+import io.mockk.verify
+import net.mullvad.mullvadvpn.applist.AppData
+import net.mullvad.mullvadvpn.applist.ApplicationsIconManager
+import net.mullvad.mullvadvpn.compose.state.SplitTunnelingUiState
+import net.mullvad.mullvadvpn.di.APPS_SCOPE
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.koin.core.context.loadKoinModules
+import org.koin.core.context.unloadKoinModules
+import org.koin.core.qualifier.named
+import org.koin.core.scope.Scope
+import org.koin.dsl.module
+import org.koin.java.KoinJavaComponent.getKoin
+
+class SplitTunnelingScreenTest {
+ @get:Rule val composeTestRule = createComposeRule()
+ private lateinit var scope: Scope
+
+ private val testModule = module {
+ scope(named(APPS_SCOPE)) {
+ scoped {
+ mockk<ApplicationsIconManager>().apply {
+ every { getAppIcon(any()) } returns mockk(relaxed = true)
+ }
+ }
+ }
+ }
+
+ @Before
+ fun setup() {
+ MockKAnnotations.init(this)
+ loadKoinModules(testModule)
+ scope = getKoin().getOrCreateScope(APPS_SCOPE, named(APPS_SCOPE))
+ }
+
+ @After
+ fun tearDown() {
+ scope.close()
+ unloadKoinModules(testModule)
+ unmockkAll()
+ }
+
+ @Test
+ fun testLoadingState() {
+ // Arrange
+ composeTestRule.setContent { SplitTunnelingScreen(uiState = SplitTunnelingUiState.Loading) }
+
+ // Assert
+ composeTestRule.apply {
+ 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.setContent {
+ SplitTunnelingScreen(
+ uiState =
+ SplitTunnelingUiState.Data(
+ excludedApps = listOf(excludedApp),
+ includedApps = listOf(includedApp),
+ showSystemApps = false
+ )
+ )
+ }
+
+ // Assert
+ composeTestRule.apply {
+ onNodeWithText(TITLE).assertExists()
+ onNodeWithText(DESCRIPTION).assertExists()
+ onNodeWithText(EXCLUDED_APPLICATIONS).assertExists()
+ onNodeWithText(EXCLUDED_APP_NAME).assertExists()
+ onNodeWithText(SHOW_SYSTEM_APPS).assertExists()
+ 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.setContent {
+ SplitTunnelingScreen(
+ uiState =
+ SplitTunnelingUiState.Data(
+ excludedApps = emptyList(),
+ includedApps = listOf(includedApp),
+ showSystemApps = false
+ )
+ )
+ }
+
+ // Assert
+ composeTestRule.apply {
+ onNodeWithText(TITLE).assertExists()
+ onNodeWithText(DESCRIPTION).assertExists()
+ onNodeWithText(EXCLUDED_APPLICATIONS).assertDoesNotExist()
+ onNodeWithText(EXCLUDED_APP_NAME).assertDoesNotExist()
+ onNodeWithText(SHOW_SYSTEM_APPS).assertExists()
+ 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.setContent {
+ SplitTunnelingScreen(
+ uiState =
+ SplitTunnelingUiState.Data(
+ excludedApps = listOf(excludedApp),
+ includedApps = listOf(includedApp),
+ showSystemApps = false
+ ),
+ addToExcluded = mockedClickHandler
+ )
+ }
+
+ // Act
+ composeTestRule.onNodeWithText(INCLUDED_APP_NAME).performClick()
+
+ // 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.setContent {
+ SplitTunnelingScreen(
+ uiState =
+ SplitTunnelingUiState.Data(
+ excludedApps = listOf(excludedApp),
+ includedApps = listOf(includedApp),
+ showSystemApps = false
+ ),
+ removeFromExcluded = mockedClickHandler
+ )
+ }
+
+ // Act
+ composeTestRule.onNodeWithText(EXCLUDED_APP_NAME).performClick()
+
+ // 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.setContent {
+ SplitTunnelingScreen(
+ uiState =
+ SplitTunnelingUiState.Data(
+ excludedApps = listOf(excludedApp),
+ includedApps = listOf(includedApp),
+ showSystemApps = false
+ ),
+ onShowSystemAppsClicked = mockedClickHandler
+ )
+ }
+
+ // Act
+ composeTestRule.onNodeWithText(SHOW_SYSTEM_APPS).performClick()
+
+ // Assert
+ verify { mockedClickHandler.invoke(true) }
+ }
+
+ companion object {
+ private const val EXCLUDED_APP_PACKAGE_NAME = "excluded-pkg"
+ private const val EXCLUDED_APP_NAME = "Excluded Name"
+ private const val INCLUDED_APP_PACKAGE_NAME = "included-pkg"
+ private const val INCLUDED_APP_NAME = "Included Name"
+ private const val TITLE = "Split tunneling"
+ private const val DESCRIPTION =
+ "Split tunneling makes it possible to select which applications should not be routed through the VPN tunnel."
+ private const val EXCLUDED_APPLICATIONS = "Excluded applications"
+ private const val SHOW_SYSTEM_APPS = "Show system apps"
+ private const val ALL_APPLICATIONS = "All applications"
+ }
+}
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragmentTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragmentTest.kt
deleted file mode 100644
index f0b6d0a24d..0000000000
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragmentTest.kt
+++ /dev/null
@@ -1,164 +0,0 @@
-package net.mullvad.mullvadvpn.ui.fragment
-
-import androidx.fragment.app.testing.launchFragmentInContainer
-import androidx.lifecycle.Lifecycle
-import androidx.test.espresso.Espresso.onView
-import androidx.test.espresso.action.ViewActions.click
-import androidx.test.espresso.assertion.ViewAssertions.matches
-import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
-import androidx.test.espresso.matcher.ViewMatchers.withId
-import androidx.test.espresso.matcher.ViewMatchers.withText
-import androidx.test.filters.LargeTest
-import androidx.test.runner.AndroidJUnit4
-import io.mockk.Runs
-import io.mockk.coEvery
-import io.mockk.coVerifyAll
-import io.mockk.every
-import io.mockk.just
-import io.mockk.mockk
-import io.mockk.mockkClass
-import io.mockk.unmockkAll
-import io.mockk.verifyAll
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.runBlocking
-import net.mullvad.mullvadvpn.R
-import net.mullvad.mullvadvpn.RecyclerViewMatcher.Companion.withRecyclerView
-import net.mullvad.mullvadvpn.applist.ApplicationsIconManager
-import net.mullvad.mullvadvpn.applist.ViewIntent
-import net.mullvad.mullvadvpn.di.APPS_SCOPE
-import net.mullvad.mullvadvpn.model.ListItemData
-import net.mullvad.mullvadvpn.model.WidgetState
-import net.mullvad.mullvadvpn.viewmodel.SplitTunnelingViewModel
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.koin.core.context.loadKoinModules
-import org.koin.core.context.unloadKoinModules
-import org.koin.core.qualifier.named
-import org.koin.core.scope.Scope
-import org.koin.dsl.module
-import org.koin.test.KoinTest
-import org.koin.test.mock.MockProviderRule
-import org.koin.test.mock.declareMock
-
-@RunWith(AndroidJUnit4::class)
-@LargeTest
-class SplitTunnelingFragmentTest : KoinTest {
-
- private val mockedViewModel = mockk<SplitTunnelingViewModel>(relaxUnitFun = true)
- private val sharedFlow = MutableSharedFlow<List<ListItemData>>()
- private lateinit var scope: Scope
-
- private val testModule = module {
- scope(named(APPS_SCOPE)) {
- scoped {
- mockk<ApplicationsIconManager>().apply {
- every { getAppIcon(any()) } returns mockk(relaxed = true)
- }
- }
- }
- }
-
- @get:Rule
- val mockProvider =
- MockProviderRule.create { clazz ->
- when (clazz) {
- SplitTunnelingViewModel::class -> mockedViewModel
- else -> mockkClass(clazz)
- }
- }
-
- @Before
- fun setUp() {
- loadKoinModules(testModule)
- scope = getKoin().getOrCreateScope(APPS_SCOPE, named(APPS_SCOPE))
- scope.declareMock<SplitTunnelingViewModel>()
- every { mockedViewModel.listItems } returns sharedFlow
- coEvery { mockedViewModel.processIntent(ViewIntent.ViewIsReady) } just Runs
- }
-
- @After
- fun tearDown() {
- scope.close()
- unloadKoinModules(testModule)
- unmockkAll()
- }
-
- @Test
- fun test_fragment_title() {
- launchFragmentInContainer<SplitTunnelingFragment>(themeResId = R.style.AppTheme)
-
- onView(withId(R.id.collapsing_toolbar))
- .check(matches(withContentDescription("Split tunneling")))
- }
-
- @Test
- fun test_fragment_loading() {
- val scenario =
- launchFragmentInContainer<SplitTunnelingFragment>(
- themeResId = R.style.AppTheme,
- initialState = Lifecycle.State.CREATED
- )
- scenario.moveToState(Lifecycle.State.RESUMED)
- sharedFlow.tryEmit(emptyList())
-
- verifyAll { mockedViewModel.listItems }
- }
-
- @Test
- fun test_fragment_list_displayed() = runBlocking {
- launchFragmentInContainer<SplitTunnelingFragment>(
- themeResId = R.style.AppTheme,
- initialState = Lifecycle.State.RESUMED
- )
-
- sharedFlow.emit(
- listOf(
- ListItemData.build("testItem") {
- type = ListItemData.PLAIN
- text = "Test Item"
- action = ListItemData.ItemAction(text.toString())
- }
- )
- )
-
- onView(withRecyclerView(R.id.recyclerView).atPositionOnView(0, R.id.plain_text))
- .check(matches(withText("Test Item")))
-
- verifyAll { mockedViewModel.listItems }
- }
-
- @Test
- fun test_fragment_list_click_application_item() = runBlocking {
- val testListItem =
- ListItemData.build("test.package.name") {
- type = ListItemData.APPLICATION
- text = "Test App Name"
- action = ListItemData.ItemAction("test.package.name")
- widget = WidgetState.ImageState(R.drawable.ic_icons_add)
- }
-
- coEvery {
- mockedViewModel.processIntent(ViewIntent.ChangeApplicationGroup(testListItem))
- } just Runs
-
- launchFragmentInContainer<SplitTunnelingFragment>(
- themeResId = R.style.AppTheme,
- initialState = Lifecycle.State.RESUMED
- )
-
- sharedFlow.emit(listOf(testListItem))
-
- onView(withRecyclerView(R.id.recyclerView).atPositionOnView(0, R.id.itemText))
- .check(matches(withText("Test App Name")))
-
- onView(withRecyclerView(R.id.recyclerView).atPositionOnView(0)).perform(click())
-
- coVerifyAll {
- mockedViewModel.listItems
- mockedViewModel.processIntent(ViewIntent.ChangeApplicationGroup(testListItem))
- }
- }
-}