Skip to content

Commit

Permalink
Merge pull request #131 from sergio-sastre/release/2.4.0
Browse files Browse the repository at this point in the history
Release/2.4.0
  • Loading branch information
sergio-sastre authored Sep 13, 2024
2 parents 4257340 + a548cae commit 87566a2
Show file tree
Hide file tree
Showing 23 changed files with 397 additions and 105 deletions.
11 changes: 7 additions & 4 deletions android-testify/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ android {

dependencies {
implementation project(':utils')
implementation 'androidx.test:rules:1.5.0'
api 'dev.testify:testify:2.0.0'
api 'androidx.activity:activity-compose:1.8.2'
//noinspection GradleDependency
implementation ('androidx.test:rules:1.5.0') {
because "1.6.x makes tests fail when running Gradle Managed Devices"
}
api 'dev.testify:testify:3.2.0'
api 'androidx.activity:activity-compose:1.9.2'
}

//https://www.talentica.com/blogs/publish-your-android-library-on-jitpack-for-better-reachability/
Expand All @@ -62,7 +65,7 @@ publishing {
release(MavenPublication) {
groupId = 'com.github.sergio-sastre'
artifactId = "android-testify"
version = '2.3.5'
version = '2.4.0'

afterEvaluate {
from components.release
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
package sergio.sastre.uitesting.android_testify

import android.graphics.Rect
import android.view.ViewGroup
import androidx.annotation.ColorInt
import dev.testify.CompareMethod
import dev.testify.core.ExclusionRectProvider
import sergio.sastre.uitesting.utils.crosslibrary.config.BitmapCaptureMethod
import sergio.sastre.uitesting.utils.crosslibrary.config.LibraryConfig

class AndroidTestifyConfig(
val bitmapCaptureMethod: BitmapCaptureMethod? = null,
val exactness: Float = 0.9f,
val enableReporter: Boolean = false,
val initialTouchMode: Boolean = false,
val generateDiffs: Boolean = true,
val animationsDisabled: Boolean = true,
val hideCursor: Boolean = true,
val hidePasswords: Boolean = true,
val hideScrollbars: Boolean = true,
val hideSoftKeyboard: Boolean = true,
val hideTextSuggestions: Boolean = true,
val useSoftwareRenderer: Boolean = false,
val exclusionRects: MutableSet<Rect> = HashSet(),
val exclusionRectProvider: ExclusionRectProvider? = null,
val compareMethod: CompareMethod? = null,
val pauseForInspection: Boolean = false,
@ColorInt val backgroundColor: Int? = null,
) : LibraryConfig

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ fun <T : Activity> ScreenshotRule<T>.waitForIdleSync(): ScreenshotRule<T> = appl

// We need to ensure the view is attached to the screenshotRule activity window for testify to
// take the screenshot. This is especially important for dialogs
internal fun <T : Activity> ScreenshotRule<T>.setViewUnderTest(
internal fun <T : Activity> ScreenshotRule<T>.setDialogViewUnderTest(
view: View,
): ScreenshotRule<T> = apply {
setScreenshotViewProvider {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package sergio.sastre.uitesting.android_testify.screenshotscenario

import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import sergio.sastre.uitesting.utils.activityscenario.ActivityScenarioForComposableRule

fun ActivityScenarioForComposableRule.setContent(
composable: @Composable () -> Unit,
): ActivityScenarioForComposableRule {
this.activityScenario
.onActivity {
it.setContent { composable.invoke() }
}

return this
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package sergio.sastre.uitesting.android_testify.screenshotscenario

import android.app.Activity
import android.graphics.Bitmap
import android.view.View
import android.view.ViewGroup
import androidx.core.view.drawToBitmap
import androidx.test.platform.app.InstrumentationRegistry
import dev.testify.TestDescription
import dev.testify.TestifyFeatures
import dev.testify.scenario.ScreenshotScenarioRule
import dev.testify.testDescription
import sergio.sastre.uitesting.utils.crosslibrary.config.BitmapCaptureMethod
import sergio.sastre.uitesting.utils.utils.drawToBitmapWithElevation
import sergio.sastre.uitesting.utils.utils.waitForMeasuredView

fun ScreenshotScenarioRule.assertSame(name: String?) {
if (name != null) {
// this is how to change the name of the screenshot file
InstrumentationRegistry.getInstrumentation().testDescription = TestDescription(
methodName = name,
testClass = InstrumentationRegistry.getInstrumentation().testDescription.testClass
)
}
assertSame()
}

fun ScreenshotScenarioRule.waitForIdleSync(): ScreenshotScenarioRule = apply {
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
}

internal fun ScreenshotScenarioRule.setDialogViewUnderTest(
view: View,
): ScreenshotScenarioRule = apply {
setScreenshotViewProvider {
InstrumentationRegistry.getInstrumentation().run {
runOnMainSync {
(view.parent as ViewGroup?)?.removeAllViews()
}
waitForIdleSync()

runOnMainSync {
(activity.window.decorView as ViewGroup).addView(view)
}
waitForIdleSync()
}
waitForMeasuredView { view }
}
}

internal fun ScreenshotScenarioRule.setViewUnderTest(
view: View,
): ScreenshotScenarioRule = apply {
setScreenshotViewProvider {
waitForMeasuredView { view }
}
}

internal fun ScreenshotScenarioRule.setBitmapCaptureMethod(
bitmapCaptureMethod: BitmapCaptureMethod?,
): ScreenshotScenarioRule = apply {
when (bitmapCaptureMethod) {
is BitmapCaptureMethod.Canvas -> {
fun canvas(activity: Activity, targetView: View?): Bitmap? {
return targetView?.drawToBitmap(bitmapCaptureMethod.config)
}
configure { captureMethod = ::canvas }
}

is BitmapCaptureMethod.PixelCopy -> {
fun pixelCopy(activity: Activity, targetView: View?): Bitmap? {
return targetView?.drawToBitmapWithElevation(
activity = activity,
config = bitmapCaptureMethod.config
)
}
configure { captureMethod = ::pixelCopy }
}

null -> { /*no-op*/ }
}
}

fun ScreenshotScenarioRule.generateDiffs(
generate: Boolean,
): ScreenshotScenarioRule = apply {
if (generate) {
TestifyFeatures.GenerateDiffs.setEnabled(true)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package sergio.sastre.uitesting.android_testify.screenshotscenario

import androidx.compose.runtime.Composable
import dev.testify.core.TestifyConfiguration
import dev.testify.scenario.ScreenshotScenarioRule
import org.junit.rules.RuleChain
import org.junit.runner.Description
import org.junit.runners.model.Statement
import sergio.sastre.uitesting.android_testify.AndroidTestifyConfig
import sergio.sastre.uitesting.utils.activityscenario.ActivityScenarioForComposableRule
import sergio.sastre.uitesting.utils.crosslibrary.config.LibraryConfig
import sergio.sastre.uitesting.utils.crosslibrary.config.ScreenshotConfigForComposable
import sergio.sastre.uitesting.utils.crosslibrary.testrules.ScreenshotTestRuleForComposable
import sergio.sastre.uitesting.utils.testrules.animations.DisableAnimationsRule

class ScreenshotScenarioRuleForComposable(
override val config: ScreenshotConfigForComposable = ScreenshotConfigForComposable(),
) : ScreenshotTestRuleForComposable(config) {

private val screenshotRule: ScreenshotScenarioRule by lazy {
ScreenshotScenarioRule(
configuration = TestifyConfiguration(
exactness = androidTestifyConfig.exactness,
hideCursor = androidTestifyConfig.hideCursor,
hidePasswords = androidTestifyConfig.hidePasswords,
hideScrollbars = androidTestifyConfig.hideScrollbars,
hideSoftKeyboard = androidTestifyConfig.hideSoftKeyboard,
hideTextSuggestions = androidTestifyConfig.hideTextSuggestions,
useSoftwareRenderer = androidTestifyConfig.useSoftwareRenderer,
exclusionRects = androidTestifyConfig.exclusionRects,
exclusionRectProvider = androidTestifyConfig.exclusionRectProvider,
pauseForInspection = androidTestifyConfig.pauseForInspection,
compareMethod = androidTestifyConfig.compareMethod,
),
enableReporter = androidTestifyConfig.enableReporter,
)
}

// Use only for the context and inflate methods, because for ScreenshotRule it would crash...
private val activityScenarioRule by lazy {
ActivityScenarioForComposableRule(
backgroundColor = androidTestifyConfig.backgroundColor,
config = config.toComposableConfig()
)
}

private var androidTestifyConfig: AndroidTestifyConfig = AndroidTestifyConfig()

override fun apply(
base: Statement,
description: Description,
): Statement =
when (androidTestifyConfig.animationsDisabled) {
true -> RuleChain
.outerRule(DisableAnimationsRule())
.around(activityScenarioRule)
.around(screenshotRule)
.apply(base, description)
false -> RuleChain
.outerRule(activityScenarioRule)
.around(screenshotRule)
.apply(base, description)
}

override fun snapshot(composable: @Composable () -> Unit) {
screenshotRule
.withScenario(activityScenarioRule.activityScenario)
.setScreenshotViewProvider {
activityScenarioRule.setContent { composable() }.composeView
}
.setBitmapCaptureMethod(androidTestifyConfig.bitmapCaptureMethod)
.generateDiffs(androidTestifyConfig.generateDiffs)
.assertSame(null)
}

override fun snapshot(name: String?, composable: @Composable () -> Unit) {
screenshotRule
.withScenario(activityScenarioRule.activityScenario)
.setScreenshotViewProvider {
activityScenarioRule.setContent { composable() }.composeView
}
.setBitmapCaptureMethod(androidTestifyConfig.bitmapCaptureMethod)
.generateDiffs(androidTestifyConfig.generateDiffs)
.assertSame(name = name)
}

override fun configure(config: LibraryConfig): ScreenshotTestRuleForComposable {
if (config is AndroidTestifyConfig) {
androidTestifyConfig = config
}
return this
}
}
Loading

0 comments on commit 87566a2

Please sign in to comment.