Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement multihop UI #7036

Merged
merged 5 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions android/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Line wrap the file at 100 chars. Th
### Added
- Add a new access method: Encrypted DNS Proxy. Encrypted DNS proxy is a way to reach the API via
proxies. The access method is enabled by default.
- Add multihop which allows the routing of traffic through an entry and exit server, making it
harder to trace.

### Changed
- Animation has been changed to look better with predictive back.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@ class ConnectScreenTest {
val inPort = 99
val inProtocol = TransportProtocol.Udp
every { mockLocation.hostname } returns mockHostName
every { mockLocation.entryHostname } returns null

// In
every { mockTunnelEndpoint.obfuscation } returns null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,7 @@ class CustomListLocationsScreenTest {
}

// Assert
onNodeWithText(EMPTY_SEARCH_FIRST_ROW.format(mockSearchString), substring = true)
.assertExists()
onNodeWithText(EMPTY_SEARCH_SECOND_ROW, substring = true).assertExists()
onNodeWithText(EMPTY_SEARCH.format(mockSearchString)).assertExists()
}

@Test
Expand Down Expand Up @@ -239,8 +237,7 @@ class CustomListLocationsScreenTest {
const val ADD_LOCATIONS_TEXT = "Add locations"
const val EDIT_LOCATIONS_TEXT = "Edit locations"
const val SEARCH_PLACEHOLDER = "Search for..."
const val EMPTY_SEARCH_FIRST_ROW = "No result for %s."
const val EMPTY_SEARCH_SECOND_ROW = "Try a different search"
const val EMPTY_SEARCH = "No result for \"%s\", please try a different search"
const val NO_LOCATIONS_FOUND_TEXT = "No locations found"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class SettingsScreenTest {
isLoggedIn = true,
isSupportedVersion = true,
isPlayBuild = false,
multihopEnabled = false,
)
)
}
Expand All @@ -56,6 +57,7 @@ class SettingsScreenTest {
isLoggedIn = false,
isSupportedVersion = true,
isPlayBuild = false,
multihopEnabled = false,
)
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package net.mullvad.mullvadvpn.compose.screen.location

import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performTextInput
import io.mockk.MockKAnnotations
import io.mockk.mockk
import io.mockk.unmockkAll
import io.mockk.verify
import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension
import net.mullvad.mullvadvpn.compose.data.DUMMY_RELAY_ITEM_CUSTOM_LISTS
import net.mullvad.mullvadvpn.compose.setContentWithTheme
import net.mullvad.mullvadvpn.compose.state.RelayListItem
import net.mullvad.mullvadvpn.compose.state.SearchLocationUiState
import net.mullvad.mullvadvpn.compose.test.SELECT_LOCATION_CUSTOM_LIST_HEADER_TEST_TAG
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension

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

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

@AfterEach
fun teardown() {
unmockkAll()
}

@Test
fun testSearchInput() =
composeExtension.use {
// Arrange
val mockedSearchTermInput: (String) -> Unit = mockk(relaxed = true)
setContentWithTheme {
SearchLocationScreen(
state =
SearchLocationUiState.NoQuery(searchTerm = "", filterChips = emptyList()),
onSearchInputChanged = mockedSearchTermInput,
)
}
val mockSearchString = "SEARCH"

// Act
onNodeWithText("Search for...").performTextInput(mockSearchString)

// Assert
verify { mockedSearchTermInput.invoke(mockSearchString) }
}

@Test
fun testSearchTermNotFound() =
composeExtension.use {
// Arrange
val mockSearchString = "SEARCH"
setContentWithTheme {
SearchLocationScreen(
state =
SearchLocationUiState.Content(
searchTerm = mockSearchString,
filterChips = emptyList(),
relayListItems =
listOf(RelayListItem.LocationsEmptyText(mockSearchString)),
customLists = emptyList(),
)
)
}

// Assert
onNodeWithText("No result for \"$mockSearchString\", please try a different search")
.assertExists()
}

@Test
fun givenNoCustomListsAndSearchIsActiveShouldNotShowCustomListHeader() =
composeExtension.use {
// Arrange
val mockSearchString = "SEARCH"
setContentWithTheme {
SearchLocationScreen(
state =
SearchLocationUiState.Content(
searchTerm = mockSearchString,
filterChips = emptyList(),
relayListItems = emptyList(),
customLists = DUMMY_RELAY_ITEM_CUSTOM_LISTS,
)
)
}

// Assert
onNodeWithText(CUSTOM_LISTS_EMPTY_TEXT).assertDoesNotExist()
onNodeWithTag(SELECT_LOCATION_CUSTOM_LIST_HEADER_TEST_TAG).assertDoesNotExist()
}

companion object {
private const val CUSTOM_LISTS_EMPTY_TEXT = "To create a custom list press the \"︙\""
}
}
Loading
Loading