diff options
| author | Albin <albin@mullvad.net> | 2022-11-23 19:57:01 +0100 |
|---|---|---|
| committer | Albin <albin@mullvad.net> | 2023-01-10 15:32:29 +0100 |
| commit | 6e09d1941569d5138b961bb7d2d56526663ef1c8 (patch) | |
| tree | 3eea5fa1a5bb9018bdc031562aaa001319a8ea44 /android/test/common/src | |
| parent | 0b55897e8a53bcc126d1954f99639d482a3b623e (diff) | |
| download | mullvadvpn-6e09d1941569d5138b961bb7d2d56526663ef1c8.tar.xz mullvadvpn-6e09d1941569d5138b961bb7d2d56526663ef1c8.zip | |
Move common test logic to :test:common
Diffstat (limited to 'android/test/common/src')
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) + } + } +} |
