Make JVM Android integration test visible 🤖📸
Roborazzi has supported Compose Desktop, but we hadn't yet supported iOS, which could be a major use case for Compose Multiplatform. Therefore, I have added support for iOS. Now, you can record, compare, and verify just as you would with Android support. https://takahirom.github.io/roborazzi/compose-multiplatform.html#experimental-feature-ios-support
However, Roborazzi is fundamentally based on the JVM. Currently, we offer only minimal features. If you are interested in helping to improve these features, please take a look at these issues: https://github.com/takahirom/roborazzi/issues/302 https://github.com/takahirom/roborazzi/issues/305
To support iOS, we have made some dependency changes for Android and Compose Desktop. I believe these changes will not affect existing behavior. However, if you notice anything, please let me know.
All Kotlin: 1.8.22 -> 1.9.21
dropbox/differ: 0.0.1 -> 0.0.2
Compose Multiplatform: 1.4.3 -> 1.6.1
Full Changelog: https://github.com/takahirom/roborazzi/compare/1.12.0...1.13.0
robolectric.screenshot.hwrdr.native
introduced in Robolectric 4.12, which caused unwanted shadows when using Compose's roboCaptureImage{}
with this new option. This has now been corrected by @lukas-mercari. Thanks for your contribution!Full Changelog: https://github.com/takahirom/roborazzi/compare/1.11.0...1.12.0
In Roborazzi, if you specify outputDir in the Gradle settings, you can use the build cache. Now, Roborazzi passes the setting into the test.
build.gradle
roborazzi {
outputDir = "src/your/screenshot/folder"
}
roborazzi.record.filePathStrategy=relativePathFromRoborazziContextOutputDirectory
Test
captureRoboImage()
-> saved src/your/screenshot/folder/package.class.method.png
captureRoboImage("test.png")
-> saved src/your/screenshot/folder/test.png
Full Changelog: https://github.com/takahirom/roborazzi/compare/1.10.1...1.11.0
This release includes a bug fix for a Javascript error that prevented the HTML report from being displayed.
Custom context data enables the addition of information to images and reports in Roborazzi's tests, which I believe is very important. For example, it can include the test class name of a screenshot or whether it is in dark mode. You can now add custom context data using RoborazziOptions, and Roborazzi will add the test class name metadata if you use RoborazziRule. If you have any opinions about this feature, please let me know at https://github.com/takahirom/roborazzi/issues/257. Furthermore, this opens up possibilities with AI. Given that AI now possesses multimodal capabilities, it has become feasible for AI to process images. This feature was made possible thanks to @sanao1006 's contribution of migrating from org.json to gson.
onView(ViewMatchers.isRoot())
.captureRoboImage(
roborazziOptions = RoborazziOptions(
contextData = mapOf(
"context_data_key" to "context_data_value"
)
)
)
}
Gradle attempts to load the test cache whenever possible, but there was an issue where Roborazzi couldn't restore images from the cache. This release includes a fix for this problem. Thank you, @francescocervone, for reporting this issue.
org.json
Library to Gson
by @sanao1006 in https://github.com/takahirom/roborazzi/pull/248
Full Changelog: https://github.com/takahirom/roborazzi/compare/1.9.0...1.10.0
Custom context data enables the addition of information to images and reports in Roborazzi's tests, which I believe is very important. For example, it can include the test class name of a screenshot or whether it is in dark mode. You can now add custom context data using RoborazziOptions, and Roborazzi will add the test class name metadata if you use RoborazziRule. If you have any opinions about this feature, please let me know at https://github.com/takahirom/roborazzi/issues/257. Furthermore, this opens up possibilities with AI. Given that AI now possesses multimodal capabilities, it has become feasible for AI to process images. This feature was made possible thanks to @sanao1006 's contribution of migrating from org.json to gson.
onView(ViewMatchers.isRoot())
.captureRoboImage(
roborazziOptions = RoborazziOptions(
contextData = mapOf(
"context_data_key" to "context_data_value"
)
)
)
}
Gradle attempts to load the test cache whenever possible, but there was an issue where Roborazzi couldn't restore images from the cache. This release includes a fix for this problem. Thank you, @francescocervone, for reporting this issue.
org.json
Library to Gson
by @sanao1006 in https://github.com/takahirom/roborazzi/pull/248
Full Changelog: https://github.com/takahirom/roborazzi/compare/1.9.0...1.10.0
We're making some changes to our release strategy to enhance your experience. Moving forward, we will be streamlining our versioning system. Instead of maintaining separate alpha/rc/stable
versions, we will integrate experimental features directly into stable releases, marked with clear experimental annotations. This approach aims to simplify updates and improve clarity while ensuring you still have access to the latest features and improvements.
We value your input and experience. If you have any thoughts or feedback on this change, please feel free to share them with us on GitHub Issue #243.
Introduction of captureScreenRoboImage()
Function:
This function executes screenshot tests that include dialogs on the screen, offering an alternative to the conventional use of Espresso's ViewInteraction or Compose Test's SemanticsNodeInteraction captureRoboImage(). Thank you, @nelletto, for bringing this issue with dialog screenshots to our attention.
Before:
onRoot().captureRoboImage()
onView(isRoot()).captureRoboImage()
After:
captureScreenRoboImage()
RoborazziTaskType Property: I developed Roborazzi to facilitate layout viewing during UI tests, addressing the limitations in Robolectric's layout visibility. Initially, Roborazzi couldn't support just viewing layouts during the verification task (roborazziVerifyDebug). Hence, I've introduced a feature allowing task type alteration during test executions.
onView(ViewMatchers.isRoot())
.captureRoboImage(
roborazziOptions = RoborazziOptions(
taskType = roborazziSystemPropertyTaskType().convertVerifyingToComparing()
)
)
compose captureRoboImage{}
multiple times within a single test. (Thanks for reporting this @vetoketju )relativePathFromRoborazziContextOutputDirectory
resulted in duplicated file paths, like build/output/roborazzi/build/output/roborazzi/xxxx.png
.Documentation Enhancement: Added comprehensive documentation using Writerside, a documentation tool from JetBrains. Roborazzi Documentation Thank you, @timothyfroehlich, @sergio-sastre, and @ZacSweers, for your suggestions regarding documentation tools.
Thanks to @itochan's contribution, Roborazzi has moved to a version catalog, reduced unwanted dependencies and organized
Enhanced performance.
Fix Bug Causing Comparison Image to Enlarge and Enhance Performance by Avoiding Creation of Unnecessary Canvases
Use ComposeTestRule interface instead of concrete AndroidComposeTestRule class by @GisoBartels in https://github.com/takahirom/roborazzi/pull/241
Pass the default output directory setting from Gradle
Full Changelog: https://github.com/takahirom/roborazzi/compare/1.8.0...1.9.0
We're making some changes to our release strategy to enhance your experience. Moving forward, we will be streamlining our versioning system. Instead of maintaining separate alpha/rc/stable
versions, we will integrate experimental features directly into stable releases, marked with clear experimental annotations. This approach aims to simplify updates and improve clarity while ensuring you still have access to the latest features and improvements.
We value your input and experience. If you have any thoughts or feedback on this change, please feel free to share them with us on GitHub Issue #243.
roboOutputName()
functionStreamline the customization of Roborazzi image file names. This utility is especially effective in parameterized tests, allowing for dynamic file naming based on test parameters. For an example of its usage, see the snippet below, which demonstrates generating screenshots before and after UI interactions.
@Test
fun launchScreen() {
// Generates a file named "org.your.pkg.TestClassName.launchScreen_before.png"
onView(ViewMatchers.isRoot()).captureRoboImage("${roboOutputName()}_before.png")
// Replace with specific actions, e.g., onView(xxx).performClick()
// Generates a file named "org.your.pkg.TestClassName.launchScreen_after.png"
onView(ViewMatchers.isRoot()).captureRoboImage("${roboOutputName()}_after.png")
}
Tailor your file naming convention in gradle.properties
for even more control, such as omitting the package name.
Set roborazzi.record.namingStrategy=testClassAndMethod
for a streamlined naming pattern.
Learn more: Roborazzi Documentation
This update introduces a new grid and label feature, making visual comparisons more intuitive and effective. The grid layout provides a structured view, while labels offer clear identification, streamlining the testing process.
You can use the old style by setting ComparisonStyle to ComparisonStyle.Simple in RoborazziOptions
data class CompareOptions(
...
val comparisonStyle: ComparisonStyle = ComparisonStyle.Grid(),
) {
@ExperimentalRoborazziApi
sealed interface ComparisonStyle {
@ExperimentalRoborazziApi
data class Grid(
val bigLineSpaceDp: Int? = 16,
val smallLineSpaceDp: Int? = 4,
val hasLabel: Boolean = true
) : ComparisonStyle
object Simple : ComparisonStyle
}
You can now modify the ImageComparator using CompareOptions.imageComparator.
Set the default value of CompareOptions.changeThreshold to zero. This means it will detect even a single pixel change.
Full Changelog: https://github.com/takahirom/roborazzi/compare/1.7.0...1.8.0
roboOutputName()
functionStreamline the customization of Roborazzi image file names. This utility is especially effective in parameterized tests, allowing for dynamic file naming based on test parameters. For an example of its usage, see the snippet below, which demonstrates generating screenshots before and after UI interactions.
@Test
fun launchScreen() {
// Generates a file named "org.your.pkg.TestClassName.launchScreen_before.png"
onView(ViewMatchers.isRoot()).captureRoboImage("${roboOutputName()}_before.png")
// Replace with specific actions, e.g., onView(xxx).performClick()
// Generates a file named "org.your.pkg.TestClassName.launchScreen_after.png"
onView(ViewMatchers.isRoot()).captureRoboImage("${roboOutputName()}_after.png")
}
Tailor your file naming convention in gradle.properties
for even more control, such as omitting the package name.
Set roborazzi.record.namingStrategy=testClassAndMethod
for a streamlined naming pattern.
Learn more: Roborazzi Documentation
This update introduces a new grid and label feature, making visual comparisons more intuitive and effective. The grid layout provides a structured view, while labels offer clear identification, streamlining the testing process.
You can use the old style by setting ComparisonStyle to ComparisonStyle.Simple in RoborazziOptions
data class CompareOptions(
...
val comparisonStyle: ComparisonStyle = ComparisonStyle.Grid(),
) {
@ExperimentalRoborazziApi
sealed interface ComparisonStyle {
@ExperimentalRoborazziApi
data class Grid(
val bigLineSpaceDp: Int? = 16,
val smallLineSpaceDp: Int? = 4,
val hasLabel: Boolean = true
) : ComparisonStyle
object Simple : ComparisonStyle
}
You can now modify the ImageComparator using CompareOptions.imageComparator.
Set the default value of CompareOptions.changeThreshold to zero. This means it will detect even a single pixel change.
Full Changelog: https://github.com/takahirom/roborazzi/compare/1.7.0...1.8.0-rc-1
Full Changelog: https://github.com/takahirom/roborazzi/compare/1.9.0-alpha-3...1.9.0-alpha-4
Thanks to @itochan's contribution, Roborazzi has moved to a version catalog, reduced unwanted dependencies and organized
Full Changelog: https://github.com/takahirom/roborazzi/compare/1.9.0-alpha-2...1.9.0-alpha-3