diff options
| author | saber safavi <saber.safavi@codic.se> | 2023-07-28 15:53:47 +0200 |
|---|---|---|
| committer | saber safavi <saber.safavi@codic.se> | 2023-07-28 16:54:16 +0200 |
| commit | f96271feeac04dcf946bea3adc5ca0e0bd48a3aa (patch) | |
| tree | eeefa81c128999cc2baa2edfa61b64b5030df664 /android | |
| parent | a202afc1ecd9c07ccda3efb06e044ecd1d4c9a34 (diff) | |
| download | mullvadvpn-f96271feeac04dcf946bea3adc5ca0e0bd48a3aa.tar.xz mullvadvpn-f96271feeac04dcf946bea3adc5ca0e0bd48a3aa.zip | |
Add UI and VM unit tests
Diffstat (limited to 'android')
2 files changed, 225 insertions, 0 deletions
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt new file mode 100644 index 0000000000..e616f67449 --- /dev/null +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt @@ -0,0 +1,128 @@ +package net.mullvad.mullvadvpn.compose.screen + +import androidx.compose.material3.ExperimentalMaterial3Api +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 kotlinx.coroutines.flow.asSharedFlow +import net.mullvad.mullvadvpn.compose.state.AccountUiState +import net.mullvad.mullvadvpn.viewmodel.AccountViewModel +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class AccountScreenTest { + @get:Rule val composeTestRule = createComposeRule() + + @Before + fun setup() { + MockKAnnotations.init(this) + } + + @OptIn(ExperimentalMaterial3Api::class) + @Test + fun testDefaultState() { + // Arrange + composeTestRule.setContent { + AccountScreen( + uiState = + AccountUiState( + deviceName = DUMMY_DEVICE_NAME, + accountNumber = DUMMY_ACCOUNT_NUMBER, + accountExpiry = null + ), + viewActions = MutableSharedFlow<AccountViewModel.ViewAction>().asSharedFlow() + ) + } + + // Assert + composeTestRule.apply { + onNodeWithText("Redeem voucher").assertExists() + onNodeWithText("Log out").assertExists() + } + } + + @OptIn(ExperimentalMaterial3Api::class) + @Test + fun testManageAccountClick() { + // Arrange + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + composeTestRule.setContent { + AccountScreen( + uiState = + AccountUiState( + deviceName = DUMMY_DEVICE_NAME, + accountNumber = DUMMY_ACCOUNT_NUMBER, + accountExpiry = null + ), + viewActions = MutableSharedFlow<AccountViewModel.ViewAction>().asSharedFlow(), + onManageAccountClick = mockedClickHandler + ) + } + + // Act + composeTestRule.onNodeWithText("Manage account").performClick() + + // Assert + verify { mockedClickHandler.invoke() } + } + + @OptIn(ExperimentalMaterial3Api::class) + @Test + fun testRedeemVoucherClick() { + // Arrange + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + composeTestRule.setContent { + AccountScreen( + uiState = + AccountUiState( + deviceName = DUMMY_DEVICE_NAME, + accountNumber = DUMMY_ACCOUNT_NUMBER, + accountExpiry = null + ), + viewActions = MutableSharedFlow<AccountViewModel.ViewAction>().asSharedFlow(), + onRedeemVoucherClick = mockedClickHandler + ) + } + + // Act + composeTestRule.onNodeWithText("Redeem voucher").performClick() + + // Assert + verify { mockedClickHandler.invoke() } + } + + @OptIn(ExperimentalMaterial3Api::class) + @Test + fun testLogoutClick() { + // Arrange + val mockedClickHandler: () -> Unit = mockk(relaxed = true) + composeTestRule.setContent { + AccountScreen( + uiState = + AccountUiState( + deviceName = DUMMY_DEVICE_NAME, + accountNumber = DUMMY_ACCOUNT_NUMBER, + accountExpiry = null + ), + viewActions = MutableSharedFlow<AccountViewModel.ViewAction>().asSharedFlow(), + onLogoutClick = mockedClickHandler + ) + } + + // Act + composeTestRule.onNodeWithText("Log out").performClick() + + // Assert + verify { mockedClickHandler.invoke() } + } + + companion object { + private const val DUMMY_DEVICE_NAME = "fake_name" + private const val DUMMY_ACCOUNT_NUMBER = "fake_number" + } +} 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 new file mode 100644 index 0000000000..e5a3f2b397 --- /dev/null +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModelTest.kt @@ -0,0 +1,97 @@ +package net.mullvad.mullvadvpn.viewmodel + +import app.cash.turbine.test +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import io.mockk.verify +import kotlin.test.assertEquals +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.runTest +import net.mullvad.mullvadvpn.TestCoroutineRule +import net.mullvad.mullvadvpn.model.AccountAndDevice +import net.mullvad.mullvadvpn.model.AccountExpiry +import net.mullvad.mullvadvpn.model.Device +import net.mullvad.mullvadvpn.model.DeviceState +import net.mullvad.mullvadvpn.repository.AccountRepository +import net.mullvad.mullvadvpn.repository.DeviceRepository +import net.mullvad.mullvadvpn.ui.serviceconnection.AuthTokenCache +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager +import net.mullvad.mullvadvpn.ui.serviceconnection.authTokenCache +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class AccountViewModelTest { + @get:Rule val testCoroutineRule = TestCoroutineRule() + + private val mockAccountRepository: AccountRepository = mockk(relaxUnitFun = true) + private val mockServiceConnectionManager: ServiceConnectionManager = mockk() + private val mockDeviceRepository: DeviceRepository = mockk() + private val mockAuthTokenCache: AuthTokenCache = mockk() + + private val deviceState: MutableStateFlow<DeviceState> = MutableStateFlow(DeviceState.Initial) + private val accountExpiryState = MutableStateFlow(AccountExpiry.Missing) + + private val dummyAccountAndDevice: AccountAndDevice = + AccountAndDevice( + DUMMY_DEVICE_NAME, + Device( + id = "fake_id", + name = "fake_name", + pubkey = byteArrayOf(), + ports = ArrayList(), + created = "mock_date" + ) + ) + + private lateinit var viewModel: AccountViewModel + + @Before + fun setUp() { + mockkStatic(CACHE_EXTENSION_CLASS) + every { mockServiceConnectionManager.authTokenCache() } returns mockAuthTokenCache + every { mockDeviceRepository.deviceState } returns deviceState + every { mockAccountRepository.accountExpiryState } returns accountExpiryState + + viewModel = + AccountViewModel( + accountRepository = mockAccountRepository, + serviceConnectionManager = mockServiceConnectionManager, + deviceRepository = mockDeviceRepository + ) + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun testAccountLoggedInState() = runTest { + // Act, Assert + viewModel.uiState.test { + var result = awaitItem() + assertEquals("", result.deviceName) + deviceState.value = DeviceState.LoggedIn(accountAndDevice = dummyAccountAndDevice) + result = awaitItem() + assertEquals(DUMMY_DEVICE_NAME, result.accountNumber) + } + } + + @Test + fun testOnLogoutClick() { + // Act + viewModel.onLogoutClick() + + // Assert + verify { mockAccountRepository.logout() } + } + + companion object { + private const val CACHE_EXTENSION_CLASS = "net.mullvad.mullvadvpn.util.CacheExtensionsKt" + private const val DUMMY_DEVICE_NAME = "fake_name" + } +} |
