Skip to content

Commit

Permalink
Migrate all navigation to compose navigation
Browse files Browse the repository at this point in the history
Remove old fragments

Improve animations between destinations

Fix glitch in animation

Temporary fix for pending intent flags

Fix status bar colors

Fix transition for welcome screen

Fix bug caused by triggering navigation too fast

Fix accidentally not starting service

Handle Change log

Fix screen rotation

Make settings use scaffold snackbar instead

Migrate info dialogs to destinations

Fix formatting

Fix some failed tests

Remove unused function

Refactor out MTU dialog

Migrate DnsDialog

Fix Devicelist confirmation dialog

Migrate ReportProblemNoEmail dialog

Migrate custom WG port to dialog

Fix voucher dialog and out of time navigation from connect

Fix tests

Update gradle lockfile

Disable settings button while logging in

Add nav graph

Fix out of time navigation, transitions and multiple navigation calls.

Fix xml formatting

Fix test

Add CVE supression

Clean up build config

Remove duplicate deps

Fix test

Fix VPNSettings UI tests

Fix out of time bug!

Remove notification limitation

bump destinations version

Fix setting onclick

Move out getActivity

Fix remarks

Fix remarks

Fix timeout

Fix timeout

clean up dns dialog

Clarify OutOfTime usacase

Handle billing error

Overlay splash screen on no service connection

Hide billing started dialog

Handle service down with destination

Update gradle verification file

Handle navigation from welcome/outoftime to connect

Remove unnecessary mock init

Fix remarks

Update graphs

Add Login to Connect/OutOfTime/Welcome transition

Update transition set

Update transitions

Rework NoDaemon overlay logic

Fix tests

Fix tests

Rework out of time
  • Loading branch information
Rawa committed Dec 8, 2023
1 parent 6f66168 commit 96166c7
Show file tree
Hide file tree
Showing 157 changed files with 4,438 additions and 4,519 deletions.
8 changes: 6 additions & 2 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ plugins {
id(Dependencies.Plugin.playPublisherId)
id(Dependencies.Plugin.kotlinAndroidId)
id(Dependencies.Plugin.kotlinParcelizeId)
id(Dependencies.Plugin.ksp) version Versions.Plugin.ksp
}

val repoRootPath = rootProject.projectDir.absoluteFile.parentFile.absolutePath
val extraAssetsDirectory = "${project.buildDir}/extraAssets"
val defaultChangeLogAssetsDirectory = "$repoRootPath/android/src/main/play/release-notes/"
val defaultChangelogAssetsDirectory = "$repoRootPath/android/src/main/play/release-notes/"
val extraJniDirectory = "${project.buildDir}/extraJni"

val credentialsPath = "${rootProject.projectDir}/credentials"
Expand Down Expand Up @@ -111,7 +112,7 @@ android {
getByName("main") {
val changelogDir =
gradleLocalProperties(rootProject.projectDir)
.getOrDefault("OVERRIDE_CHANGELOG_DIR", defaultChangeLogAssetsDirectory)
.getOrDefault("OVERRIDE_CHANGELOG_DIR", defaultChangelogAssetsDirectory)

assets.srcDirs(extraAssetsDirectory, changelogDir)
jniLibs.srcDirs(extraJniDirectory)
Expand Down Expand Up @@ -337,6 +338,9 @@ dependencies {
implementation(Dependencies.Compose.uiController)
implementation(Dependencies.Compose.ui)
implementation(Dependencies.Compose.uiUtil)
implementation(Dependencies.Compose.destinations)
ksp(Dependencies.Compose.destinationsKsp)

implementation(Dependencies.jodaTime)
implementation(Dependencies.Koin.core)
implementation(Dependencies.Koin.android)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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.onNodeWithTag
import androidx.compose.ui.test.performTextInput
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

class CustomPortDialogTest {
@get:Rule val composeTestRule = createComposeRule()

@Before
fun setup() {
MockKAnnotations.init(this)
}

@SuppressLint("ComposableNaming")
@Composable
private fun testWireguardCustomPortDialog(
initialPort: Int? = null,
allowedPortRanges: List<PortRange> = emptyList(),
onSave: (Int?) -> Unit = { _ -> },
onDismiss: () -> Unit = {},
) {

WireguardCustomPortDialog(
initialPort = initialPort,
allowedPortRanges = allowedPortRanges,
onSave = onSave,
onDismiss = onDismiss
)
}

@Test
fun testShowWireguardCustomPortDialogInvalidInt() {
// 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() }

// Act
composeTestRule
.onNodeWithTag(CUSTOM_PORT_DIALOG_INPUT_TEST_TAG)
.performTextInput(invalidCustomPort)

// Assert
composeTestRule
.onNodeWithTagAndText(CUSTOM_PORT_DIALOG_INPUT_TEST_TAG, invalidCustomPort)
.assertDoesNotExist()
}

companion object {
const val invalidCustomPort = "21474836471"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package net.mullvad.mullvadvpn.compose.dialog

import android.annotation.SuppressLint
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import net.mullvad.mullvadvpn.compose.setContentWithTheme
import net.mullvad.mullvadvpn.viewmodel.DnsDialogViewState
import org.junit.Rule
import org.junit.Test

class DnsDialogTest {
@get:Rule val composeTestRule = createComposeRule()

private val defaultState =
DnsDialogViewState(
ipAddress = "",
validationResult = DnsDialogViewState.ValidationResult.Success,
isLocal = false,
isAllowLanEnabled = false,
isNewEntry = true
)

@SuppressLint("ComposableNaming")
@Composable
private fun testDnsDialog(
state: DnsDialogViewState = defaultState,
onDnsInputChange: (String) -> Unit = { _ -> },
onSaveDnsClick: () -> Unit = {},
onRemoveDnsClick: () -> Unit = {},
onDismiss: () -> Unit = {}
) {
DnsDialog(state, onDnsInputChange, onSaveDnsClick, onRemoveDnsClick, onDismiss)
}

@Test
fun testDnsDialogLanWarningShownWhenLanTrafficDisabledAndLocalAddressUsed() {
// Arrange
composeTestRule.setContentWithTheme {
testDnsDialog(defaultState.copy(isAllowLanEnabled = false, isLocal = true))
}

// Assert
composeTestRule.onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertExists()
}

@Test
fun testDnsDialogLanWarningNotShownWhenLanTrafficEnabledAndLocalAddressUsed() {
// Arrange
composeTestRule.setContentWithTheme {
testDnsDialog(defaultState.copy(isAllowLanEnabled = true, isLocal = true))
}

// Assert
composeTestRule.onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist()
}

@Test
fun testDnsDialogLanWarningNotShownWhenLanTrafficEnabledAndNonLocalAddressUsed() {
// Arrange
composeTestRule.setContentWithTheme {
testDnsDialog(defaultState.copy(isAllowLanEnabled = true, isLocal = false))
}

// Assert
composeTestRule.onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist()
}

@Test
fun testDnsDialogLanWarningNotShownWhenLanTrafficDisabledAndNonLocalAddressUsed() {
// Arrange
composeTestRule.setContentWithTheme {
testDnsDialog(defaultState.copy(isAllowLanEnabled = false, isLocal = false))
}

// Assert
composeTestRule.onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist()
}

@Test
fun testDnsDialogSubmitButtonDisabledOnInvalidDnsAddress() {
// Arrange
composeTestRule.setContentWithTheme {
testDnsDialog(
defaultState.copy(
ipAddress = invalidIpAddress,
validationResult = DnsDialogViewState.ValidationResult.InvalidAddress,
)
)
}

// Assert
composeTestRule.onNodeWithText("Submit").assertIsNotEnabled()
}

@Test
fun testDnsDialogSubmitButtonDisabledOnDuplicateDnsAddress() {
// Arrange
composeTestRule.setContentWithTheme {
testDnsDialog(
defaultState.copy(
ipAddress = "192.168.0.1",
validationResult = DnsDialogViewState.ValidationResult.DuplicateAddress,
)
)
}

// Assert
composeTestRule.onNodeWithText("Submit").assertIsNotEnabled()
}

companion object {
private const val LOCAL_DNS_SERVER_WARNING =
"The local DNS server will not work unless you enable " +
"\"Local Network Sharing\" under Preferences."

private const val invalidIpAddress = "300.300.300.300"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package net.mullvad.mullvadvpn.compose.dialog

import android.annotation.SuppressLint
import androidx.compose.runtime.Composable
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 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

class MtuDialogTest {
@get:Rule val composeTestRule = createComposeRule()

@Before
fun setup() {
MockKAnnotations.init(this)
}

@SuppressLint("ComposableNaming")
@Composable
private fun testMtuDialog(
mtuInitial: Int? = null,
onSaveMtu: (Int) -> Unit = { _ -> },
onResetMtu: () -> Unit = {},
onDismiss: () -> Unit = {},
) {
MtuDialog(
mtuInitial = mtuInitial,
onSaveMtu = onSaveMtu,
onResetMtu = onResetMtu,
onDismiss = onDismiss
)
}

@Test
fun testMtuDialogWithDefaultValue() {
// Arrange
composeTestRule.setContentWithTheme { testMtuDialog() }

// Assert
composeTestRule.onNodeWithText(EMPTY_STRING).assertExists()
}

@Test
fun testMtuDialogWithEditValue() {
// Arrange
composeTestRule.setContentWithTheme {
testMtuDialog(
mtuInitial = VALID_DUMMY_MTU_VALUE,
)
}

// Assert
composeTestRule.onNodeWithText(VALID_DUMMY_MTU_VALUE.toString()).assertExists()
}

@Test
fun testMtuDialogTextInput() {
// Arrange
composeTestRule.setContentWithTheme {
testMtuDialog(
null,
)
}

// Act
composeTestRule
.onNodeWithText(EMPTY_STRING)
.performTextInput(VALID_DUMMY_MTU_VALUE.toString())

// Assert
composeTestRule.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,
)
}

// Act
composeTestRule.onNodeWithText("Submit").assertIsEnabled().performClick()

// Assert
verify { mockedSubmitHandler.invoke(VALID_DUMMY_MTU_VALUE) }
}

@Test
fun testMtuDialogSubmitButtonDisabledWhenInvalidInput() {
// Arrange
composeTestRule.setContentWithTheme {
testMtuDialog(
INVALID_DUMMY_MTU_VALUE,
)
}

// Assert
composeTestRule.onNodeWithText("Submit").assertIsNotEnabled()
}

@Test
fun testMtuDialogResetClick() {
// Arrange
val mockedClickHandler: () -> Unit = mockk(relaxed = true)
composeTestRule.setContentWithTheme {
testMtuDialog(
onResetMtu = mockedClickHandler,
)
}

// Act
composeTestRule.onNodeWithText("Reset to default").performClick()

// Assert
verify { mockedClickHandler.invoke() }
}

@Test
fun testMtuDialogCancelClick() {
// Arrange
val mockedClickHandler: () -> Unit = mockk(relaxed = true)
composeTestRule.setContentWithTheme {
testMtuDialog(
onDismiss = mockedClickHandler,
)
}

// Assert
composeTestRule.onNodeWithText("Cancel").performClick()

// Assert
verify { mockedClickHandler.invoke() }
}

companion object {
private const val EMPTY_STRING = ""
private const val VALID_DUMMY_MTU_VALUE = 1337
private const val INVALID_DUMMY_MTU_VALUE = 1111
}
}
Loading

0 comments on commit 96166c7

Please sign in to comment.