Skip to content

Commit 50a0996

Browse files
committed
Migrate helper classes and extensions to Ktx library
1 parent 21b43ac commit 50a0996

30 files changed

+304
-194
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## 5.0.0
2+
3+
- https://github.com/ndtp/android-testify/pull/XXX
4+
- `dev.testify.internal.extensions.cyan` moved to `dev.testify.extensions.cyan`
5+
- Java interop for `findAnnotation` method is now available from `AnnotationExtensionsKtx`
6+
- Java interop for `instrumentationPrintln` method is now available from `InstrumentationRegistryExtensionsKt`
7+
- Java interop for `getModuleName` method is now available from `InstrumentationRegistryExtensionsKt`
8+
- `fun Context.updateLocale(locale: Locale?): Context` is now public
9+
- `fun getMetaDataBundle(context: Context): Bundle?` is now public
10+
111
## 4.0.0
212

313
- https://github.com/ndtp/android-testify/pull/266 Updated to Kotlin 2.2

Ext/Accessibility/src/main/java/dev/testify/accessibility/internal/AccessibilityScreenshotLifecycleObserver.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ import com.google.android.apps.common.testing.accessibility.framework.Accessibil
3535
import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchyAndroid
3636
import dev.testify.ScreenshotLifecycle
3737
import dev.testify.accessibility.exception.AccessibilityErrorsException
38+
import dev.testify.extensions.cyan
3839
import dev.testify.internal.extensions.TestInstrumentationRegistry
3940
import dev.testify.internal.extensions.TestInstrumentationRegistry.instrumentationPrintln
4041
import dev.testify.internal.extensions.TestInstrumentationRegistry.isRecordMode
41-
import dev.testify.internal.extensions.cyan
4242
import dev.testify.testDescription
4343
import java.util.Locale
4444

Ext/Ktx/README.md

Lines changed: 12 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
1-
# Testify — Android Screenshot Testing — Fullscreen capture method
1+
# Testify — Android Screenshot Testing — Kotlin Extensions
22

3-
<a href="https://search.maven.org/artifact/dev.testify/testify-fullscreen"><img alt="Maven Central" src="https://img.shields.io/maven-central/v/dev.testify/testify-fullscreen?color=%236e40ed&label=dev.testify%3Atestify-fullscreen"/></a>
3+
<a href="https://search.maven.org/artifact/dev.testify/testify-kts"><img alt="Maven Central" src="https://img.shields.io/maven-central/v/dev.testify/testify-ktx?color=%236e40ed&label=dev.testify%3Atestify-ktx"/></a>
44

5-
**Capture the entire device screen, including system UI, dialogs and menus.**
5+
**Kotlin extensions for Android Testify, providing more idiomatic and helper APIs to work with screenshot testing in Android.**
66

7-
Use the [UiAutomator's](https://developer.android.com/training/testing/other-components/ui-automator) built-in [screenshotting](https://developer.android.com/reference/androidx/test/uiautomator/UiDevice#takescreenshot) capability to capture a [Bitmap](https://developer.android.com/reference/android/graphics/Bitmap) of the entire device.
7+
The new KTX library packages up a set of foundational utilities that originally lived deep inside Testify’s screenshot testing engine. These components are broadly useful for any instrumentation test suite. By extracting and stabilizing these internals, the library provides a standalone toolkit that improves the reliability, predictability, and ergonomics of your androidTest environment, even if you never call a screenshot API.
88

9-
The bitmap will be generated from a PNG at 1:1 scale and 100% quality. The bitmap's size will match the full device resolution and include all system UI such as the status bar and navigation bar.
9+
Why Use Testify KTX?
1010

11-
As the system UI content is highly variable, you can use [ScreenshotRule.excludeStatusBar](./src/main/java/dev/testify/capture/fullscreen/provider/StatusBarExclusionRectProvider.kt) and/or [ScreenshotRule.excludeNavigationBar](./src/main/java/dev/testify/capture/fullscreen/provider/NavigationBarExclusionRectProvider.kt) to ignore the status bar and navigation bar, respectively.
11+
- Adds idiomatic Kotlin helpers around core Testify APIs, reducing boilerplate.
12+
- Provides a simplified set of file I/O utilities for files on the emulator SD card, `data/data` directory, or Test Storage.
13+
- Includes utilities for working with annotations, device identification, and test instrumentation.
1214

13-
Though the PNG is intended to be lossless, some compression artifacts or GPU-related variance can occur. As such, it is recommended to use a small tolerance when capturing fullscreen images.
14-
15-
You can set a comparison tolerance using [ScreenshotRule.setExactness](../../Library/src/main/java/dev/testify/ScreenshotRule.kt).
16-
17-
# Set up testify-fullscreen
15+
# Set up testify-ktx
1816

1917
**Root build.gradle**
2018

@@ -31,43 +29,20 @@ Ensure that `mavenCentral()` is available to both `pluginManagement` and `depend
3129
**Application build.gradle**
3230
```groovy
3331
dependencies {
34-
androidTestImplementation "dev.testify:testify-fullscreen:3.2.3"
32+
androidTestImplementation "dev.testify:testify-ktx:3.2.3"
3533
}
3634
```
3735

38-
# Write a test
39-
40-
In order to capture the full device screen, you must set the capture method on `ScreenshotRule` to `fullscreenCapture()`.
41-
You can do this with either `setCaptureMethod(::fullscreenCapture)` or the helper extension method `captureFullscreen()`.
42-
43-
Additonal examples can be found in [FullscreenCaptureExampleTest.kt](../../Samples/Legacy/src/androidTest/java/dev/testify/sample/FullscreenCaptureExampleTests.kt).
44-
45-
```kotlin
46-
class FullscreenCaptureTest {
36+
# Included API
4737

48-
@get:Rule
49-
var rule = ScreenshotRule(MainActivity::class.java)
50-
51-
@ScreenshotInstrumentation
52-
@Test
53-
fun fullscreen() {
54-
rule
55-
.captureFullscreen() // Set the fullscreen capture method
56-
.excludeSystemUi() // Exclude the navigation bar and status bar areas from the comparison
57-
.setExactness(0.95f) // Allow a 5% variation in color
58-
.assertSame()
59-
}
60-
}
61-
62-
```
6338

6439
---
6540

6641
# License
6742

6843
MIT License
6944

70-
Copyright (c) 2022 ndtp
45+
Copyright (c) 2025 ndtp
7146

7247
Permission is hereby granted, free of charge, to any person obtaining a copy
7348
of this software and associated documentation files (the "Software"), to deal

Ext/Ktx/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ plugins {
22
id 'com.android.library'
33
id 'kotlin-android'
44
id 'org.jetbrains.dokka'
5+
id 'maven-publish'
6+
id 'signing'
57
}
68

79
ext {
@@ -74,6 +76,7 @@ android {
7476
implementation libs.androidx.rules
7577
implementation libs.androidx.uiautomator
7678
implementation libs.core.ktx
79+
implementation libs.androidx.test.storage
7780
}
7881

7982
publishing {
@@ -88,5 +91,3 @@ android {
8891
afterEvaluate {
8992
apply from: "../../publish.build.gradle"
9093
}
91-
92-
apply from: '../../ktlint.gradle'

Library/src/main/java/dev/testify/CaptureMethod.kt renamed to Ext/Ktx/src/main/java/dev/testify/CaptureMethod.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* The MIT License (MIT)
33
*
4-
* Copyright (c) 2022 ndtp
4+
* Copyright (c) 2022-2025 ndtp
55
*
66
* Permission is hereby granted, free of charge, to any person obtaining a copy
77
* of this software and associated documentation files (the "Software"), to deal

Library/src/main/java/dev/testify/ScreenshotUtility.kt renamed to Ext/Ktx/src/main/java/dev/testify/ScreenshotUtility.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* The MIT License (MIT)
33
*
4-
* Modified work copyright (c) 2022 ndtp
4+
* Modified work copyright (c) 2022-2025 ndtp
55
* Original work copyright (c) 2019 Shopify Inc.
66
*
77
* Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -60,7 +60,7 @@ val preferredBitmapOptions: BitmapFactory.Options
6060
*
6161
* @param context The [Context] to use when writing the bitmap to disk.
6262
* @param bitmap The [Bitmap] to write to disk. If null, this function will return false.
63-
* @param destination The [Destination] to write the bitmap to.
63+
* @param destination The [dev.testify.output.Destination] to write the bitmap to.
6464
*
6565
* @throws Exception if the destination cannot be found.
6666
*
@@ -130,7 +130,7 @@ fun loadBaselineBitmapForComparison(
130130
*
131131
* @param activity The [Activity] instance to capture.
132132
* @param fileName The name to use when writing the captured image to disk.
133-
* @param captureMethod a [CaptureMethod] that will return a [Bitmap] from the provided [Activity] and [View]
133+
* @param captureMethod a [dev.testify.CaptureMethod] that will return a [Bitmap] from the provided [Activity] and [View]
134134
* @param screenshotView A [View] found in the [activity]'s view hierarchy.
135135
* If screenshotView is null, defaults to activity.window.decorView.
136136
*
@@ -182,7 +182,7 @@ fun loadBitmapFromFile(outputPath: String, preferredBitmapOptions: BitmapFactory
182182
/**
183183
* Delete the Bitmap [File] specified by [destination].
184184
*
185-
* @param destination The [Destination] to delete.
185+
* @param destination The [dev.testify.output.Destination] to delete.
186186
*
187187
* @return true if the file was successfully deleted, false otherwise.
188188
*/
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2022-2025 ndtp
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
@file:JvmName("AnnotationExtensionsKtx")
25+
package dev.testify.annotation
26+
27+
/**
28+
* Find the first [Annotation] in the given [Collection] which is of type [T]
29+
*
30+
* @return Annotation of type T
31+
*/
32+
inline fun <reified T : Annotation> Collection<Annotation>.findAnnotation(): T? =
33+
this.find { it is T } as? T
34+
35+
/**
36+
* Find the first [Annotation] in the given [Collection] which is of type [T]
37+
*
38+
* @return Annotation of type T
39+
*/
40+
fun <T : Annotation> Collection<Annotation>.findAnnotation(clazz: Class<T>): T? =
41+
this.find { clazz.isInstance(it) }?.let { clazz.cast(it) }
42+
43+
/**
44+
* Find the first [Annotation] in the given [Collection] which has the given [name]
45+
*
46+
* @param name - The qualified class name of the requested annotation
47+
*
48+
* @return Annotation of type T
49+
*/
50+
inline fun <reified T : Annotation> Collection<Annotation>.findAnnotation(name: String): T? =
51+
this.find { it.annotationClass.qualifiedName == name } as? T
52+
53+
/**
54+
* Find the first [Annotation] in the given [Collection] which has the given [name]
55+
*
56+
* @param name - The qualified class name of the requested annotation
57+
*
58+
* @return Annotation of type T
59+
*/
60+
fun <T : Annotation> Collection<Annotation>.findAnnotation(name: String, clazz: Class<T>): T? =
61+
this.find { it.annotationClass.qualifiedName == name && clazz.isInstance(it) }?.let { clazz.cast(it) }

Library/src/main/java/dev/testify/core/DeviceIdentifier.kt renamed to Ext/Ktx/src/main/java/dev/testify/core/DeviceIdentifier.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* The MIT License (MIT)
33
*
4-
* Modified work copyright (c) 2022 ndtp
4+
* Modified work copyright (c) 2022-2025 ndtp
55
* Original work copyright (c) 2019 Shopify Inc.
66
*
77
* Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -31,6 +31,7 @@ import android.view.WindowManager
3131
import dev.testify.internal.extensions.languageTag
3232
import dev.testify.internal.helpers.buildVersionSdkInt
3333
import java.util.Locale
34+
import kotlin.text.iterator
3435

3536
/**
3637
* A typealias for the test class and test name.

Library/src/main/java/dev/testify/core/exception/RootViewNotFoundException.kt renamed to Ext/Ktx/src/main/java/dev/testify/core/exception/RootViewNotFoundException.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* The MIT License (MIT)
33
*
4-
* Modified work copyright (c) 2022 ndtp
4+
* Modified work copyright (c) 2022-2025 ndtp
55
* Original work copyright (c) 2019 Shopify Inc.
66
*
77
* Permission is hereby granted, free of charge, to any person obtaining a copy

Library/src/main/java/dev/testify/core/exception/TestifyException.kt renamed to Ext/Ktx/src/main/java/dev/testify/core/exception/TestifyException.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/*
22
* The MIT License (MIT)
33
*
4-
* Copyright (c) 2023 ndtp
4+
* Modified work copyright (c) 2022-2025 ndtp
5+
* Original work copyright (c) 2019 Shopify Inc.
56
*
67
* Permission is hereby granted, free of charge, to any person obtaining a copy
78
* of this software and associated documentation files (the "Software"), to deal

0 commit comments

Comments
 (0)