diff options
| author | Albin <albin@mullvad.net> | 2024-01-18 22:36:40 +0100 |
|---|---|---|
| committer | Albin <albin@mullvad.net> | 2024-01-18 22:36:40 +0100 |
| commit | 6e7f6eb0b99b7c1b2bf700f4ea92337065e2903f (patch) | |
| tree | 253c6af0397a20b46646cfab7f6ac6d9d563f208 /android/test | |
| parent | 8f8e8c4699237ce6f254c0d6b7f02657dc4224ca (diff) | |
| parent | 9157306570bdb1c11850d5e30222504f86f2ba79 (diff) | |
| download | mullvadvpn-6e7f6eb0b99b7c1b2bf700f4ea92337065e2903f.tar.xz mullvadvpn-6e7f6eb0b99b7c1b2bf700f4ea92337065e2903f.zip | |
Merge branch 'run-stagemole-connect-test-in-firebase-droid-593'
Diffstat (limited to 'android/test')
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" |
