Skip to content

Commit

Permalink
Add api access method details ui tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Pururun committed Jun 11, 2024
1 parent e09efc0 commit a4c0b3d
Show file tree
Hide file tree
Showing 6 changed files with 304 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import net.mullvad.mullvadvpn.lib.model.ApiAccessMethod
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodId
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodName
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodType
import net.mullvad.mullvadvpn.lib.model.Cipher
import net.mullvad.mullvadvpn.lib.model.Port

private const val UUID1 = "12345678-1234-5678-1234-567812345678"
private const val UUID2 = "12345678-1234-5678-1234-567812345679"

val DIRECT_ACCESS_METHOD =
ApiAccessMethod(
Expand All @@ -14,3 +17,17 @@ val DIRECT_ACCESS_METHOD =
enabled = true,
apiAccessMethodType = ApiAccessMethodType.Direct
)

val CUSTOM_ACCESS_METHOD =
ApiAccessMethod(
id = ApiAccessMethodId.fromString(UUID2),
name = ApiAccessMethodName.fromString("ShadowSocks"),
enabled = true,
apiAccessMethodType =
ApiAccessMethodType.CustomProxy.Shadowsocks(
ip = "1.1.1.1",
port = Port(123),
password = "Password",
cipher = Cipher.RC4
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
package net.mullvad.mullvadvpn.compose.screen

import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import io.mockk.mockk
import io.mockk.verify
import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension
import net.mullvad.mullvadvpn.compose.data.CUSTOM_ACCESS_METHOD
import net.mullvad.mullvadvpn.compose.data.DIRECT_ACCESS_METHOD
import net.mullvad.mullvadvpn.compose.setContentWithTheme
import net.mullvad.mullvadvpn.compose.state.ApiAccessMethodDetailsUiState
import net.mullvad.mullvadvpn.compose.test.API_ACCESS_DETAILS_EDIT_BUTTON
import net.mullvad.mullvadvpn.compose.test.API_ACCESS_DETAILS_TOP_BAR_DROPDOWN_BUTTON_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.API_ACCESS_TEST_METHOD_BUTTON
import net.mullvad.mullvadvpn.compose.test.API_ACCESS_USE_METHOD_BUTTON
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodId
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension

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

@Test
fun whenApiAccessMethodIsNotEditableShouldNotShowDeleteAndEdit() =
composeExtension.use {
// Arrange
val apiAccessMethod = DIRECT_ACCESS_METHOD
setContentWithTheme {
ApiAccessMethodDetailsScreen(
state =
ApiAccessMethodDetailsUiState.Content(
apiAccessMethodId = apiAccessMethod.id,
name = apiAccessMethod.name,
enabled = apiAccessMethod.enabled,
isEditable = false,
isDisableable = true,
isCurrentMethod = true,
testApiAccessMethodState = null
)
)
}

// Assert
onNodeWithTag(API_ACCESS_DETAILS_TOP_BAR_DROPDOWN_BUTTON_TEST_TAG).assertDoesNotExist()
onNodeWithTag(API_ACCESS_DETAILS_EDIT_BUTTON).assertDoesNotExist()
}

@Test
fun whenApiAccessMethodIsNotDisableableShouldNotBeAbleDisable() =
composeExtension.use {
// Arrange
val onEnableClicked: (Boolean) -> Unit = mockk(relaxed = true)
val apiAccessMethod = DIRECT_ACCESS_METHOD
setContentWithTheme {
ApiAccessMethodDetailsScreen(
state =
ApiAccessMethodDetailsUiState.Content(
apiAccessMethodId = apiAccessMethod.id,
name = apiAccessMethod.name,
enabled = apiAccessMethod.enabled,
isEditable = false,
isDisableable = false,
isCurrentMethod = true,
testApiAccessMethodState = null
),
onEnableClicked = onEnableClicked
)
}

// Act
onNodeWithText("Enable method").performClick()

// Assert
onNodeWithText("At least one method needs to be enabled")
verify(exactly = 0) { onEnableClicked(any()) }
}

@Test
fun whenApiAccessMethodIsCurrentMethodShouldNotBeAbleToSetCurrent() =
composeExtension.use {
// Arrange
val onUseMethodClicked: () -> Unit = mockk(relaxed = true)
val apiAccessMethod = DIRECT_ACCESS_METHOD
setContentWithTheme {
ApiAccessMethodDetailsScreen(
state =
ApiAccessMethodDetailsUiState.Content(
apiAccessMethodId = apiAccessMethod.id,
name = apiAccessMethod.name,
enabled = apiAccessMethod.enabled,
isEditable = false,
isDisableable = false,
isCurrentMethod = true,
testApiAccessMethodState = null
),
onUseMethodClicked = onUseMethodClicked
)
}

// Act
onNodeWithTag(API_ACCESS_USE_METHOD_BUTTON).performClick()

// Assert
onNodeWithTag(API_ACCESS_USE_METHOD_BUTTON).assertIsNotEnabled()
onNodeWithText("This is already set as current")
verify(exactly = 0) { onUseMethodClicked() }
}

@Test
fun whenClickingOnDeleteMethodShouldCallOnDeleteApiAccessMethodClicked() =
composeExtension.use {
// Arrange
val onDeleteApiAccessMethodClicked: (ApiAccessMethodId) -> Unit = mockk(relaxed = true)
val apiAccessMethod = CUSTOM_ACCESS_METHOD
setContentWithTheme {
ApiAccessMethodDetailsScreen(
state =
ApiAccessMethodDetailsUiState.Content(
apiAccessMethodId = apiAccessMethod.id,
name = apiAccessMethod.name,
enabled = apiAccessMethod.enabled,
isEditable = true,
isDisableable = false,
isCurrentMethod = true,
testApiAccessMethodState = null
),
onDeleteApiAccessMethodClicked = onDeleteApiAccessMethodClicked
)
}

// Act
onNodeWithTag(API_ACCESS_DETAILS_TOP_BAR_DROPDOWN_BUTTON_TEST_TAG).performClick()
onNodeWithText("Delete method").performClick()

// Assert
verify(exactly = 1) { onDeleteApiAccessMethodClicked(apiAccessMethod.id) }
}

@Test
fun whenClickingOnEditMethodShouldCallOnEditMethodClicked() =
composeExtension.use {
// Arrange
val onEditMethodClicked: () -> Unit = mockk(relaxed = true)
val apiAccessMethod = CUSTOM_ACCESS_METHOD
setContentWithTheme {
ApiAccessMethodDetailsScreen(
state =
ApiAccessMethodDetailsUiState.Content(
apiAccessMethodId = apiAccessMethod.id,
name = apiAccessMethod.name,
enabled = apiAccessMethod.enabled,
isEditable = true,
isDisableable = false,
isCurrentMethod = true,
testApiAccessMethodState = null
),
onEditMethodClicked = onEditMethodClicked
)
}

// Act
onNodeWithTag(API_ACCESS_DETAILS_EDIT_BUTTON).performClick()

// Assert
verify(exactly = 1) { onEditMethodClicked() }
}

@Test
fun whenClickingOnEnableMethodShouldCallOnEnableClicked() =
composeExtension.use {
// Arrange
val onEnableClicked: (Boolean) -> Unit = mockk(relaxed = true)
val apiAccessMethod = DIRECT_ACCESS_METHOD
setContentWithTheme {
ApiAccessMethodDetailsScreen(
state =
ApiAccessMethodDetailsUiState.Content(
apiAccessMethodId = apiAccessMethod.id,
name = apiAccessMethod.name,
enabled = apiAccessMethod.enabled,
isEditable = false,
isDisableable = true,
isCurrentMethod = true,
testApiAccessMethodState = null
),
onEnableClicked = onEnableClicked
)
}

// Act
onNodeWithText("Enable method").performClick()

// Assert
verify(exactly = 1) { onEnableClicked(false) }
}

@Test
fun whenClickingOnTestMethodShouldCallOnTestMethodClicked() =
composeExtension.use {
// Arrange
val onTestMethodClicked: () -> Unit = mockk(relaxed = true)
val apiAccessMethod = DIRECT_ACCESS_METHOD
setContentWithTheme {
ApiAccessMethodDetailsScreen(
state =
ApiAccessMethodDetailsUiState.Content(
apiAccessMethodId = apiAccessMethod.id,
name = apiAccessMethod.name,
enabled = apiAccessMethod.enabled,
isEditable = false,
isDisableable = true,
isCurrentMethod = true,
testApiAccessMethodState = null
),
onTestMethodClicked = onTestMethodClicked
)
}

// Act
onNodeWithTag(API_ACCESS_TEST_METHOD_BUTTON).performClick()

// Assert
verify(exactly = 1) { onTestMethodClicked() }
}

@Test
fun whenClickingOnUseMethodShouldCallOnUseMethodClicked() =
composeExtension.use {
// Arrange
val onUseMethodClicked: () -> Unit = mockk(relaxed = true)
val apiAccessMethod = DIRECT_ACCESS_METHOD
setContentWithTheme {
ApiAccessMethodDetailsScreen(
state =
ApiAccessMethodDetailsUiState.Content(
apiAccessMethodId = apiAccessMethod.id,
name = apiAccessMethod.name,
enabled = apiAccessMethod.enabled,
isEditable = false,
isDisableable = true,
isCurrentMethod = false,
testApiAccessMethodState = null
),
onUseMethodClicked = onUseMethodClicked
)
}

// Act
onNodeWithTag(API_ACCESS_USE_METHOD_BUTTON).performClick()

// Assert
verify(exactly = 1) { onUseMethodClicked() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ fun NavigationComposeCell(
showWarning: Boolean = false,
bodyView: @Composable () -> Unit = { DefaultNavigationView(chevronContentDescription = title) },
isRowEnabled: Boolean = true,
onClick: () -> Unit
onClick: () -> Unit,
testTag: String = ""
) {
BaseCell(
onCellClicked = onClick,
Expand All @@ -79,7 +80,8 @@ fun NavigationComposeCell(
)
},
bodyView = { bodyView() },
isRowEnabled = isRowEnabled
isRowEnabled = isRowEnabled,
testTag = testTag
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ import net.mullvad.mullvadvpn.compose.destinations.DeleteApiAccessMethodConfirma
import net.mullvad.mullvadvpn.compose.destinations.EditApiAccessMethodDestination
import net.mullvad.mullvadvpn.compose.preview.ApiAccessMethodDetailsUiStatePreviewParameterProvider
import net.mullvad.mullvadvpn.compose.state.ApiAccessMethodDetailsUiState
import net.mullvad.mullvadvpn.compose.test.API_ACCESS_DETAILS_EDIT_BUTTON
import net.mullvad.mullvadvpn.compose.test.API_ACCESS_DETAILS_TOP_BAR_DROPDOWN_BUTTON_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.API_ACCESS_TEST_METHOD_BUTTON
import net.mullvad.mullvadvpn.compose.test.API_ACCESS_USE_METHOD_BUTTON
import net.mullvad.mullvadvpn.compose.test.DELETE_DROPDOWN_MENU_ITEM_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.TOP_BAR_DROPDOWN_BUTTON_TEST_TAG
import net.mullvad.mullvadvpn.compose.transitions.SlideInFromRightTransition
import net.mullvad.mullvadvpn.compose.util.LaunchedEffectCollect
import net.mullvad.mullvadvpn.compose.util.OnNavResultValue
Expand Down Expand Up @@ -176,7 +179,8 @@ private fun Content(
if (state.isEditable) {
NavigationComposeCell(
title = stringResource(id = R.string.edit_method),
onClick = onEditMethodClicked
onClick = onEditMethodClicked,
testTag = API_ACCESS_DETAILS_EDIT_BUTTON
)
HorizontalDivider()
}
Expand All @@ -193,14 +197,16 @@ private fun Content(
}
Spacer(modifier = Modifier.height(Dimens.verticalSpace))
TestMethodButton(
modifier = Modifier.padding(horizontal = Dimens.sideMargin),
modifier =
Modifier.padding(horizontal = Dimens.sideMargin).testTag(API_ACCESS_TEST_METHOD_BUTTON),
testMethodState = state.testApiAccessMethodState,
onTestMethod = onTestMethodClicked
)
Spacer(modifier = Modifier.height(Dimens.verticalSpace))
PrimaryButton(
isEnabled = !state.isCurrentMethod,
modifier = Modifier.padding(horizontal = Dimens.sideMargin),
modifier =
Modifier.padding(horizontal = Dimens.sideMargin).testTag(API_ACCESS_USE_METHOD_BUTTON),
onClick = onUseMethodClicked,
text = stringResource(id = R.string.use_method)
)
Expand All @@ -214,7 +220,7 @@ private fun Actions(onDeleteAccessMethod: () -> Unit) {
var showMenu by remember { mutableStateOf(false) }
IconButton(
onClick = { showMenu = true },
modifier = Modifier.testTag(TOP_BAR_DROPDOWN_BUTTON_TEST_TAG)
modifier = Modifier.testTag(API_ACCESS_DETAILS_TOP_BAR_DROPDOWN_BUTTON_TEST_TAG)
) {
Icon(painter = painterResource(id = R.drawable.icon_more_vert), contentDescription = null)
if (showMenu) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,10 +506,9 @@ private fun EnableAuthentication(
painter = painterResource(id = R.drawable.icon_tick),
contentDescription = null,
modifier =
Modifier.padding(end = Dimens.selectableCellTextMargin)
.alpha(
if (authenticationEnabled.not()) AlphaVisible else AlphaInvisible
)
Modifier.alpha(
if (authenticationEnabled.not()) AlphaVisible else AlphaInvisible
)
)
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,15 @@ const val SAVE_API_ACCESS_METHOD_CANCEL_BUTTON_TEST_TAG =
const val SAVE_API_ACCESS_METHOD_SAVE_BUTTON_TEST_TAG =
"save_api_access_method_save_button_test_tag"

// ApiAccessList
// ApiAccessListScreen
const val API_ACCESS_LIST_INFO_TEST_TAG = "api_access_list_info_test_tag"

// ApiAccessMethodDetailsScreen
const val API_ACCESS_DETAILS_TOP_BAR_DROPDOWN_BUTTON_TEST_TAG =
"api_access_details_top_bar_dropdown_button_test_tag"
const val API_ACCESS_DETAILS_EDIT_BUTTON =
"api_access_details_edit_button_test_tag"
const val API_ACCESS_USE_METHOD_BUTTON =
"api_access_details_use_method_test_tag"
const val API_ACCESS_TEST_METHOD_BUTTON =
"api_access_details_test_method_test_tag"

0 comments on commit a4c0b3d

Please sign in to comment.