Skip to content

Commit

Permalink
Migrate remaining e2e tests to POP
Browse files Browse the repository at this point in the history
  • Loading branch information
niklasberglund authored and Rawa committed Dec 13, 2024
1 parent 37e90ed commit d34461a
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,15 @@ class AppInteractor(
device.wait(Until.hasObject(By.pkg(targetPackageName).depth(0)), LONG_TIMEOUT)
}

fun launchAndEnsureLoggedIn(accountNumber: String) {
fun launchAndEnsureOnLoginPage() {
launch()
device.clickAgreeOnPrivacyDisclaimer()
device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove()
waitForLoginPrompt()
}

fun launchAndEnsureLoggedIn(accountNumber: String) {
launchAndEnsureOnLoginPage()
attemptLogin(accountNumber)
device.dismissChangelogDialogIfShown()
ensureLoggedIn()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package net.mullvad.mullvadvpn.test.common.page

import androidx.test.uiautomator.By
import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout

class AccountPage internal constructor() : Page() {
private val logOutSelector = By.text("Log out")

override fun assertIsDisplayed() {
uiDevice.findObjectWithTimeout(By.text("Account"))
}

fun clickLogOut() {
uiDevice.findObjectWithTimeout(logOutSelector).click()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class ConnectPage internal constructor() : Page() {
private val cancelSelector = By.text("Cancel")
private val connectedSelector = By.text("CONNECTED")
private val connectingSelector = By.text("CONNECTING...")
private val disconnectedSelector = By.text("DISCONNECTED")

override fun assertIsDisplayed() {
uiDevice.findObjectWithTimeout(By.res(CONNECT_CARD_HEADER_TEST_TAG))
Expand All @@ -34,6 +35,10 @@ class ConnectPage internal constructor() : Page() {
uiDevice.findObjectWithTimeout(connectedSelector, timeout)
}

fun waitForDisconnectedLabel(timeout: Long = VERY_LONG_TIMEOUT) {
uiDevice.findObjectWithTimeout(disconnectedSelector, timeout)
}

fun waitForConnectingLabel() {
uiDevice.findObjectWithTimeout(connectingSelector)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package net.mullvad.mullvadvpn.test.common.page

import androidx.test.uiautomator.By
import net.mullvad.mullvadvpn.test.common.constant.VERY_LONG_TIMEOUT
import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout

class MullvadWebsite internal constructor() : Page() {
override fun assertIsDisplayed() {
uiDevice.findObjectWithTimeout(
selector = By.text("Mullvad help center"),
timeout = VERY_LONG_TIMEOUT,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout

class SettingsPage internal constructor() : Page() {
private val settingsSelector = By.text("Settings")
private val faqAndGuidesSelector = By.text("FAQs & Guides")

override fun assertIsDisplayed() {
uiDevice.findObjectWithTimeout(settingsSelector)
Expand All @@ -14,6 +15,10 @@ class SettingsPage internal constructor() : Page() {
uiDevice.findObjectWithTimeout(By.res(VPN_SETTINGS_CELL_TEST_TAG)).click()
}

fun clickFaqAndGuides() {
uiDevice.findObjectWithTimeout(faqAndGuidesSelector).click()
}

companion object {
const val VPN_SETTINGS_CELL_TEST_TAG = "vpn_settings_cell_test_tag"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,18 @@ class VpnSettingsPage internal constructor() : Page() {
scrollView2.scrollUntil(Direction.DOWN, Until.hasObject(By.res(testTag)))
}

fun clickWireguardCustomPort() {
uiDevice.findObjectWithTimeout(By.res(WIREGUARD_CUSTOM_PORT_CELL_TEST_TAG)).click()
}

companion object {
const val SETTINGS_SCROLL_VIEW_TEST_TAG = "lazy_list_vpn_settings_test_tag"
const val WIREGUARD_OBFUSCATION_UDP_OVER_TCP_CELL_TEST_TAG =
"wireguard_obfuscation_udp_over_tcp_cell_test_tag"
const val WIREGUARD_OBFUSCATION_OFF_CELL_TEST_TAG =
"wireguard_obfuscation_off_cell_test_tag"
const val WIREGUARD_CUSTOM_PORT_CELL_TEST_TAG =
"lazy_list_wireguard_custom_port_text_test_tag"
const val SWITCH_TEST_TAG = "switch_test_tag"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package net.mullvad.mullvadvpn.test.common.page

import androidx.test.uiautomator.By
import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout

class WireGuardCustomPortDialog internal constructor() : Page() {
private val textFieldLabelSelector = By.text("Enter port")
private val setPortSelector = By.text("Set port")
private val cancelSelector = By.text("Cancel")

override fun assertIsDisplayed() {
uiDevice.findObjectWithTimeout(textFieldLabelSelector)
}

fun enterCustomPort(port: String) {
uiDevice.findObjectWithTimeout(textFieldLabelSelector).parent.text = port
}

fun clickSetPort() {
uiDevice.findObjectWithTimeout(setPortSelector).click()
}

fun clickCancel() {
uiDevice.findObjectWithTimeout(cancelSelector).click()
}

companion object {
const val TEXT_FIELD_TEST_TAG = "custom_port_dialog_input_test_tag"
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package net.mullvad.mullvadvpn.test.e2e

import androidx.test.uiautomator.By
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import net.mullvad.mullvadvpn.compose.test.EXPAND_BUTTON_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.SELECT_LOCATION_BUTTON_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.SWITCH_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.TOP_BAR_SETTINGS_BUTTON
import net.mullvad.mullvadvpn.test.common.constant.VERY_LONG_TIMEOUT
import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout
import net.mullvad.mullvadvpn.test.common.misc.Attachment
import net.mullvad.mullvadvpn.test.common.page.ConnectPage
import net.mullvad.mullvadvpn.test.common.page.SelectLocationPage
import net.mullvad.mullvadvpn.test.common.page.SettingsPage
import net.mullvad.mullvadvpn.test.common.page.SystemVpnConfigurationAlert
import net.mullvad.mullvadvpn.test.common.page.TopBar
import net.mullvad.mullvadvpn.test.common.page.VpnSettingsPage
import net.mullvad.mullvadvpn.test.common.page.WireGuardCustomPortDialog
import net.mullvad.mullvadvpn.test.common.page.on
import net.mullvad.mullvadvpn.test.common.rule.ForgetAllVpnAppsInSettingsTestRule
import net.mullvad.mullvadvpn.test.e2e.annotations.HasDependencyOnLocalAPI
import net.mullvad.mullvadvpn.test.e2e.misc.AccountTestRule
Expand All @@ -34,18 +35,22 @@ class LeakTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) {
@BeforeEach
fun setupVPNSettings() {
app.launchAndEnsureLoggedIn(accountTestRule.validAccountNumber)
device.findObjectWithTimeout(By.res(TOP_BAR_SETTINGS_BUTTON)).click()
device.findObjectWithTimeout(By.text("VPN settings")).click()

val localNetworkSharingCell =
device.findObjectWithTimeout(By.text("Local network sharing")).parent
val localNetworkSharingSwitch =
localNetworkSharingCell.findObjectWithTimeout(By.res(SWITCH_TEST_TAG))
on<TopBar> { clickSettings() }

localNetworkSharingSwitch.click()
on<SettingsPage> { clickVpnSettings() }

// Only use port 51820 to make packet capture more deterministic
device.findObjectWithTimeout(By.text("51820")).click()
on<VpnSettingsPage> {
clickLocalNetworkSharingSwitch()
clickWireguardCustomPort()
}

on<WireGuardCustomPortDialog> {
enterCustomPort("51820")
clickSetPort()
}

on<VpnSettingsPage> {}

device.pressBack()
device.pressBack()
Expand All @@ -56,18 +61,26 @@ class LeakTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) {
fun testNegativeLeak() =
runBlocking<Unit> {
app.launch()
device.findObjectWithTimeout(By.text("DISCONNECTED"))

val targetIpAddress = BuildConfig.TRAFFIC_GENERATION_IP_ADDRESS
val targetPort = 80
on<ConnectPage> {
waitForDisconnectedLabel()

clickSelectLocation()
}

device.findObjectWithTimeout(By.res(SELECT_LOCATION_BUTTON_TEST_TAG)).click()
clickLocationExpandButton(DEFAULT_COUNTRY)
clickLocationExpandButton(DEFAULT_CITY)
device.findObjectWithTimeout(By.text(DEFAULT_RELAY)).click()
device.findObjectWithTimeout(By.text("OK")).click()
device.findObjectWithTimeout(By.text("CONNECTED"), VERY_LONG_TIMEOUT)
on<SelectLocationPage> {
clickLocationExpandButton(DEFAULT_COUNTRY)
clickLocationExpandButton(DEFAULT_CITY)
clickLocationCell(DEFAULT_RELAY)
}

on<SystemVpnConfigurationAlert> { clickOk() }

on<ConnectPage> { waitForConnectedLabel() }

// Capture generated traffic to a specific host
val targetIpAddress = BuildConfig.TRAFFIC_GENERATION_IP_ADDRESS
val targetPort = 80
val captureResult =
PacketCapture().capturePackets {
TrafficGenerator(targetIpAddress, targetPort).generateTraffic(10.milliseconds) {
Expand All @@ -76,11 +89,10 @@ class LeakTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) {
}
}

device.findObjectWithTimeout(By.text("Disconnect")).click()
on<ConnectPage> { clickDisconnect() }

val capturedStreams = captureResult.streams
val capturedPcap = captureResult.pcap

val timestamp = System.currentTimeMillis()
Attachment.saveAttachment("capture-testNegativeLeak-$timestamp.pcap", capturedPcap)

Expand All @@ -93,37 +105,51 @@ class LeakTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) {
fun testShouldHaveNegativeLeak() =
runBlocking<Unit> {
app.launch()
device.findObjectWithTimeout(By.text("DISCONNECTED"))

val targetIpAddress = BuildConfig.TRAFFIC_GENERATION_IP_ADDRESS
val targetPort = 80
on<ConnectPage> {
waitForDisconnectedLabel()

clickSelectLocation()
}

on<SelectLocationPage> {
clickLocationExpandButton(DEFAULT_COUNTRY)
clickLocationExpandButton(DEFAULT_CITY)
clickLocationCell(DEFAULT_RELAY)
}

device.findObjectWithTimeout(By.res(SELECT_LOCATION_BUTTON_TEST_TAG)).click()
delay(1000.milliseconds)
clickLocationExpandButton(DEFAULT_COUNTRY)
clickLocationExpandButton(DEFAULT_CITY)
device.findObjectWithTimeout(By.text(DEFAULT_RELAY)).click()
device.findObjectWithTimeout(By.text("OK")).click()
device.findObjectWithTimeout(By.text("CONNECTED"), VERY_LONG_TIMEOUT)
on<SystemVpnConfigurationAlert> { clickOk() }

on<ConnectPage> { waitForConnectedLabel() }

// Capture generated traffic to a specific host
val targetIpAddress = BuildConfig.TRAFFIC_GENERATION_IP_ADDRESS
val targetPort = 80
val captureResult: PacketCaptureResult =
PacketCapture().capturePackets {
TrafficGenerator(targetIpAddress, targetPort).generateTraffic(10.milliseconds) {
delay(
3000.milliseconds
) // Give it some time for generating traffic in tunnel
device.findObjectWithTimeout(By.text("Disconnect")).click()

on<ConnectPage> { clickDisconnect() }

delay(
2000.milliseconds
) // Give it some time to leak traffic outside of tunnel
device.findObjectWithTimeout(By.text("Connect")).click()

on<ConnectPage> {
clickConnect()
waitForConnectedLabel()
}

delay(
3000.milliseconds
) // Give it some time for generating traffic in tunnel
}
}

device.findObjectWithTimeout(By.text("Disconnect")).click()
on<ConnectPage> { clickDisconnect() }

val capturedStreams = captureResult.streams
val capturedPcap = captureResult.pcap
Expand All @@ -133,10 +159,4 @@ class LeakTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) {
val leakRules = listOf(NoTrafficToHostRule(targetIpAddress))
LeakCheck.assertLeaks(capturedStreams, leakRules)
}

private fun clickLocationExpandButton(locationName: String) {
val locationCell = device.findObjectWithTimeout(By.text(locationName)).parent.parent
val expandButton = locationCell.findObjectWithTimeout(By.res(EXPAND_BUTTON_TEST_TAG))
expandButton.click()
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package net.mullvad.mullvadvpn.test.e2e

import androidx.test.uiautomator.By
import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout
import net.mullvad.mullvadvpn.test.common.page.AccountPage
import net.mullvad.mullvadvpn.test.common.page.LoginPage
import net.mullvad.mullvadvpn.test.common.page.TopBar
import net.mullvad.mullvadvpn.test.common.page.on
import net.mullvad.mullvadvpn.test.e2e.misc.AccountTestRule
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension

Expand All @@ -16,11 +17,10 @@ class LogoutTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) {
// Given
app.launchAndEnsureLoggedIn(accountTestRule.validAccountNumber)

// When
app.clickAccountCog()
app.clickActionButtonByText("Log out")
on<TopBar> { clickAccount() }

// Then
assertNotNull(device.findObjectWithTimeout(By.text("Login")))
on<AccountPage> { clickLogOut() }

on<LoginPage>()
}
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,22 @@
package net.mullvad.mullvadvpn.test.e2e

import androidx.test.uiautomator.By
import net.mullvad.mullvadvpn.test.common.annotation.SkipForFlavors
import net.mullvad.mullvadvpn.test.common.constant.VERY_LONG_TIMEOUT
import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer
import net.mullvad.mullvadvpn.test.common.extension.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove
import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout
import net.mullvad.mullvadvpn.test.common.page.MullvadWebsite
import net.mullvad.mullvadvpn.test.common.page.SettingsPage
import net.mullvad.mullvadvpn.test.common.page.TopBar
import net.mullvad.mullvadvpn.test.common.page.on
import org.junit.jupiter.api.Test

class WebLinkTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) {
@Test
@SkipForFlavors(currentFlavor = BuildConfig.FLAVOR_billing, "play")
fun testOpenFaqFromApp() {
// Given
app.launch()
app.launchAndEnsureOnLoginPage()

// When
device.clickAgreeOnPrivacyDisclaimer()
device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove()
device.findObjectWithTimeout(By.text("Login"))
app.clickSettingsCog()
app.clickListItemByText("FAQs & Guides")
on<TopBar> { clickSettings() }

// Then
device.findObjectWithTimeout(
selector = By.text("Mullvad help center"),
timeout = VERY_LONG_TIMEOUT,
)
on<SettingsPage> { clickFaqAndGuides() }

on<MullvadWebsite>()
}
}

0 comments on commit d34461a

Please sign in to comment.