summaryrefslogtreecommitdiffhomepage
path: root/android/test/common/src
diff options
context:
space:
mode:
authorAlbin <albin@mullvad.net>2022-11-23 19:57:01 +0100
committerAlbin <albin@mullvad.net>2023-01-10 15:32:29 +0100
commit6e09d1941569d5138b961bb7d2d56526663ef1c8 (patch)
tree3eea5fa1a5bb9018bdc031562aaa001319a8ea44 /android/test/common/src
parent0b55897e8a53bcc126d1954f99639d482a3b623e (diff)
downloadmullvadvpn-6e09d1941569d5138b961bb7d2d56526663ef1c8.tar.xz
mullvadvpn-6e09d1941569d5138b961bb7d2d56526663ef1c8.zip
Move common test logic to :test:common
Diffstat (limited to 'android/test/common/src')
-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/constant/TimeoutConstants.kt7
-rw-r--r--android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/extension/UiAutomatorExtensions.kt55
-rw-r--r--android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt81
-rw-r--r--android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt30
5 files changed, 179 insertions, 0 deletions
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
new file mode 100644
index 0000000000..05b47ef99b
--- /dev/null
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/constant/AppConstants.kt
@@ -0,0 +1,6 @@
+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/constant/TimeoutConstants.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/constant/TimeoutConstants.kt
new file mode 100644
index 0000000000..e96eaad744
--- /dev/null
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/constant/TimeoutConstants.kt
@@ -0,0 +1,7 @@
+package net.mullvad.mullvadvpn.test.common.constant
+
+const val APP_LAUNCH_TIMEOUT = 5000L
+const val CONNECTION_TIMEOUT = 30000L
+const val DEFAULT_INTERACTION_TIMEOUT = 3000L
+const val LOGIN_TIMEOUT = 30000L
+const val LOGIN_FAILURE_TIMEOUT = 60000L
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/extension/UiAutomatorExtensions.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/extension/UiAutomatorExtensions.kt
new file mode 100644
index 0000000000..e6b63f5d36
--- /dev/null
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/extension/UiAutomatorExtensions.kt
@@ -0,0 +1,55 @@
+package net.mullvad.mullvadvpn.test.common.extension
+
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import java.util.regex.Pattern
+import net.mullvad.mullvadvpn.test.common.constant.DEFAULT_INTERACTION_TIMEOUT
+
+fun UiDevice.findObjectByCaseInsensitiveText(text: String): UiObject2 {
+ return findObjectWithTimeout(By.text(Pattern.compile(text, Pattern.CASE_INSENSITIVE)))
+}
+
+fun UiObject2.findObjectByCaseInsensitiveText(text: String): UiObject2 {
+ return findObjectWithTimeout(By.text(Pattern.compile(text, Pattern.CASE_INSENSITIVE)))
+}
+
+fun UiDevice.findObjectWithTimeout(
+ selector: BySelector,
+ timeout: Long = DEFAULT_INTERACTION_TIMEOUT
+): UiObject2 {
+
+ wait(
+ Until.hasObject(selector),
+ timeout
+ )
+
+ return try {
+ findObject(selector)
+ } catch (e: NullPointerException) {
+ throw IllegalArgumentException(
+ "No matches for selector within timeout ($timeout): $selector"
+ )
+ }
+}
+
+fun UiObject2.findObjectWithTimeout(
+ selector: BySelector,
+ timeout: Long = DEFAULT_INTERACTION_TIMEOUT
+): UiObject2 {
+
+ wait(
+ Until.hasObject(selector),
+ timeout
+ )
+
+ return try {
+ findObject(selector)
+ } catch (e: NullPointerException) {
+ throw IllegalArgumentException(
+ "No matches for selector within timeout ($timeout): $selector"
+ )
+ }
+}
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
new file mode 100644
index 0000000000..e225997534
--- /dev/null
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt
@@ -0,0 +1,81 @@
+package net.mullvad.mullvadvpn.test.common.interactor
+
+import android.content.Context
+import android.content.Intent
+import android.widget.ImageButton
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import net.mullvad.mullvadvpn.test.common.constant.APP_LAUNCH_TIMEOUT
+import net.mullvad.mullvadvpn.test.common.constant.CONNECTION_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.constant.SETTINGS_COG_ID
+import net.mullvad.mullvadvpn.test.common.constant.TUNNEL_INFO_ID
+import net.mullvad.mullvadvpn.test.common.constant.TUNNEL_OUT_ADDRESS_ID
+import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout
+
+class AppInteractor(
+ private val device: UiDevice,
+ private val targetContext: Context
+) {
+ fun launch() {
+ device.pressHome()
+ // Wait for launcher
+ device.wait(
+ Until.hasObject(By.pkg(device.launcherPackageName).depth(0)),
+ APP_LAUNCH_TIMEOUT
+ )
+ val intent =
+ targetContext.packageManager.getLaunchIntentForPackage(MULLVAD_PACKAGE)?.apply {
+ // Clear out any previous instances
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ }
+ targetContext.startActivity(intent)
+ device.wait(
+ Until.hasObject(By.pkg(MULLVAD_PACKAGE).depth(0)),
+ APP_LAUNCH_TIMEOUT
+ )
+ }
+
+ fun launchAndEnsureLoggedIn(accountToken: String) {
+ launch()
+ attemptLogin(accountToken)
+ ensureLoggedIn()
+ }
+
+ fun attemptLogin(accountToken: String) {
+ device.findObjectWithTimeout(By.text("Login"))
+ val loginObject = device.findObjectWithTimeout(By.clazz("android.widget.EditText"))
+ .apply { text = accountToken }
+ loginObject.parent.findObject(By.clazz(ImageButton::class.java)).click()
+ }
+
+ fun ensureLoggedIn() {
+ device.findObjectWithTimeout(By.text("UNSECURED CONNECTION"), LOGIN_TIMEOUT)
+ }
+
+ fun extractIpAddress(): String {
+ device.findObjectWithTimeout(By.res(TUNNEL_INFO_ID)).click()
+ return device.findObjectWithTimeout(
+ By.res(TUNNEL_OUT_ADDRESS_ID),
+ CONNECTION_TIMEOUT
+ ).text.extractIpAddress()
+ }
+
+ fun clickSettingsCog() {
+ device.findObjectWithTimeout(By.res(SETTINGS_COG_ID)).click()
+ }
+
+ fun clickListItemByText(text: String) {
+ device.findObjectWithTimeout(By.text(text)).click()
+ }
+
+ fun clickActionButtonByText(text: String) {
+ device.findObjectWithTimeout(By.text(text)).click()
+ }
+
+ private fun String.extractIpAddress(): String {
+ return split(" ")[1].split(" ")[0]
+ }
+}
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt
new file mode 100644
index 0000000000..7f70d98ca7
--- /dev/null
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt
@@ -0,0 +1,30 @@
+package net.mullvad.mullvadvpn.test.common.rule
+
+import android.util.Log
+import androidx.test.runner.screenshot.BasicScreenCaptureProcessor
+import androidx.test.runner.screenshot.ScreenCaptureProcessor
+import androidx.test.runner.screenshot.Screenshot
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+import java.time.LocalDateTime
+import java.time.format.DateTimeFormatter
+
+class CaptureScreenshotOnFailedTestRule(private val logTag: String) : TestWatcher() {
+ override fun failed(e: Throwable?, description: Description?) {
+ Log.d(logTag, "Capturing screenshot of failed test: " + description?.methodName)
+ val timestamp = DateTimeFormatter.ISO_DATE_TIME.format(LocalDateTime.now()).replace(":", "")
+ val screenshotName = "$timestamp-${description?.methodName}"
+ captureScreenshot(screenshotName)
+ }
+
+ private fun captureScreenshot(screenShotName: String) {
+ try {
+ val screenCapture = Screenshot.capture().apply { name = screenShotName }
+ val processorSet: MutableSet<ScreenCaptureProcessor> = HashSet()
+ processorSet.add(BasicScreenCaptureProcessor())
+ screenCapture.process(processorSet)
+ } catch (ex: Exception) {
+ Log.d(logTag, "Error capturing screenshot: " + ex.message)
+ }
+ }
+}