Skip to content

Commit

Permalink
Merge branch 'create-server-ip-overrides-composable-droid-709'
Browse files Browse the repository at this point in the history
  • Loading branch information
Rawa committed Mar 20, 2024
2 parents fc7a0c2 + cb19d35 commit 2b40cfa
Show file tree
Hide file tree
Showing 64 changed files with 1,969 additions and 42 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Line wrap the file at 100 chars. Th
- Add auto connect and lockdown mode guide on platforms that has system vpn settings.
- Add 3D map to Connect screen.
- Add the ability to create and manage custom lists of relays.
- Add Server IP overrides feature.

### Changed
- Change default obfuscation setting to `auto`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package net.mullvad.mullvadvpn.compose.dialog

import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import io.mockk.MockKAnnotations
import io.mockk.mockk
import io.mockk.verify
import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension
import net.mullvad.mullvadvpn.compose.setContentWithTheme
import net.mullvad.mullvadvpn.compose.test.RESET_SERVER_IP_OVERRIDE_CANCEL_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.RESET_SERVER_IP_OVERRIDE_RESET_TEST_TAG
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension

class ResetServerIPOverridesConfirmationDialogTest {
@OptIn(ExperimentalTestApi::class)
@JvmField
@RegisterExtension
val composeExtension = createEdgeToEdgeComposeExtension()

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

@Test
fun ensure_cancel_click_works() =
composeExtension.use {
val clickHandler: () -> Unit = mockk(relaxed = true)

// Arrange
setContentWithTheme {
ResetServerIpOverridesConfirmationDialog(
onNavigateBack = clickHandler,
onClearAllOverrides = {}
)
}

// Act
onNodeWithTag(RESET_SERVER_IP_OVERRIDE_CANCEL_TEST_TAG).performClick()

// Assert
verify { clickHandler() }
}

@Test
fun ensure_reset_click_works() =
composeExtension.use {
val clickHandler: () -> Unit = mockk(relaxed = true)

// Arrange
setContentWithTheme {
ResetServerIpOverridesConfirmationDialog(
onNavigateBack = {},
onClearAllOverrides = clickHandler
)
}

// Act
onNodeWithTag(RESET_SERVER_IP_OVERRIDE_RESET_TEST_TAG).performClick()

// Assert
verify { clickHandler() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package net.mullvad.mullvadvpn.compose.screen

import androidx.compose.runtime.Composable
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 io.mockk.MockKAnnotations
import io.mockk.mockk
import io.mockk.verify
import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension
import net.mullvad.mullvadvpn.compose.setContentWithTheme
import net.mullvad.mullvadvpn.compose.test.SERVER_IP_OVERRIDES_IMPORT_BY_FILE_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.SERVER_IP_OVERRIDES_IMPORT_BY_TEXT_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.SERVER_IP_OVERRIDE_IMPORT_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.SERVER_IP_OVERRIDE_INFO_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.SERVER_IP_OVERRIDE_MORE_VERT_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.SERVER_IP_OVERRIDE_RESET_OVERRIDES_TEST_TAG
import net.mullvad.mullvadvpn.viewmodel.ServerIpOverridesViewState
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension

@ExperimentalTestApi
class ServerIpOverridesScreenTest {
@JvmField @RegisterExtension val composeExtension = createEdgeToEdgeComposeExtension()

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

@Suppress("TestFunctionName")
@Composable
private fun ScreenWithDefault(
state: ServerIpOverridesViewState,
onBackClick: () -> Unit = {},
onInfoClick: () -> Unit = {},
onResetOverridesClick: () -> Unit = {},
onImportByFile: () -> Unit = {},
onImportByText: () -> Unit = {},
) {
ServerIpOverridesScreen(
state = state,
onBackClick = onBackClick,
onInfoClick = onInfoClick,
onResetOverridesClick = onResetOverridesClick,
onImportByFile = onImportByFile,
onImportByText = onImportByText
)
}

@Test
fun ensure_overrides_inactive_is_displayed() =
composeExtension.use {
// Arrange
setContentWithTheme {
ScreenWithDefault(state = ServerIpOverridesViewState.Loaded(false))
}

// Assert
onNodeWithText("Overrides inactive").assertExists()
}

@Test
fun ensure_overrides_active_is_displayed() =
composeExtension.use {
// Arrange
setContentWithTheme {
ScreenWithDefault(state = ServerIpOverridesViewState.Loaded(true))
}

// Assert
onNodeWithText("Overrides active").assertExists()
}

@Test
fun ensure_overrides_active_shows_warning_on_import() =
composeExtension.use {
// Arrange
setContentWithTheme {
ScreenWithDefault(state = ServerIpOverridesViewState.Loaded(true))
}

// Act
onNodeWithTag(testTag = SERVER_IP_OVERRIDE_IMPORT_TEST_TAG).performClick()

// Assert
onNodeWithText(
"Importing new overrides might replace some previously imported overrides."
)
.assertExists()
}

@Test
fun ensure_info_click_works() =
composeExtension.use {
// Arrange
val clickHandler: () -> Unit = mockk(relaxed = true)
setContentWithTheme {
ScreenWithDefault(
state = ServerIpOverridesViewState.Loaded(false),
onInfoClick = clickHandler
)
}

// Act
onNodeWithTag(SERVER_IP_OVERRIDE_INFO_TEST_TAG).performClick()

// Assert
verify { clickHandler() }
}

@Test
fun ensure_reset_click_works() =
composeExtension.use {
// Arrange
val clickHandler: () -> Unit = mockk(relaxed = true)
setContentWithTheme {
ScreenWithDefault(
state = ServerIpOverridesViewState.Loaded(true),
onResetOverridesClick = clickHandler
)
}

// Act
onNodeWithTag(SERVER_IP_OVERRIDE_MORE_VERT_TEST_TAG).performClick()
onNodeWithTag(SERVER_IP_OVERRIDE_RESET_OVERRIDES_TEST_TAG).performClick()

// Assert
verify { clickHandler() }
}

@Test
fun ensure_import_by_file_works() =
composeExtension.use {
// Arrange
val clickHandler: () -> Unit = mockk(relaxed = true)
setContentWithTheme {
ScreenWithDefault(
state = ServerIpOverridesViewState.Loaded(false),
onImportByFile = clickHandler
)
}

// Act
onNodeWithTag(SERVER_IP_OVERRIDE_IMPORT_TEST_TAG).performClick()
onNodeWithTag(SERVER_IP_OVERRIDES_IMPORT_BY_FILE_TEST_TAG).performClick()

// Assert
verify { clickHandler() }
}

@Test
fun ensure_import_by_text() =
composeExtension.use {
// Arrange
val clickHandler: () -> Unit = mockk(relaxed = true)
setContentWithTheme {
ScreenWithDefault(
state = ServerIpOverridesViewState.Loaded(false),
onImportByText = clickHandler
)
}

// Act
onNodeWithTag(SERVER_IP_OVERRIDE_IMPORT_TEST_TAG).performClick()
onNodeWithTag(SERVER_IP_OVERRIDES_IMPORT_BY_TEXT_TEST_TAG).performClick()

// Assert
verify { clickHandler() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package net.mullvad.mullvadvpn.compose.button

import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import net.mullvad.mullvadvpn.R

@Composable
fun InfoIconButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
contentDescription: String? = null,
iconTint: Color = MaterialTheme.colorScheme.onPrimary
) {
IconButton(modifier = modifier, onClick = onClick) {
Icon(
painter = painterResource(id = R.drawable.icon_info),
contentDescription = contentDescription,
tint = iconTint
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ private fun PreviewIconCell() {
@Composable
fun IconCell(
iconId: Int?,
contentDescription: String? = null,
title: String,
modifier: Modifier = Modifier,
contentDescription: String? = null,
titleStyle: TextStyle = MaterialTheme.typography.labelLarge,
titleColor: Color = MaterialTheme.colorScheme.onPrimary,
onClick: () -> Unit = {},
background: Color = MaterialTheme.colorScheme.primary,
enabled: Boolean = true,
enabled: Boolean = true
) {
BaseCell(
headlineContent = {
Expand All @@ -49,6 +50,7 @@ fun IconCell(
},
onCellClicked = onClick,
background = background,
isRowEnabled = enabled
isRowEnabled = enabled,
modifier = modifier
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package net.mullvad.mullvadvpn.compose.cell

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.component.MullvadCircularProgressIndicatorSmall
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.lib.theme.color.AlphaInactive
import net.mullvad.mullvadvpn.lib.theme.color.AlphaVisible
import net.mullvad.mullvadvpn.lib.theme.color.selected

@Preview
@Composable
private fun PreviewServerIpOverridesCell() {
AppTheme { ServerIpOverridesCell(active = true) }
}

@Composable
fun ServerIpOverridesCell(
active: Boolean?,
modifier: Modifier = Modifier,
activeColor: Color = MaterialTheme.colorScheme.selected,
inactiveColor: Color = MaterialTheme.colorScheme.error,
) {
BaseCell(
modifier = modifier,
iconView = {
if (active == null) {
MullvadCircularProgressIndicatorSmall()
} else {
Box(
modifier =
Modifier.size(Dimens.relayCircleSize)
.background(
color =
when {
active -> activeColor
else -> inactiveColor
},
shape = CircleShape
)
)
}
},
headlineContent = {
if (active != null) {
Text(
text =
if (active) stringResource(id = R.string.server_ip_overrides_active)
else stringResource(id = R.string.server_ip_overrides_inactive),
color = MaterialTheme.colorScheme.onPrimary,
modifier =
Modifier.weight(1f)
.alpha(
if (active) {
AlphaVisible
} else {
AlphaInactive
}
)
.padding(
horizontal = Dimens.smallPadding,
vertical = Dimens.mediumPadding
)
)
}
},
isRowEnabled = false
)
}
Loading

0 comments on commit 2b40cfa

Please sign in to comment.