Skip to content
This repository has been archived by the owner on Sep 27, 2024. It is now read-only.

[Android] Improve reliability of UI tests running on CI emulator #872

Merged
merged 2 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions platforms/android/library-compose/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ dependencies {
testImplementation libs.test.kotlin.coroutines
testImplementation libs.test.turbine
testImplementation libs.molecule.runtime
androidTestImplementation project(":test")
androidTestImplementation libs.test.androidx.junit
androidTestImplementation libs.test.androidx.espresso
androidTestImplementation libs.test.mockk.android
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import io.element.android.wysiwyg.compose.testutils.ComposerActions
import io.element.android.wysiwyg.compose.testutils.StateFactory
import io.element.android.wysiwyg.compose.testutils.copy
import io.element.android.wysiwyg.compose.testutils.showContent
import io.element.android.wysiwyg.test.rules.createFlakyEmulatorRule
import io.element.android.wysiwyg.utils.NBSP
import io.element.android.wysiwyg.view.models.InlineFormat
import kotlinx.coroutines.test.runTest
Expand All @@ -19,6 +20,9 @@ class RichTextEditorActionsTest {
@get:Rule
val composeTestRule = createComposeRule()

@get:Rule
val flakyEmulatorRule = createFlakyEmulatorRule()

@Test
fun testBold() = runTest {
val state = StateFactory.createState()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withText
import io.element.android.wysiwyg.test.rules.createFlakyEmulatorRule
import io.element.android.wysiwyg.utils.NBSP
import io.element.android.wysiwyg.view.models.InlineFormat
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -27,6 +28,9 @@ class RichTextEditorStateTest {
@get:Rule
val composeTestRule = createComposeRule()

@get:Rule
val flakyEmulatorRule = createFlakyEmulatorRule()

@Test
fun testSharingState() = runTest {
val state = RichTextEditorState()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.withText
import io.element.android.wysiwyg.compose.testutils.StateFactory.createState
import io.element.android.wysiwyg.compose.testutils.ViewMatchers.isRichTextEditor
import io.element.android.wysiwyg.test.rules.createFlakyEmulatorRule
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import org.junit.Rule
Expand All @@ -22,6 +23,9 @@ class RichTextEditorStyleTest {
@get:Rule
val composeTestRule = createComposeRule()

@get:Rule
val flakyEmulatorRule = createFlakyEmulatorRule()

private val state = createState()
private val bulletRadius = MutableStateFlow(2.dp)
private val codeBgColor = MutableStateFlow(Color.Blue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import io.element.android.wysiwyg.compose.testutils.StateFactory.createState
import io.element.android.wysiwyg.compose.testutils.ViewMatchers.isRichTextEditor
import io.element.android.wysiwyg.compose.testutils.copy
import io.element.android.wysiwyg.compose.testutils.showContent
import io.element.android.wysiwyg.test.rules.createFlakyEmulatorRule
import io.element.android.wysiwyg.view.models.LinkAction
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
Expand All @@ -26,6 +27,9 @@ class RichTextEditorTest {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()

@get:Rule
val flakyEmulatorRule = createFlakyEmulatorRule()

@Test
fun testTypeText() {
val state = createState()
Expand Down
1 change: 1 addition & 0 deletions platforms/android/library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ dependencies {
testImplementation libs.test.robolectric
testImplementation libs.test.mockk
testImplementation libs.test.hamcrest
androidTestImplementation project(":test")
androidTestImplementation libs.test.androidx.junit
androidTestImplementation libs.test.androidx.espresso
androidTestImplementation libs.test.androidx.espresso.accessibility
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.wysiwyg.display.TextDisplay
import io.element.android.wysiwyg.test.R
import io.element.android.wysiwyg.test.rules.createFlakyEmulatorRule
import io.element.android.wysiwyg.test.utils.*
import io.element.android.wysiwyg.utils.RustErrorCollector
import io.element.android.wysiwyg.view.models.InlineFormat
Expand All @@ -54,6 +55,9 @@ class EditorEditTextInputTests {
@get:Rule
val scenarioRule = ActivityScenarioRule(TestActivity::class.java)

@get:Rule
val flakyEmulatorRule = createFlakyEmulatorRule()

private val ipsum = "Lorem Ipsum is simply dummy text of the printing and typesetting industry."

init {
Expand Down
2 changes: 1 addition & 1 deletion platforms/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ dependencyResolutionManagement {
}
}
rootProject.name = "Rich Text Editor"
include ':example-view', ':example-compose', ':library', ':library-compose'
include ':example-view', ':example-compose', ':library', ':library-compose', ':test'
1 change: 1 addition & 0 deletions platforms/android/test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
34 changes: 34 additions & 0 deletions platforms/android/test/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
}

android {
namespace = "io.element.android.wysiwyg.test"
compileSdk = 34

defaultConfig {
minSdk = 21
}

buildTypes {
release {
isMinifyEnabled = false
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

kotlin {
jvmToolchain(11)
}

dependencies {
implementation(libs.test.androidx.uiautomator)
implementation(libs.test.junit)
implementation(libs.test.androidx.espresso)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.element.android.wysiwyg.test.rules

import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject
import androidx.test.uiautomator.UiSelector
import org.junit.rules.TestWatcher
import org.junit.runner.Description

internal class DismissAnrRule : TestWatcher() {
override fun starting(description: Description) {
dismissAnr()
}
}

private fun dismissAnr() {
val device = UiDevice.getInstance(getInstrumentation())
val dialog = device.findAnrDialog()
if (dialog.exists()) {
device.findWaitButton().click()
}
}

private fun UiDevice.findAnrDialog(): UiObject =
findObject(UiSelector().textContains("isn't responding"))

private fun UiDevice.findWaitButton(): UiObject =
findObject(UiSelector().text("Wait").enabled(true))
.apply { waitForExists(5000) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.element.android.wysiwyg.test.rules

import org.junit.rules.RuleChain
import org.junit.rules.TestRule

/**
* Creates a rule that helps to reduce emulator related flakiness.
*/
fun createFlakyEmulatorRule(): TestRule = RuleChain
.outerRule(RetryOnFailureRule())
.around(DismissAnrRule())
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.element.android.wysiwyg.test.rules

import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement

internal class RetryOnFailureRule : TestRule {
override fun apply(
base: Statement,
description: Description
): Statement =
RetryStatement(base)
}

private class RetryStatement(private val base: Statement) : Statement() {
override fun evaluate() {
try {
base.evaluate()
return
} catch (t: Throwable) {
base.evaluate()
}
}
}
Loading