summaryrefslogtreecommitdiffhomepage
path: root/android/test
diff options
context:
space:
mode:
authorAlbin <albin@mullvad.net>2024-01-18 22:36:40 +0100
committerAlbin <albin@mullvad.net>2024-01-18 22:36:40 +0100
commit6e7f6eb0b99b7c1b2bf700f4ea92337065e2903f (patch)
tree253c6af0397a20b46646cfab7f6ac6d9d563f208 /android/test
parent8f8e8c4699237ce6f254c0d6b7f02657dc4224ca (diff)
parent9157306570bdb1c11850d5e30222504f86f2ba79 (diff)
downloadmullvadvpn-6e7f6eb0b99b7c1b2bf700f4ea92337065e2903f.tar.xz
mullvadvpn-6e7f6eb0b99b7c1b2bf700f4ea92337065e2903f.zip
Merge branch 'run-stagemole-connect-test-in-firebase-droid-593'
Diffstat (limited to 'android/test')
-rw-r--r--android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/annotation/SkipForFlavors.kt25
-rw-r--r--android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/constant/AppConstants.kt6
-rw-r--r--android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt11
-rw-r--r--android/test/e2e/build.gradle.kts42
-rw-r--r--android/test/e2e/e2e.properties1
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/ConnectionTest.kt23
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/EndToEndTest.kt21
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LaunchAppTest.kt2
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LoginTest.kt32
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LogoutTest.kt8
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/WebLinkTest.kt4
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/ConnCheckConstants.kt3
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/Constants.kt1
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/UrlConstants.kt17
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/AccountTestRule.kt43
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/SimpleMullvadHttpClient.kt45
-rw-r--r--android/test/firebase/e2e-play-stagemole.yml11
-rw-r--r--android/test/firebase/mockapi-oss.yml (renamed from android/test/firebase-test-lab.yml)2
-rw-r--r--android/test/mockapi/build.gradle.kts7
-rw-r--r--android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt3
-rw-r--r--android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/constant/Constants.kt2
21 files changed, 237 insertions, 72 deletions
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/annotation/SkipForFlavors.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/annotation/SkipForFlavors.kt
new file mode 100644
index 0000000000..f1d3a990a1
--- /dev/null
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/annotation/SkipForFlavors.kt
@@ -0,0 +1,25 @@
+package net.mullvad.mullvadvpn.test.common.annotation
+
+import org.junit.jupiter.api.extension.ConditionEvaluationResult
+import org.junit.jupiter.api.extension.ExecutionCondition
+import org.junit.jupiter.api.extension.ExtendWith
+import org.junit.jupiter.api.extension.ExtensionContext
+
+@Retention(AnnotationRetention.RUNTIME)
+@ExtendWith(SkipForFlavors.FlavorCondition::class)
+annotation class SkipForFlavors(val currentFlavor: String, vararg val skipForFlavors: String) {
+ class FlavorCondition : ExecutionCondition {
+ override fun evaluateExecutionCondition(p0: ExtensionContext?): ConditionEvaluationResult {
+ val annotation = p0?.element?.get()?.getAnnotation(SkipForFlavors::class.java)
+ return if (annotation?.skipForFlavors?.contains(annotation.currentFlavor) == true) {
+ ConditionEvaluationResult.disabled(
+ "Skipping test for flavor: ${annotation.currentFlavor}"
+ )
+ } else {
+ ConditionEvaluationResult.enabled(
+ "Running test for flavor: ${annotation?.currentFlavor}"
+ )
+ }
+ }
+ }
+}
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/constant/AppConstants.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/constant/AppConstants.kt
deleted file mode 100644
index 05b47ef99b..0000000000
--- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/constant/AppConstants.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package net.mullvad.mullvadvpn.test.common.constant
-
-const val MULLVAD_PACKAGE = "net.mullvad.mullvadvpn"
-const val SETTINGS_COG_ID = "net.mullvad.mullvadvpn:id/settings"
-const val TUNNEL_INFO_ID = "net.mullvad.mullvadvpn:id/tunnel_info"
-const val TUNNEL_OUT_ADDRESS_ID = "net.mullvad.mullvadvpn:id/out_address"
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt
index 1608f28bcf..fd976887ce 100644
--- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt
@@ -13,12 +13,15 @@ import net.mullvad.mullvadvpn.test.common.constant.CONNECTION_TIMEOUT
import net.mullvad.mullvadvpn.test.common.constant.DEFAULT_INTERACTION_TIMEOUT
import net.mullvad.mullvadvpn.test.common.constant.LOGIN_PROMPT_TIMEOUT
import net.mullvad.mullvadvpn.test.common.constant.LOGIN_TIMEOUT
-import net.mullvad.mullvadvpn.test.common.constant.MULLVAD_PACKAGE
import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer
import net.mullvad.mullvadvpn.test.common.extension.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove
import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout
-class AppInteractor(private val device: UiDevice, private val targetContext: Context) {
+class AppInteractor(
+ private val device: UiDevice,
+ private val targetContext: Context,
+ private val targetPackageName: String
+) {
fun launch(customApiEndpointConfiguration: CustomApiEndpointConfiguration? = null) {
device.pressHome()
// Wait for launcher
@@ -28,7 +31,7 @@ class AppInteractor(private val device: UiDevice, private val targetContext: Con
)
val intent =
- targetContext.packageManager.getLaunchIntentForPackage(MULLVAD_PACKAGE)?.apply {
+ targetContext.packageManager.getLaunchIntentForPackage(targetPackageName)?.apply {
// Clear out any previous instances
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
if (customApiEndpointConfiguration != null) {
@@ -36,7 +39,7 @@ class AppInteractor(private val device: UiDevice, private val targetContext: Con
}
}
targetContext.startActivity(intent)
- device.wait(Until.hasObject(By.pkg(MULLVAD_PACKAGE).depth(0)), APP_LAUNCH_TIMEOUT)
+ device.wait(Until.hasObject(By.pkg(targetPackageName).depth(0)), APP_LAUNCH_TIMEOUT)
}
fun launchAndEnsureLoggedIn(accountToken: String) {
diff --git a/android/test/e2e/build.gradle.kts b/android/test/e2e/build.gradle.kts
index 946e8effa9..8440967aa9 100644
--- a/android/test/e2e/build.gradle.kts
+++ b/android/test/e2e/build.gradle.kts
@@ -1,5 +1,6 @@
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
import java.util.Properties
+import org.gradle.configurationcache.extensions.capitalized
plugins {
id(Dependencies.Plugin.androidTestId)
@@ -19,9 +20,6 @@ android {
"de.mannodermaus.junit5.AndroidJUnit5Builder"
targetProjectPath = ":app"
- missingDimensionStrategy(FlavorDimensions.BILLING, Flavors.OSS)
- missingDimensionStrategy(FlavorDimensions.INFRASTRUCTURE, Flavors.PROD)
-
fun Properties.addRequiredPropertyAsBuildConfigField(name: String) {
val value = getProperty(name) ?: throw GradleException("Missing property: $name")
buildConfigField(type = "String", name = name, value = "\"$value\"")
@@ -29,7 +27,6 @@ android {
Properties().apply {
load(project.file("e2e.properties").inputStream())
- addRequiredPropertyAsBuildConfigField("API_BASE_URL")
addRequiredPropertyAsBuildConfigField("API_VERSION")
}
@@ -51,6 +48,30 @@ android {
}
}
+ flavorDimensions += FlavorDimensions.BILLING
+ flavorDimensions += FlavorDimensions.INFRASTRUCTURE
+
+ productFlavors {
+ create(Flavors.OSS) { dimension = FlavorDimensions.BILLING }
+ create(Flavors.PLAY) { dimension = FlavorDimensions.BILLING }
+ create(Flavors.PROD) {
+ dimension = FlavorDimensions.INFRASTRUCTURE
+ buildConfigField(
+ type = "String",
+ name = "INFRASTRUCTURE_BASE_DOMAIN",
+ value = "\"mullvad.net\""
+ )
+ }
+ create(Flavors.STAGEMOLE) {
+ dimension = FlavorDimensions.INFRASTRUCTURE
+ buildConfigField(
+ type = "String",
+ name = "INFRASTRUCTURE_BASE_DOMAIN",
+ value = "\"stagemole.eu\""
+ )
+ }
+ }
+
testOptions { execution = "ANDROIDX_TEST_ORCHESTRATOR" }
compileOptions {
@@ -78,6 +99,19 @@ android {
}
}
+androidComponents {
+ beforeVariants { variantBuilder ->
+ variantBuilder.enable =
+ variantBuilder.let { currentVariant ->
+ val enabledVariants =
+ enabledE2eVariantTriples.map { (billing, infra, buildType) ->
+ billing + infra.capitalized() + buildType.capitalized()
+ }
+ enabledVariants.contains(currentVariant.name)
+ }
+ }
+}
+
configure<org.owasp.dependencycheck.gradle.extension.DependencyCheckExtension> {
// Skip the lintClassPath configuration, which relies on many dependencies that has been flagged
// to have CVEs, as it's related to the lint tooling rather than the project's compilation class
diff --git a/android/test/e2e/e2e.properties b/android/test/e2e/e2e.properties
index f9420786c8..58798ef1b6 100644
--- a/android/test/e2e/e2e.properties
+++ b/android/test/e2e/e2e.properties
@@ -1,2 +1 @@
-API_BASE_URL=https://api.mullvad.net
API_VERSION=v1
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/ConnectionTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/ConnectionTest.kt
index bbecb037f3..d66ee8a37c 100644
--- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/ConnectionTest.kt
+++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/ConnectionTest.kt
@@ -3,25 +3,40 @@ 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.rule.ForgetAllVpnAppsInSettingsTestRule
-import net.mullvad.mullvadvpn.test.e2e.misc.CleanupAccountTestRule
+import net.mullvad.mullvadvpn.test.e2e.misc.AccountTestRule
import net.mullvad.mullvadvpn.test.e2e.misc.ConnCheckState
import net.mullvad.mullvadvpn.test.e2e.misc.SimpleMullvadHttpClient
import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
-class ConnectionTest : EndToEndTest() {
+class ConnectionTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) {
- @RegisterExtension @JvmField val cleanupAccountTestRule = CleanupAccountTestRule()
+ @RegisterExtension @JvmField val accountTestRule = AccountTestRule()
@RegisterExtension
@JvmField
val forgetAllVpnAppsInSettingsTestRule = ForgetAllVpnAppsInSettingsTestRule()
@Test
+ fun testConnect() {
+ // Given
+ app.launchAndEnsureLoggedIn(accountTestRule.validAccountNumber)
+
+ // When
+ device.findObjectWithTimeout(By.text("Secure my connection")).click()
+ device.findObjectWithTimeout(By.text("OK")).click()
+
+ // Then
+ device.findObjectWithTimeout(By.text("SECURE CONNECTION"))
+ }
+
+ @Test
+ @Disabled("Disabled since the connection check isn't reliable in the stagemole infrastructure.")
fun testConnectAndVerifyWithConnectionCheck() {
// Given
- app.launchAndEnsureLoggedIn(validTestAccountToken)
+ app.launchAndEnsureLoggedIn(accountTestRule.validAccountNumber)
// When
device.findObjectWithTimeout(By.text("Secure my connection")).click()
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/EndToEndTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/EndToEndTest.kt
index 2cf8ba712d..f4979258f2 100644
--- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/EndToEndTest.kt
+++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/EndToEndTest.kt
@@ -8,14 +8,11 @@ import androidx.test.uiautomator.UiDevice
import de.mannodermaus.junit5.extensions.GrantPermissionExtension
import net.mullvad.mullvadvpn.test.common.interactor.AppInteractor
import net.mullvad.mullvadvpn.test.common.rule.CaptureScreenshotOnFailedTestRule
-import net.mullvad.mullvadvpn.test.e2e.constant.INVALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY
import net.mullvad.mullvadvpn.test.e2e.constant.LOG_TAG
-import net.mullvad.mullvadvpn.test.e2e.constant.VALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY
-import net.mullvad.mullvadvpn.test.e2e.extension.getRequiredArgument
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.extension.RegisterExtension
-abstract class EndToEndTest {
+abstract class EndToEndTest(private val infra: String) {
@RegisterExtension @JvmField val rule = CaptureScreenshotOnFailedTestRule(LOG_TAG)
@@ -34,21 +31,19 @@ abstract class EndToEndTest {
lateinit var device: UiDevice
lateinit var targetContext: Context
lateinit var app: AppInteractor
- lateinit var validTestAccountToken: String
- lateinit var invalidTestAccountToken: String
@BeforeEach
fun setup() {
device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
targetContext = InstrumentationRegistry.getInstrumentation().targetContext
- validTestAccountToken =
- InstrumentationRegistry.getArguments()
- .getRequiredArgument(VALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY)
- invalidTestAccountToken =
- InstrumentationRegistry.getArguments()
- .getRequiredArgument(INVALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY)
+ val targetPackageNameSuffix =
+ when (infra) {
+ "devmole" -> ".devmole"
+ "stagemole" -> ".stagemole"
+ else -> ""
+ }
- app = AppInteractor(device, targetContext)
+ app = AppInteractor(device, targetContext, "net.mullvad.mullvadvpn$targetPackageNameSuffix")
}
}
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LaunchAppTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LaunchAppTest.kt
index f68df92854..724bffcad0 100644
--- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LaunchAppTest.kt
+++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LaunchAppTest.kt
@@ -2,7 +2,7 @@ package net.mullvad.mullvadvpn.test.e2e
import org.junit.jupiter.api.Test
-class LaunchAppTest : EndToEndTest() {
+class LaunchAppTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) {
@Test
fun testLaunchApp() {
app.launch()
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LoginTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LoginTest.kt
index 792c63f1a1..9380589709 100644
--- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LoginTest.kt
+++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LoginTest.kt
@@ -5,39 +5,41 @@ import net.mullvad.mullvadvpn.test.common.constant.LOGIN_FAILURE_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.e2e.misc.CleanupAccountTestRule
+import net.mullvad.mullvadvpn.test.e2e.misc.AccountTestRule
+import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
-class LoginTest : EndToEndTest() {
+class LoginTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) {
- @RegisterExtension @JvmField val cleanupAccountTestRule = CleanupAccountTestRule()
+ @RegisterExtension @JvmField val accountTestRule = AccountTestRule()
@Test
- fun testLoginWithInvalidCredentials() {
+ fun testLoginWithValidCredentials() {
// Given
- val invalidDummyAccountToken = invalidTestAccountToken
+ val validTestAccountToken = accountTestRule.validAccountNumber
// When
- app.launch()
- device.clickAgreeOnPrivacyDisclaimer()
- device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove()
- app.waitForLoginPrompt()
- app.attemptLogin(invalidDummyAccountToken)
+ app.launchAndEnsureLoggedIn(validTestAccountToken)
// Then
- device.findObjectWithTimeout(By.text("Invalid account number"), LOGIN_FAILURE_TIMEOUT)
+ app.ensureLoggedIn()
}
@Test
- fun testLoginWithValidCredentials() {
+ @Disabled("Disabled to avoid getting rate-limited.")
+ fun testLoginWithInvalidCredentials() {
// Given
- val token = validTestAccountToken
+ val invalidDummyAccountToken = accountTestRule.invalidAccountNumber
// When
- app.launchAndEnsureLoggedIn(token)
+ app.launch()
+ device.clickAgreeOnPrivacyDisclaimer()
+ device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove()
+ app.waitForLoginPrompt()
+ app.attemptLogin(invalidDummyAccountToken)
// Then
- app.ensureLoggedIn()
+ device.findObjectWithTimeout(By.text("Invalid account number"), LOGIN_FAILURE_TIMEOUT)
}
}
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LogoutTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LogoutTest.kt
index 95ad9c22f5..4d77c2a33a 100644
--- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LogoutTest.kt
+++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LogoutTest.kt
@@ -2,19 +2,19 @@ package net.mullvad.mullvadvpn.test.e2e
import androidx.test.uiautomator.By
import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout
-import net.mullvad.mullvadvpn.test.e2e.misc.CleanupAccountTestRule
+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
-class LogoutTest : EndToEndTest() {
+class LogoutTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) {
- @RegisterExtension @JvmField val cleanupAccountTestRule = CleanupAccountTestRule()
+ @RegisterExtension @JvmField val accountTestRule = AccountTestRule()
@Test
fun testLogout() {
// Given
- app.launchAndEnsureLoggedIn(validTestAccountToken)
+ app.launchAndEnsureLoggedIn(accountTestRule.validAccountNumber)
// When
app.clickAccountCog()
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/WebLinkTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/WebLinkTest.kt
index 5e72305efe..d4caa1da56 100644
--- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/WebLinkTest.kt
+++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/WebLinkTest.kt
@@ -1,14 +1,16 @@
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.WEB_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 org.junit.jupiter.api.Test
-class WebLinkTest : EndToEndTest() {
+class WebLinkTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) {
@Test
+ @SkipForFlavors(currentFlavor = BuildConfig.FLAVOR_billing, "play")
fun testOpenFaqFromApp() {
// Given
app.launch()
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/ConnCheckConstants.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/ConnCheckConstants.kt
deleted file mode 100644
index 5357ce0e75..0000000000
--- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/ConnCheckConstants.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package net.mullvad.mullvadvpn.test.e2e.constant
-
-const val CONN_CHECK_URL = "https://am.i.mullvad.net/json"
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/Constants.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/Constants.kt
index 98fd52a333..dec8f1e07f 100644
--- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/Constants.kt
+++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/Constants.kt
@@ -1,5 +1,6 @@
package net.mullvad.mullvadvpn.test.e2e.constant
const val LOG_TAG = "mullvad-e2e"
+const val PARTNER_AUTH = "partner_auth"
const val VALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY = "valid_test_account_token"
const val INVALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY = "invalid_test_account_token"
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/UrlConstants.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/UrlConstants.kt
new file mode 100644
index 0000000000..70b06f9697
--- /dev/null
+++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/UrlConstants.kt
@@ -0,0 +1,17 @@
+package net.mullvad.mullvadvpn.test.e2e.constant
+
+import net.mullvad.mullvadvpn.test.e2e.BuildConfig
+
+// API URLs
+const val API_BASE_URL = "https://api.${BuildConfig.INFRASTRUCTURE_BASE_DOMAIN}"
+const val AUTH_URL = "$API_BASE_URL/auth/${BuildConfig.API_VERSION}/token"
+const val ACCOUNT_URL = "$API_BASE_URL/accounts/${BuildConfig.API_VERSION}/accounts"
+const val DEVICE_LIST_URL = "$API_BASE_URL/accounts/${BuildConfig.API_VERSION}/devices"
+
+// Partner URLs
+const val PARTNER_BASE_URL =
+ "https://partner.${BuildConfig.INFRASTRUCTURE_BASE_DOMAIN}/${BuildConfig.API_VERSION}"
+const val PARTNER_ACCOUNT_URL = "$PARTNER_BASE_URL/accounts"
+
+// Connection check
+const val CONN_CHECK_URL = "https://am.i.${BuildConfig.INFRASTRUCTURE_BASE_DOMAIN}/json"
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/AccountTestRule.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/AccountTestRule.kt
new file mode 100644
index 0000000000..bbd59eba9b
--- /dev/null
+++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/AccountTestRule.kt
@@ -0,0 +1,43 @@
+package net.mullvad.mullvadvpn.test.e2e.misc
+
+import androidx.test.platform.app.InstrumentationRegistry
+import net.mullvad.mullvadvpn.test.e2e.constant.INVALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY
+import net.mullvad.mullvadvpn.test.e2e.constant.PARTNER_AUTH
+import net.mullvad.mullvadvpn.test.e2e.constant.VALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY
+import net.mullvad.mullvadvpn.test.e2e.extension.getRequiredArgument
+import org.junit.jupiter.api.extension.BeforeEachCallback
+import org.junit.jupiter.api.extension.ExtensionContext
+
+class AccountTestRule : BeforeEachCallback {
+
+ private val partnerAccount: String?
+ private val client =
+ SimpleMullvadHttpClient(InstrumentationRegistry.getInstrumentation().targetContext)
+
+ val validAccountNumber: String
+ val invalidAccountNumber: String
+
+ init {
+ InstrumentationRegistry.getArguments().also { bundle ->
+ partnerAccount = bundle.getString(PARTNER_AUTH)
+
+ if (partnerAccount != null) {
+ validAccountNumber = client.createAccount()
+ client.addTimeToAccountUsingPartnerAuth(
+ accountNumber = validAccountNumber,
+ daysToAdd = 1,
+ partnerAuth = partnerAccount
+ )
+ } else {
+ validAccountNumber =
+ bundle.getRequiredArgument(VALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY)
+ client.removeAllDevices(validAccountNumber)
+ }
+
+ invalidAccountNumber =
+ bundle.getRequiredArgument(INVALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY)
+ }
+ }
+
+ override fun beforeEach(context: ExtensionContext) {}
+}
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/SimpleMullvadHttpClient.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/SimpleMullvadHttpClient.kt
index b97fb28f45..dff31b6049 100644
--- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/SimpleMullvadHttpClient.kt
+++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/SimpleMullvadHttpClient.kt
@@ -9,9 +9,12 @@ import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.toolbox.RequestFuture
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
-import net.mullvad.mullvadvpn.test.e2e.BuildConfig
+import net.mullvad.mullvadvpn.test.e2e.constant.ACCOUNT_URL
+import net.mullvad.mullvadvpn.test.e2e.constant.AUTH_URL
import net.mullvad.mullvadvpn.test.e2e.constant.CONN_CHECK_URL
+import net.mullvad.mullvadvpn.test.e2e.constant.DEVICE_LIST_URL
import net.mullvad.mullvadvpn.test.e2e.constant.LOG_TAG
+import net.mullvad.mullvadvpn.test.e2e.constant.PARTNER_ACCOUNT_URL
import org.json.JSONArray
import org.json.JSONObject
@@ -37,6 +40,24 @@ class SimpleMullvadHttpClient(context: Context) {
}
}
+ fun createAccount(): String {
+ return sendSimpleSynchronousRequest(method = Request.Method.POST, url = ACCOUNT_URL)!!
+ .getString("number")
+ }
+
+ fun addTimeToAccountUsingPartnerAuth(
+ accountNumber: String,
+ daysToAdd: Int,
+ partnerAuth: String
+ ) {
+ sendSimpleSynchronousRequest(
+ method = Request.Method.POST,
+ url = "$PARTNER_ACCOUNT_URL/$accountNumber/extend",
+ body = JSONObject().apply { put("days", "$daysToAdd") },
+ authorizationHeader = "Basic $partnerAuth"
+ )
+ }
+
fun getDeviceList(accessToken: String): List<String> {
Log.v(LOG_TAG, "Get devices")
@@ -62,9 +83,9 @@ class SimpleMullvadHttpClient(context: Context) {
fun removeDevice(token: String, deviceId: String) {
Log.v(LOG_TAG, "Remove device: $deviceId")
sendSimpleSynchronousRequestString(
- Request.Method.DELETE,
- "$DEVICE_LIST_URL/$deviceId",
- token = token
+ method = Request.Method.DELETE,
+ url = "$DEVICE_LIST_URL/$deviceId",
+ authorizationHeader = "Bearer $token"
)
}
@@ -83,7 +104,7 @@ class SimpleMullvadHttpClient(context: Context) {
method: Int,
url: String,
body: JSONObject? = null,
- token: String? = null
+ authorizationHeader: String? = null
): JSONObject? {
val future = RequestFuture.newFuture<JSONObject>()
val request =
@@ -93,8 +114,8 @@ class SimpleMullvadHttpClient(context: Context) {
if (body != null) {
headers.put("Content-Type", "application/json")
}
- if (token != null) {
- headers.put("Authorization", "Bearer $token")
+ if (authorizationHeader != null) {
+ headers.put("Authorization", authorizationHeader)
}
return headers
}
@@ -114,7 +135,7 @@ class SimpleMullvadHttpClient(context: Context) {
method: Int,
url: String,
body: String? = null,
- token: String? = null
+ authorizationHeader: String? = null
): String? {
val future = RequestFuture.newFuture<String>()
val request =
@@ -124,8 +145,8 @@ class SimpleMullvadHttpClient(context: Context) {
if (body != null) {
headers.put("Content-Type", "application/json")
}
- if (token != null) {
- headers.put("Authorization", "Bearer $token")
+ if (authorizationHeader != null) {
+ headers.put("Authorization", authorizationHeader)
}
return headers
}
@@ -172,10 +193,6 @@ class SimpleMullvadHttpClient(context: Context) {
(0 until this.length()).asSequence().map { this.get(it) as T }.iterator()
companion object {
- private const val AUTH_URL =
- "${BuildConfig.API_BASE_URL}/auth/${BuildConfig.API_VERSION}/token"
- private const val DEVICE_LIST_URL =
- "${BuildConfig.API_BASE_URL}/accounts/${BuildConfig.API_VERSION}/devices"
private const val REQUEST_ERROR_MESSAGE =
"Unable to verify account due to invalid account or connectivity issues."
}
diff --git a/android/test/firebase/e2e-play-stagemole.yml b/android/test/firebase/e2e-play-stagemole.yml
new file mode 100644
index 0000000000..1fecfefe94
--- /dev/null
+++ b/android/test/firebase/e2e-play-stagemole.yml
@@ -0,0 +1,11 @@
+---
+default:
+ type: instrumentation
+ app: android/app/build/outputs/apk/playStagemole/debug/app-play-stagemole-debug.apk
+ test: android/test/e2e/build/outputs/apk/playStagemole/debug/e2e-play-stagemole-debug.apk
+ timeout: 10m
+ use-orchestrator: true
+ device:
+ - {model: shiba, version: 34, locale: en, orientation: portrait}
+ - {model: tangorpro, version: 33, locale: en, orientation: portrait}
+ - {model: felix, version: 33, locale: en, orientation: portrait}
diff --git a/android/test/firebase-test-lab.yml b/android/test/firebase/mockapi-oss.yml
index d84e64134b..a1245f1d4f 100644
--- a/android/test/firebase-test-lab.yml
+++ b/android/test/firebase/mockapi-oss.yml
@@ -2,7 +2,7 @@
default:
type: instrumentation
app: android/app/build/outputs/apk/ossProd/debug/app-oss-prod-debug.apk
- test: android/test/mockapi/build/outputs/apk/debug/mockapi-debug.apk
+ test: android/test/mockapi/build/outputs/apk/oss/debug/mockapi-oss-debug.apk
timeout: 10m
use-orchestrator: true
device:
diff --git a/android/test/mockapi/build.gradle.kts b/android/test/mockapi/build.gradle.kts
index 3fea2d5d60..5c88d90d82 100644
--- a/android/test/mockapi/build.gradle.kts
+++ b/android/test/mockapi/build.gradle.kts
@@ -26,6 +26,13 @@ android {
)
}
+ flavorDimensions += FlavorDimensions.BILLING
+
+ productFlavors {
+ create(Flavors.OSS) { dimension = FlavorDimensions.BILLING }
+ create(Flavors.PLAY) { dimension = FlavorDimensions.BILLING }
+ }
+
testOptions { execution = "ANDROIDX_TEST_ORCHESTRATOR" }
compileOptions {
diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt
index 9ee4f52e04..02e53a09d9 100644
--- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt
+++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt
@@ -12,6 +12,7 @@ import net.mullvad.mullvadvpn.lib.endpoint.CustomApiEndpointConfiguration
import net.mullvad.mullvadvpn.test.common.interactor.AppInteractor
import net.mullvad.mullvadvpn.test.common.rule.CaptureScreenshotOnFailedTestRule
import net.mullvad.mullvadvpn.test.mockapi.constant.LOG_TAG
+import net.mullvad.mullvadvpn.test.mockapi.constant.PACKAGE_NAME
import okhttp3.mockwebserver.MockWebServer
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
@@ -39,7 +40,7 @@ abstract class MockApiTest {
device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
targetContext = InstrumentationRegistry.getInstrumentation().targetContext
- app = AppInteractor(device, targetContext)
+ app = AppInteractor(device, targetContext, PACKAGE_NAME)
mockWebServer.start()
Log.d(LOG_TAG, "Mocked web server started using port: ${mockWebServer.port}")
diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/constant/Constants.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/constant/Constants.kt
index 16123a8e1a..37e782af11 100644
--- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/constant/Constants.kt
+++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/constant/Constants.kt
@@ -1,5 +1,7 @@
package net.mullvad.mullvadvpn.test.mockapi.constant
+const val PACKAGE_NAME = "net.mullvad.mullvadvpn"
+
const val LOG_TAG = "mullvad-mockapi"
const val AUTH_TOKEN_URL_PATH = "/auth/v1/token"