diff --git a/android-snaptesting/build.gradle.kts b/android-snaptesting/build.gradle.kts index f347328..04855c9 100644 --- a/android-snaptesting/build.gradle.kts +++ b/android-snaptesting/build.gradle.kts @@ -48,6 +48,7 @@ dependencies { implementation(libs.androidx.test.monitor) implementation(libs.androidx.test.runner) implementation(libs.androidx.ui.test.junit4.android) + implementation(libs.espresso.core) } apply("${rootProject.projectDir}/mavencentral.gradle") diff --git a/android-snaptesting/src/main/java/com/telefonica/androidsnaptesting/screenshots/ScreenshotsRule.kt b/android-snaptesting/src/main/java/com/telefonica/androidsnaptesting/screenshots/ScreenshotsRule.kt index 8f96c98..f7ddc39 100644 --- a/android-snaptesting/src/main/java/com/telefonica/androidsnaptesting/screenshots/ScreenshotsRule.kt +++ b/android-snaptesting/src/main/java/com/telefonica/androidsnaptesting/screenshots/ScreenshotsRule.kt @@ -4,12 +4,19 @@ import android.app.Activity import android.graphics.Bitmap import android.graphics.BitmapFactory import android.os.Build +import android.os.Looper +import android.view.View +import android.widget.EditText +import android.widget.HorizontalScrollView +import android.widget.ScrollView import androidx.annotation.RequiresApi import androidx.compose.ui.graphics.asAndroidBitmap import androidx.compose.ui.test.captureToImage import androidx.compose.ui.test.junit4.ComposeTestRule import androidx.compose.ui.test.onRoot +import androidx.test.espresso.Espresso import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import androidx.test.runner.screenshot.Screenshot import com.dropbox.differ.ImageComparator import com.dropbox.differ.Mask @@ -62,6 +69,10 @@ public class ScreenshotsRule( activity: Activity, name: String? = null, ) { + val view = activity.findViewById(android.R.id.content) + + disableFlakyComponentsAndWaitForIdle(view) + val bitmap = Screenshot.capture(activity).bitmap compareScreenshot(bitmap, name) } @@ -141,4 +152,58 @@ public class ScreenshotsRule( ) } } + + private fun disableFlakyComponentsAndWaitForIdle(view: View? = null) { + if (view != null) { + disableAnimatedComponents(view) + } + if (notInAppMainThread()) { + waitForAnimationsToFinish() + } + } + + private fun disableAnimatedComponents(view: View) { + runOnUi { + hideEditTextCursors(view) + hideScrollViewBars(view) + } + } + + private fun hideEditTextCursors(view: View) { + view.childrenViews().forEach { + it.isCursorVisible = false + } + } + + private fun hideScrollViewBars(view: View) { + view.childrenViews().forEach { + hideViewBars(it) + } + + view.childrenViews().forEach { + hideViewBars(it) + } + } + + private fun hideViewBars(it: View) { + it.isHorizontalScrollBarEnabled = false + it.isVerticalScrollBarEnabled = false + it.overScrollMode = View.OVER_SCROLL_NEVER + } + + public fun waitForAnimationsToFinish() { + getInstrumentation().waitForIdleSync() + Espresso.onIdle() + } + + public fun runOnUi(block: () -> Unit) { + if (notInAppMainThread()) { + getInstrumentation().runOnMainSync { block() } + } else { + block() + } + } + + private fun notInAppMainThread() = Looper.myLooper() != Looper.getMainLooper() + } diff --git a/android-snaptesting/src/main/java/com/telefonica/androidsnaptesting/screenshots/ViewUtils.kt b/android-snaptesting/src/main/java/com/telefonica/androidsnaptesting/screenshots/ViewUtils.kt new file mode 100644 index 0000000..c58fa6f --- /dev/null +++ b/android-snaptesting/src/main/java/com/telefonica/androidsnaptesting/screenshots/ViewUtils.kt @@ -0,0 +1,30 @@ +package com.telefonica.androidsnaptesting.screenshots + +import android.view.View +import android.view.ViewGroup + +@Suppress("UNCHECKED_CAST") +public inline fun View.childrenViews(): List = filterChildrenViews { + it is T +} as List + +public fun View.filterChildrenViews(filter: (View) -> Boolean): List { + val children = mutableSetOf() + val view = this + if (view !is ViewGroup) { + if (filter.invoke(view)) { + children.add(view) + } + } else { + for (i in 0 until view.childCount) { + view.getChildAt(i).let { + children.addAll(it.filterChildrenViews(filter)) + if (filter.invoke(it)) { + children.add(it) + } + } + } + } + + return children.toList() +} diff --git a/app/src/androidTest/java/com/telefonica/androidsnaptesting/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/telefonica/androidsnaptesting/ExampleInstrumentedTest.kt index 08932c3..173b13f 100644 --- a/app/src/androidTest/java/com/telefonica/androidsnaptesting/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/com/telefonica/androidsnaptesting/ExampleInstrumentedTest.kt @@ -2,6 +2,9 @@ package com.telefonica.androidsnaptesting import androidx.test.core.app.ActivityScenario import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.telefonica.androidsnaptesting.logs.LogsRecorder +import com.telefonica.androidsnaptesting.logs.LogsRule +import com.telefonica.androidsnaptesting.screenshots.ScreenshotsRule import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith