[#290] Screen on for UI tests (#400)

This improves the reliability of UI tests, especially on physical devices, by helping ensure the screen is on.

Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
Carter Jernigan 2022-05-02 15:49:49 -04:00 committed by GitHub
parent 08fad9e763
commit b0ae6b5e72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 148 additions and 13 deletions

View File

@ -24,6 +24,8 @@ android {
if (isOrchestratorEnabled) {
testInstrumentationRunnerArguments["clearPackageData"] = "true"
}
testInstrumentationRunner = "co.electriccoin.zcash.test.ZcashUiTestRunner"
}
if (isOrchestratorEnabled) {
@ -142,6 +144,7 @@ dependencies {
implementation(libs.kotlinx.coroutines.core)
implementation(projects.uiLib)
androidTestImplementation(projects.testLib)
androidTestImplementation(libs.androidx.compose.test.junit)
androidTestImplementation(libs.androidx.navigation.compose)
androidTestImplementation(libs.androidx.uiAutomator)

View File

@ -30,6 +30,7 @@ import cash.z.ecc.sdk.fixture.WalletAddressFixture
import co.electriccoin.zcash.app.test.EccScreenCaptureProcessor
import co.electriccoin.zcash.app.test.getStringResource
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.screen.backup.BackupTag
@ -45,7 +46,7 @@ import org.junit.rules.RuleChain
// TODO [#285]: Screenshot tests fail on older devices due to issue granting external storage permission
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
class ScreenshotTest {
class ScreenshotTest : UiTestPrerequisites() {
companion object {
@BeforeClass

View File

@ -214,6 +214,7 @@ include("preference-impl-android-lib")
include("sdk-ext-lib")
include("sdk-ext-ui-lib")
include("spackle-lib")
include("test-lib")
include("ui-design-lib")
include("ui-lib")

20
test-lib/build.gradle.kts Normal file
View File

@ -0,0 +1,20 @@
plugins {
id("com.android.library")
kotlin("android")
id("zcash.android-build-conventions")
}
android {
// TODO [#6]: Figure out how to move this into the build-conventions
kotlinOptions {
jvmTarget = libs.versions.java.get()
allWarningsAsErrors = project.property("ZCASH_IS_TREAT_WARNINGS_AS_ERRORS").toString().toBoolean()
freeCompilerArgs = freeCompilerArgs.plus("-opt-in=kotlin.RequiresOptIn")
}
resourcePrefix = "co_electriccoin_zcash_"
}
dependencies {
api(libs.bundles.androidx.test)
}

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="co.electriccoin.zcash.test">
<permission android:name="android.permission.WAKE_LOCK"/>
<application />
</manifest>

View File

@ -0,0 +1,54 @@
package co.electriccoin.zcash.test
import android.annotation.TargetApi
import android.app.KeyguardManager
import android.content.Context
import android.os.Build
import android.os.PowerManager
import androidx.test.core.app.ApplicationProvider
import org.junit.Before
/**
* Subclass this in view unit and integration tests. This verifies that
* prerequisites necessary for reliable UI tests are met, and it provides more useful error messages.
*/
// Originally hoped to put this into ZcashUiTestRunner, although it causes reporting of test results to fail
open class UiTestPrerequisites {
@Before
@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
fun verifyPrerequisites() {
assertScreenIsOn()
assertKeyguardIsUnlocked()
}
companion object {
@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
fun assertScreenIsOn() {
if (!isScreenOn()) {
throw AssertionError("Screen must be on for Android UI tests to run") // $NON-NLS
}
}
@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
private fun isScreenOn(): Boolean {
val powerService = ApplicationProvider.getApplicationContext<Context>()
.getSystemService(Context.POWER_SERVICE) as PowerManager
return powerService.isInteractive
}
fun assertKeyguardIsUnlocked() {
if (isKeyguardLocked()) {
throw AssertionError("Device must be unlocked on for Android UI tests to run") // $NON-NLS
}
}
private fun isKeyguardLocked(): Boolean {
val keyguardService = (
ApplicationProvider.getApplicationContext<Context>()
.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
)
return keyguardService.isKeyguardLocked
}
}
}

View File

@ -0,0 +1,29 @@
package co.electriccoin.zcash.test
import android.content.Context
import android.os.Bundle
import android.os.PowerManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.runner.AndroidJUnitRunner
class ZcashUiTestRunner : AndroidJUnitRunner() {
private lateinit var wakeLock: PowerManager.WakeLock
override fun onCreate(arguments: Bundle?) {
super.onCreate(arguments)
val powerManager = ApplicationProvider.getApplicationContext<Context>()
.getSystemService(Context.POWER_SERVICE) as PowerManager
// There is no alternative to this deprecated API. The suggestion of a view to keep the screen
// on won't work well for our tests.
@Suppress("DEPRECATION")
val flags = PowerManager.FULL_WAKE_LOCK or PowerManager.ON_AFTER_RELEASE
wakeLock = powerManager.newWakeLock(flags, "zcash:keep_screen_on_for_tests")
}
override fun onDestroy() {
super.onDestroy()
wakeLock.release()
}
}

View File

@ -6,6 +6,10 @@ plugins {
}
android {
defaultConfig {
testInstrumentationRunner = "co.electriccoin.zcash.test.ZcashUiTestRunner"
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
@ -77,6 +81,7 @@ dependencies {
implementation(projects.spackleLib)
implementation(projects.uiDesignLib)
androidTestImplementation(projects.testLib)
androidTestImplementation(libs.bundles.androidx.test)
androidTestImplementation(libs.androidx.compose.test.junit)
androidTestImplementation(libs.androidx.compose.test.manifest)

View File

@ -8,6 +8,7 @@ import androidx.compose.ui.test.onChildren
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.filters.MediumTest
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.fixture.TestChoicesFixture
import co.electriccoin.zcash.ui.screen.backup.BackupTag
@ -18,7 +19,7 @@ import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
class BackupIntegrationTest {
class BackupIntegrationTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createAndroidComposeRule<TestBackupActivity>()

View File

@ -10,6 +10,7 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.test.filters.MediumTest
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.fixture.TestChoicesFixture
import co.electriccoin.zcash.ui.screen.backup.BackupTag
@ -19,7 +20,7 @@ import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
class BackupViewTest {
class BackupViewTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createComposeRule()

View File

@ -5,6 +5,7 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.filters.MediumTest
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.screen.onboarding.TestOnboardingActivity
import co.electriccoin.zcash.ui.screen.onboarding.model.OnboardingStage
@ -14,7 +15,7 @@ import org.junit.Rule
import org.junit.Test
// TODO [#382]: https://github.com/zcash/secant-android-wallet/issues/382
class OnboardingIntegrationTest {
class OnboardingIntegrationTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createAndroidComposeRule<TestOnboardingActivity>()

View File

@ -6,6 +6,7 @@ import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.filters.MediumTest
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.screen.onboarding.model.OnboardingStage
import co.electriccoin.zcash.ui.test.getStringResource
@ -13,7 +14,7 @@ import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
class OnboardingViewTest {
class OnboardingViewTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createComposeRule()

View File

@ -15,6 +15,7 @@ import cash.z.ecc.sdk.fixture.ZecRequestFixture
import cash.z.ecc.sdk.model.Zatoshi
import cash.z.ecc.sdk.model.ZecRequest
import cash.z.ecc.sdk.model.ZecRequestMessage
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.test.getStringResource
@ -28,7 +29,7 @@ import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class RequestViewTest {
class RequestViewTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createComposeRule()

View File

@ -19,6 +19,7 @@ import androidx.test.filters.MediumTest
import cash.z.ecc.android.bip39.Mnemonics
import cash.z.ecc.sdk.fixture.SeedPhraseFixture
import cash.z.ecc.sdk.model.SeedPhrase
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.CommonTag
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
@ -32,7 +33,7 @@ import org.junit.Test
import java.util.Locale
import java.util.concurrent.atomic.AtomicInteger
class RestoreViewTest {
class RestoreViewTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createComposeRule()

View File

@ -8,6 +8,7 @@ import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.test.filters.MediumTest
import cash.z.ecc.sdk.fixture.PersistableWalletFixture
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.test.getStringResource
@ -19,7 +20,7 @@ import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger
@OptIn(ExperimentalCoroutinesApi::class)
class SeedViewTest {
class SeedViewTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createComposeRule()

View File

@ -18,6 +18,7 @@ import cash.z.ecc.sdk.fixture.ZecRequestFixture
import cash.z.ecc.sdk.model.Memo
import cash.z.ecc.sdk.model.Zatoshi
import cash.z.ecc.sdk.model.ZecSend
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.test.getStringResource
@ -30,7 +31,7 @@ import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class SendViewTest {
class SendViewTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createComposeRule()

View File

@ -6,6 +6,7 @@ import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.filters.MediumTest
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.test.getStringResource
@ -17,7 +18,7 @@ import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger
@OptIn(ExperimentalCoroutinesApi::class)
class SettingsViewTest {
class SettingsViewTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createComposeRule()

View File

@ -7,13 +7,14 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import androidx.test.filters.MediumTest
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.test.getStringResource
import co.electriccoin.zcash.ui.test.getStringResourceWithArgs
import org.junit.Rule
import org.junit.Test
class SupportViewIntegrationTest {
class SupportViewIntegrationTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createComposeRule()

View File

@ -7,6 +7,7 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import androidx.test.filters.MediumTest
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.test.getStringResource
import co.electriccoin.zcash.ui.test.getStringResourceWithArgs
@ -14,7 +15,7 @@ import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
class SupportViewTest {
class SupportViewTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createComposeRule()

View File

@ -8,6 +8,7 @@ import androidx.compose.ui.test.performClick
import androidx.test.filters.MediumTest
import cash.z.ecc.sdk.fixture.WalletAddressesFixture
import cash.z.ecc.sdk.model.WalletAddresses
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.test.getStringResource
@ -19,7 +20,7 @@ import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger
@OptIn(ExperimentalCoroutinesApi::class)
class WalletAddressViewTest {
class WalletAddressViewTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createComposeRule()