diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..ba04e8b0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,34 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG] " +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots/Videos** +If applicable, add screenshots or recordings to help explain your problem. + +**Environment (please complete the following information):** + - JDK Version: [e.g. 17, 21] + - AGP Version: [e.g. 9.0.0] + - Composable Preview Scanner Version: [e.g. 0.8.0] + - Kotlin Version: [e.g. 2.0.0] + - Compose Version: [e.g. 1.7.0] + +**Additional context** +Add any other context about the problem here (e.g., logs, stack traces). diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..8cf1fda2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: GitHub Discussions + url: https://github.com/sergio-sastre/ComposablePreviewScanner/discussions + about: Please ask questions and share ideas here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..8403b86c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEATURE] " +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..035f7bfe --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and unwelcome sexual attention or + advances +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for enforcement +decisions as appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +sergio.sastre.florez@gmail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of community standards violations, +including sustained inappropriate behavior, harassment of an individual, or +aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][faq]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[faq]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations +[Mozilla CoC]: https://github.com/mozilla/diversity diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..a13ed716 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,45 @@ +# Contributing to Composable Preview Scanner + +Thank you for your interest in contributing to Composable Preview Scanner! We welcome contributions from the community to help make this library better for everyone. + +This project is developed using **Test-Driven Development (TDD)** and maintains strict **binary compatibility** using **Metalava**. + +## How to Contribute + +### Reporting Issues +If you find a bug or have a feature request, please [open an issue](https://github.com/sergio-sastre/ComposablePreviewScanner/issues). When reporting a bug, please include: +- A clear and descriptive title. +- Steps to reproduce the issue. +- Expected vs. actual behavior. +- Relevant environment details (OS, JDK version, Compose version, etc.). + +### Submitting Pull Requests +1. **Fork the repository** and create your branch from `master`. +2. **Implement your changes.** If you are adding a new feature or fixing a bug, please include corresponding tests. We follow a TDD approach. +3. **Ensure the build passes.** Run the relevant Gradle tasks (see [Verification](#verification) below). +4. **Follow the code style.** Maintain consistency with the existing codebase. +5. **Update documentation** if your changes introduce new APIs or change existing behavior. +6. **Submit a Pull Request** with a clear description of your changes and a link to the relevant issue. + +## Development Setup +- **JDK 17+** is required. +- **Android Studio** (latest stable version) is recommended for development. + +## Verification +To maintain high quality, all PRs must pass the following custom Gradle tasks, which are also automatically executed in our **Continuous Integration (CI)** pipeline. We recommend running them locally before submitting your changes: + +| Task | Description | +| :--- | :--- | +| `./gradlew :tests:testApi` | Runs API logic tests. | +| `./gradlew :tests:testSourceSets` | Runs SourceSet logic tests. | +| `./gradlew :tests:paparazziPreviews` | Runs Paparazzi integration tests. | +| `./gradlew :tests:roborazziPreviews` | Runs Roborazzi integration tests. | + +### Binary Compatibility +We use **Metalava** to track API changes and prevent breaking binary compatibility for our users. If your changes modify public APIs, ensure you've considered the impact and updated the API tracking files accordingly. + +## Code of Conduct +Please be respectful and professional in all interactions within this project. + +## License +By contributing, you agree that your contributions will be licensed under the project's [LICENSE](https://github.com/sergio-sastre/ComposablePreviewScanner/blob/main/LICENSE). diff --git a/README.md b/README.md index 6b2e664b..d141c977 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,99 @@ -[![](https://jitpack.io/v/sergio-sastre/ComposablePreviewScanner.svg)](https://jitpack.io/#sergio-sastre/ComposablePreviewScanner) ![](https://jitpack.io/v/sergio-sastre/ComposablePreviewScanner/month.svg)
-[![](https://img.shields.io/badge/dynamic/xml.svg?color=brightgreen&label=Maven%20Central&query=%2F%2Fmetadata%2Fversioning%2Frelease&url=https%3A%2F%2Frepo.maven.apache.org%2Fmaven2%2Fio%2Fgithub%2Fsergio-sastre%2FComposablePreviewScanner%2Fandroid%2Fmaven-metadata.xml)](https://central.sonatype.com/artifact/io.github.sergio-sastre.ComposablePreviewScanner/android) ![](https://img.shields.io/badge/downloads-unknown-yell)
- -
+
As Seen In - jetc.dev Newsletter Issue #221 -#

Composable Preview Scanner

+

+ +


+[![](https://img.shields.io/badge/Kotlin-Multiplatform-%237f52ff?logo=kotlin&logoColor=white)](https://kotlinlang.org/docs/multiplatform.html) [![](https://img.shields.io/badge/Platform-Android-3DDC84?logo=android&logoColor=white)](#) [![](https://img.shields.io/badge/Platform-Desktop-0078D4?logo=openjdk&logoColor=white)](#)
-

- -

+A Compose Multiplatform friendly library to help auto-generate screenshot tests from Composable Previews (e.g. **Android**, **Glance**) with any screenshot testing library: +JVM-based (i.e. Paparazzi, Roborazzi) as well as Instrumentation-based (i.e. Shot, Dropshots, Android-Testify, etc.). -A library to help auto-generate screenshot tests from Composable Previews (e.g. **Android**, **Glance** & **Compose Multiplatform**) with any screenshot testing library: -JVM-based (i.e. Paparazzi, Roborazzi) as well as Instrumentation-based (i.e. Shot, Dropshots, Android-Testify, etc.) +> [!IMPORTANT] +> **Roborazzi** has integrated Composable Preview Scanner (maven-central) as a core dependency for its native [Compose Preview support](https://github.com/takahirom/roborazzi?tab=readme-ov-file#compose-preview-support-experimental).
+ +# Overview +[![](https://jitpack.io/v/sergio-sastre/ComposablePreviewScanner.svg)](https://jitpack.io/#sergio-sastre/ComposablePreviewScanner) ![](https://jitpack.io/v/sergio-sastre/ComposablePreviewScanner/month.svg)
+[![](https://img.shields.io/badge/dynamic/xml.svg?color=brightgreen&label=Maven%20Central&query=%2F%2Fmetadata%2Fversioning%2Frelease&url=https%3A%2F%2Frepo.maven.apache.org%2Fmaven2%2Fio%2Fgithub%2Fsergio-sastre%2FComposablePreviewScanner%2Fandroid%2Fmaven-metadata.xml)](https://central.sonatype.com/artifact/io.github.sergio-sastre.ComposablePreviewScanner/android) ![](https://img.shields.io/badge/downloads-unknown-yell)
+With over **300,000 monthly downloads** (JitPack + Maven Central), Composable Preview Scanner is a trusted solution for automated visual UI verification in the Compose ecosystem. ![composable_preview_scanner_overview.png](composable_preview_scanner_overview.png) > [!NOTE] -> 1. Support for Wear OS Tile `@Previews` is under evaluation
-> 2. `common` and `desktop` previews are deprecated in favour of the `android` preview (`androidx.compose.ui.tooling.preview.Preview`), which can be used -> in `common` and JVM-based source sets like `desktop` since Compose Multiplatform 1.10.0-beta-02. This library has also deprecated its support. More info in [README_DEPRECATED.md](README_DEPRECATED.md)
- - -#### Provide anonymous feedback -Already using ComposablePreviewScanner?
-I'd love to hear your thoughts!
-Help shape its future by taking [this quick survey](https://forms.gle/jcvggBxv14CLqjFo6) +> If you are still using the deprecated `org.jetbrains.compose.ui.tooling.preview.Preview`, see [README_DEPRECATED.md](README_DEPRECATED.md)
# Comparison with other solutions -| | Composable Preview Scanner | Showkase | Compose Preview Screenshot Testing | -|------------------------------------------------------|------------------------------------------------------------------------|-----------------------------------------------------------------|-----------------------------------------------| -| Independent of AGP version | ✅ | ✅ | ❌ | -| Library-agnostic solution | ✅ | ✅ | ❌1 | -| Scans previews in different sources sets2 | ✅ main
✅ screenshotTest
✅ androidTest | ✅ main
❌ screenshotTest
❌ androidTest | ❌ main
✅ screenshotTest
❌ androidTest | -| Preview Infos available | ✅ | ❌3 | ✅ | -| Specific Config (e.g. for Libs) available | ✅4 | ❌ | ⚠️5 | -| Supported Preview types | ✅ Android
✅ Glance
✅ Compose Multiplatform 6
| ✅ Android
❌Glance
❌Compose Multiplatform 7 | ✅ Android
❌ Glance
❌ Compose Multiplatform | - -1 Compose Preview Screenshot Testing is a standalone solution based on LayoutLib, whereas ComposablePreviewScanner and Showkase provide Composables' infos so you can run screenshot tests with your favourite screenshot testing library.

-2 From version 0.5.0, ComposablePreviewScanner can scan previews in any source set. Compose Preview Screenshot Testing requires to put the previews in a brand-new "screenshotTest" source.

-3 Showkase components only hold information about the Composable, but not about the Preview Info (i.e. ApiLevel, Locale, UiMode, FontScale...).

-4 ComposablePreviewScanner supports adding extra lib-config (e.g. Paparazzi's Rendering Mode or Roborazzi's compare options) in the form of annotations that are additionally added to the preview. You can check how in the examples below in [Jvm Screenshot Tests](#jvm-screenshot-tests) and [Instrumentation Screenshot Tests](#instrumentation-screenshot-tests) respectively.

-5 Compose Preview Screenshot Testing supports *only general tolerance* via gradle plugin from version [0.0.1-alpha06](https://developer.android.com/studio/preview/compose-screenshot-testing#001-alpha06)

-6 Desktop Previews (which are deprecated since Compose Multiplatform 1.10.0-beta02) are supported with a workaround. See [README_DEPRECATED.md](README_DEPRECATED.md)

+| | Composable Preview Scanner |       Showkase       | Compose Preview Screenshot Testing | +|------------------------------------------------------|-----------------------------------------------------------|----------------------------------------------------|---------------------------------------------| +| Independent of AGP version | ✅ | ✅ | ❌ | +| Library-agnostic solution | ✅ | ✅ | ❌1 | +| Scans previews in different sources sets2 | ✅ main
✅ screenshotTest
✅ androidTest | ✅ main
❌ screenshotTest
❌ androidTest | ❌ main
✅ screenshotTest
❌ androidTest | +| Preview Infos available | ✅ | ❌3 | ✅ | +| Specific Config (e.g. for Libs) available | ✅4 | ❌ | ⚠️5 | +| Supported Preview types | ✅ Android
✅ Glance
❌ Wear6 | ✅ Android
❌ Glance
❌ Wear | ✅ Android
❌ Glance
❌ Wear | +| Supported Locations in Compose Multiplatform | ✅ Android
✅ Desktop/JVM
✅ Common | ✅ Android
❌ Desktop/JVM7
❌ Common | ✅ Android
✅ Desktop/JVM
❌ Common | + +
+Click to see comparison footnotes + +1 Compose Preview Screenshot Testing is a standalone solution based on LayoutLib, whereas ComposablePreviewScanner and Showkase provide Composables' infos so you can run screenshot tests with your favourite screenshot testing library.

+2 From version 0.5.0, ComposablePreviewScanner can scan previews in any source set. Compose Preview Screenshot Testing requires to put the previews in a brand-new "screenshotTest" source.

+3 Showkase components only hold information about the Composable, but not about the Preview Info (i.e. ApiLevel, Locale, UiMode, FontScale...).

+4 ComposablePreviewScanner supports adding extra lib-config (e.g. Paparazzi's Rendering Mode or Roborazzi's compare options) in the form of annotations that are additionally added to the preview. You can check how in the examples below in [Jvm Screenshot Tests](#jvm-screenshot-tests) and [Instrumentation Screenshot Tests](#instrumentation-screenshot-tests) respectively.

+5 Compose Preview Screenshot Testing supports *only general tolerance* via gradle plugin from version [0.0.1-alpha06](https://developer.android.com/studio/preview/compose-screenshot-testing#001-alpha06)

+6 Wear Previews support is planned

7 [Showkase: Compose Multiplatform Support](https://github.com/airbnb/Showkase/issues/364) -


+

+ ComposablePreviewScanner also works with: -- **NEW*** `@PreviewWrapper` (since 0.9.0+) automatically. No changes required in the Screenshot Testing library using ComposablePreviewScanner. -- `@PreviewParameters` (for Compose Multiplatform since 0.6.0+) -- Multi-Previews, including `@PreviewScreenSizes`, `@PreviewFontScales`, `@PreviewLightDark`, and `@PreviewDynamicColors` as well as custom multi-previews. -- private `@Previews` (from version 0.1.3+) -- `@Previews` inside public classes1 (from version 0.3.0+), not nested classes though -- `@Previews` located in any source set, like "main", "screenshotTest" and "androidTest" (from version 0.5.0+) +- **NEW** `@PreviewWrapper` (since 0.9.0+) automatically. No changes required in the Screenshot Testing library using ComposablePreviewScanner. +- `@PreviewParameters` +- `@PreviewScreenSizes`, `@PreviewFontScales`, `@PreviewLightDark` and `@PreviewDynamicColors` as well as custom multi-previews. - `@Previews` with default-parameters (from version 0.5.1+) +- `@Previews` located in any source set, like "main", "screenshotTest" and "androidTest" (from version 0.5.0+) +- `@Previews` inside public classes1 (from version 0.3.0+) +- private `@Previews` (from version 0.1.3+) 1 The [Compose Preview Screenshot Testing tool](https://developer.android.com/studio/preview/compose-screenshot-testing) from Google requires you to put your `@Previews` inside a class. +## Compose Multiplatform Support +Compose Multiplatform allows you to use the standard Android Preview **`androidx.compose.ui.tooling.preview.Preview`** annotation across all relevant source sets, like **Android** and **Desktop** but also including `common`. +ComposablePreviewScanner can scan and run on both platforms, **Android** and **Desktop**, and additionally scan Previews in `common`. + +For example, to scan previews located in both a platform-specific (`androidMain` or `desktopMain`) and a ui-shared (e.g. `commonMain`) source set, you would configure the scanner like this: + +```kotlin +AndroidComposablePreviewScanner() + .scanPackageTrees( + include = listOf( + "your.package.android_or_desktop", // platform-specific + "your.package.common" // commonMain + ) + ) + .getPreviews() +``` + +> [!NOTE] +> Screenshot tests must run on a platform supported by your screenshot library.
+> • Android: Paparazzi, Roborazzi & instrumentation libraries (e.g. Dropshots, Android-Testify) are supported.
+> • Desktop: Roborazzi only.
+> • Common: no library runs directly in common; run your tests from an Android or Desktop target instead. ComposablePreviewScanner can still find the Previews in common target packages.
+ +You can find executable examples with Roborazzi here: +- [Android @Previews in common](https://github.com/sergio-sastre/roborazzi/blob/droidcon/preview_tests/sample-generate-preview-common/src/androidUnitTest/kotlin/com/github/takahirom/preview/tests/AndroidPreviewTest.kt) +- [Android @Previews in desktop](https://github.com/sergio-sastre/roborazzi/blob/droidcon/preview_tests/sample-generate-preview-desktop/src/desktopTest/kotlin/AndroidPreviewTest.kt) + +If you are still using the deprecated Common or Desktop `@Preview` annotations, see [README_DEPRECATED.md](README_DEPRECATED.md) for guidance. + # How to set up > [!WARNING] -> Beware the prefixes:
-> *Maven Central* -> **io.github**
-> *JitPack* -> **com.github**
+> Beware the prefixes:
+> *Maven Central* -> **io.github**
+> *JitPack* -> **com.github**
-## Maven Central (since 0.3.2) +## Maven Central (since 0.3.2+) ```kotlin dependencies { // android previews (androidx.compose.ui.tooling.preview.Preview) @@ -73,14 +103,6 @@ dependencies { // glance previews (androidx.glance.preview.Preview) // supported since 0.7.0+ in android target testImplementation("io.github.sergio-sastre.ComposablePreviewScanner:glance:") - - // common previews (org.jetbrains.compose.ui.tooling.preview.Preview) (deprecated) - // supported in jvm targets e.g. android & desktop - testImplementation("io.github.sergio-sastre.ComposablePreviewScanner:common:") - - // desktop previews (androidx.compose.desktop.ui.tooling.preview.Preview) via custom annotation (deprecated) - // supported in jvm targets e.g. android & desktop - testImplementation("io.github.sergio-sastre.ComposablePreviewScanner:jvm:") } ``` @@ -103,22 +125,14 @@ dependencies { // glance previews (androidx.glance.preview.Preview) // supported since 0.7.0+ in android target testImplementation("com.github.sergio-sastre.ComposablePreviewScanner:glance:") - - // common previews (org.jetbrains.compose.ui.tooling.preview.Preview) (deprecated) - // supported in jvm targets e.g. android & desktop - testImplementation("com.github.sergio-sastre.ComposablePreviewScanner:common:") - - // desktop previews (androidx.compose.desktop.ui.tooling.preview.Preview) via custom annotation (deprecated) - // supported in jvm targets e.g. android & desktop - testImplementation("com.github.sergio-sastre.ComposablePreviewScanner:jvm:") } ``` # How to use ### Examples with Screenshot Testing Libraries (Android target) -1. [JVM Screenshot Tests](#jvm-screenshot-tests)
- 1.1 [Paparazzi](#paparazzi)
- 1.2 [Roborazzi](#roborazzi)
+1. [JVM Screenshot Tests](#jvm-screenshot-tests)
+ 1.1 [Paparazzi](#paparazzi)
+ 1.2 [Roborazzi (without configuring its plugin)](#roborazzi)
2. [Instrumentation Screenshot Tests](#instrumentation-screenshot-tests) If you encounter any issues when executing the screenshot tests, take a look at the [Troubleshooting](#troubleshooting) section. @@ -131,11 +145,11 @@ If you encounter any issues when executing the screenshot tests, take a look at 2. [Compose Multiplatform Support](#compose-multiplatform-support) ## API -`AndroidComposablePreviewScanner`, `GlanceComposablePreviewScanner`, `CommonComposablePreviewScanner` (deprecated), and `JvmAnnotationScanner` (deprecated) have the same API. +`AndroidComposablePreviewScanner` and `GlanceComposablePreviewScanner` have the same API. The API is pretty simple: ```kotlin -AndroidComposablePreviewScanner() +AndroidComposablePreviewScanner() // or GlanceComposablePreviewScanner() // Optional to log scanning info like scanning time or amount of previews found .enableScanningLogs() // Optional to scan previews in compiled classes of other source sets, like "screenshotTest" or "androidTest" @@ -169,9 +183,9 @@ AndroidComposablePreviewScanner() ) // Optional to also provide private Previews .includePrivatePreviews() - // Optional to filter by any previewInfo: name, group, apiLevel, locale, uiMode, fontScale... + // Optional to filter by any previewInfo: heightDp, widthDp... .filterPreviews { - previewInfo -> previewInfo.apiLevel == 30 + previewInfo -> previewInfo.heightDP >= 800 } // --- .getPreviews() @@ -182,7 +196,7 @@ AndroidComposablePreviewScanner() ### Scanning Source Sets (screenshotTest, androidTest, main) By default, ComposablePreviewScanner scans `@Preview`s in the `main` Source Set at build time. However, one can scan previews in other Source Sets different from `main` by using `.setTargetSourceSet(classpath:Classpath)`, -where `classpath` is the local path to the compiled classes of that Source Set.
+where `classpath` is the local path to the compiled classes of that Source Set.
ComposablePreviewScanner provides some default values to facilitate this: ```kotlin // Previews under "screenshotTest" @@ -192,6 +206,9 @@ Classpath(SourceSet.SCREENSHOT_TEST) Classpath(SourceSet.ANDROID_TEST) ``` +
+Click to see scanning Source Set instructions + #### Ensure compiled classes exist You have to make sure the corresponding compiled classes for that Source Set exist and are up to date. The simplest way is to execute the corresponding compile task before running your tests or dumping the scan result to a file, namely `:compileKotlin`, for instance @@ -213,7 +230,7 @@ tasks.withType { #### Ensure Source Set dependencies available in tests Last but not least, make sure all the code inside the previews of the target Source Set is also -available in `test` (for Roborazzi and Paparazzi) or `android test` (for any instrumentation-based library).
+available in `test` (for Roborazzi and Paparazzi) or `android test` (for any instrumentation-based library).
So, let's say that you only have `@Preview`s in `screenshotTest`, and not in `main`. Therefore you've only added that dependency to `screenshotTest`: ```kotlin screenshotTestImplementation("androidx.compose.ui:ui-tooling-preview:") @@ -227,6 +244,7 @@ testImplementation("androidx.compose.ui:ui-tooling-preview:") > [!WARNING] > For instrumentation tests and Source Sets different from `main` or `androidTest`, like `screenshotTest`, you'll also need to ensure that the classes of those source sets > are also included in the .apk installed on the device or emulator, or it will throw ClassNotFoundErrors. +> > The easiest way to achieve this is to add the following code snippet to your gradle file: > ```kotlin > val includeScreenshotTests = project.hasProperty("includeSourceSetScreenshotTest") @@ -241,16 +259,22 @@ testImplementation("androidx.compose.ui:ui-tooling-preview:") > ``` > And pass that gradle property when executing the screenshot tests via command-line, e.g.: > `./gradlew :tests:screenshotRecord -PincludeSourceSetScreenshotTest` -> +> > This is NOT necessary for JVM-based screenshot testing libraries like Roborazzi and Paparazzi +
+ ### Scanning Source Options (packages, files, inputStreams) +
+Click to see scanning source options + Apart from `scanPackageTrees(include:List, exclude:List)`, there are 2 more options to scan previews: 1. All Packages: `scanAllPackages()`. This might require a huge amount of memory since it would scan not only in a set of packages, but in all packages used in your app/module (i.e. also in its transitive dependencies). This is in 99% of the cases unnecessary, and scanning the main package trees of your module should be sufficient. -2. From a file containing the ScanResult. This speeds up your screenshot tests, since it avoids the time-consuming process of scanning each time by reusing previously scanned data:
- 2.1. `scanFile(jsonFile: File)`. Use this for JVM-based screenshot testing libraries (i.e. Roborazzi & Paparazzi).
- 2.2. `scanFile(targetInputStream: InputStream, customPreviewsInfoInputStream: InputStream)`. This is meant for Instrumentation-based screenshot testing libraries.

+2. From a file containing the ScanResult. This speeds up your screenshot tests, since it avoids the time-consuming process of scanning each time by reusing previously scanned data:
+ 2.1. `scanFile(jsonFile: File)`. Use this for JVM-based screenshot testing libraries (i.e. Roborazzi & Paparazzi).
+ 2.2. `scanFile(targetInputStream: InputStream, customPreviewsInfoInputStream: InputStream)`. This is meant for Instrumentation-based screenshot testing libraries.

You can create a unit test for that: + ```kotlin class SaveScanResultInFiles { @Test @@ -270,6 +294,7 @@ class SaveScanResultInFiles { } } ``` +
## JVM Screenshot Tests @@ -280,6 +305,9 @@ You can find [executable examples here](https://github.com/sergio-sastre/Android > You can also find a paparazzi-plugin in this repo that generates all this boilerplate code for you! > Take a look at [its README.md](paparazzi-plugin/README.md) +
+Click to see Paparazzi implementation details + Let's say we want to enable some custom Paparazzi config for some Previews, for instance a maxPercentDifference value 1. Define your own annotation for the Lib config. @@ -296,7 +324,9 @@ fun MyComposable(){ // Composable code here } ``` -3. Create custom record and verify `SnapshotHandler`s for better control over the screenshot file names.
By default, Paparazzi prefixes all generated screenshot files using its internal `SnapshotHandler`. While this works for most cases, it causes issues in parameterized tests: the default `SnapshotHandler` includes the test [index] in the filename. If the order of your previews changes, filenames no longer match, which can break snapshot verification.

+3. Create custom record and verify `SnapshotHandler`s for better control over the screenshot file names. + +By default, Paparazzi prefixes all generated screenshot files using its internal `SnapshotHandler`. While this works for most cases, it causes issues in parameterized tests: the default `SnapshotHandler` includes the test [index] in the filename. If the order of your previews changes, filenames no longer match, which can break snapshot verification.

To solve this, we can create custom `SnapshotHandler`s that use a fixed prefix, like "Paparazzi_Preview_Test", instead of a test-index-dependent name. This ensures filenames remain stable regardless of test order. ```kotlin // Define the prefix = __ @@ -358,11 +388,11 @@ private class PreviewHtmlReportWriter: SnapshotHandler { snapshotHandler.close() } } -``` In the next step, we’ll show how to pass these custom SnapshotHandlers to the Paparazzi TestRule to take full control of screenshot filenames. 4. Map the PreviewInfo and PaparazziConfig values. + ```kotlin class Dimensions( val screenWidthInPx: Int, @@ -498,9 +528,9 @@ fun PreviewBackground( } } } -``` 5. Create the corresponding Parameterized Test: + ```kotlin @RunWith(Parameterized::class) class PreviewTestParameterTests( @@ -559,10 +589,14 @@ class PreviewTestParameterTests( ``` 6. Run these Paparazzi tests together with the existing ones by executing the corresponding command e.g. `./gradlew yourModule:recordPaparazziDebug` +
### Roborazzi You can find [executable examples here](https://github.com/sergio-sastre/Android-screenshot-testing-playground/tree/master/lazycolumnscreen-previews/roborazzi/src) +
+Click to see Roborazzi implementation details + Let's say we want to enable some custom Roborazzi Config for some Previews, for instance a maxPercentDifferent value 1. Define your own annotation for the Lib Config. @@ -580,7 +614,8 @@ fun MyComposable(){ } ``` -3. Map the PreviewInfo and RoborazziConfig values. For instance, you can use a custom class for that. +3. Map the PreviewInfo and RoborazziConfig values. + ```kotlin object RoborazziOptionsMapper { fun createFor(preview: ComposablePreview): RoborazziOptions = @@ -614,6 +649,7 @@ object RoborazziComposeOptionsMapper { Check the following link for a full list of [Robolectric device qualifiers](https://robolectric.org/device-configuration/) and this blog post on how to [set the cumulative Qualifiers dynamically](https://sergiosastre.hashnode.dev/efficient-testing-with-robolectric-roborazzi-across-many-ui-states-devices-and-configurations) 4. Create the corresponding Parameterized Test: + ```kotlin @RunWith(ParameterizedRobolectricTestRunner::class) class PreviewParameterizedTests( @@ -655,17 +691,22 @@ class PreviewParameterizedTests( ``` 5. Run these Roborazzi tests together with the existing ones by executing the corresponding command e.g. `./gradlew yourModule:recordRoborazziDebug` +
## Instrumentation Screenshot Tests You can find executable examples that use ComposablePreviewScanner with the different instrumentation-based libraries in the corresponding links below: - [Dropshots](https://github.com/sergio-sastre/Android-screenshot-testing-playground/tree/master/recyclerviewscreen-previews/dropshots) - [Shot](https://github.com/sergio-sastre/Android-screenshot-testing-playground/tree/master/recyclerviewscreen-previews/shot) -- [Android-Testify](https://github.com/sergio-sastre/Android-screenshot-testing-playground/tree/master/recyclerviewscreen-previews/android-testify)
+- [Android-Testify](https://github.com/sergio-sastre/Android-screenshot-testing-playground/tree/master/recyclerviewscreen-previews/android-testify)
Android does not use the standard Java bytecode format and does not actually even have a runtime classpath. Moreover, the "build" folders, where the compiled classes are located, are not accessible from instrumentation tests. Therefore, the current way to support instrumentation tests, is by previously dumping the relevant classes into a file and moving it into a folder that can be accessed while running instrumentation tests. +
+Click to see Instrumentation implementation details + 1. run the scan in a unit test & save it in a file accessible by instrumentation tests e.g. in assets + ```kotlin class SaveScanResultInAssets { @Test @@ -685,6 +726,7 @@ class SaveScanResultInAssets { } } ``` + Ensure that the .json with the scan result is up-to-date before executing the instrumentation screenshot tests. For instance, execute that test always before your instrumentation screenshot tests. Ideally, this scanning could be done via a Gradle Plugin in the future instead of by running it in a unit test. @@ -704,12 +746,13 @@ Let's say we want to enable some custom Dropshots Config for some Previews, for } ``` - Map the PreviewInfo and DropshotsConfig values. For instance, you can use a custom class for that. To map the Preview Info values, I recommend to use the ActivityScenarioForComposableRule of [AndroidUiTestingUtils](https://github.com/sergio-sastre/AndroidUiTestingUtils) + ```kotlin object DropshotsPreviewRule { fun createFor(preview: ComposablePreview): Dropshots = preview.getAnnotation()?.let { config -> Dropshots( - resultValidator = ThresholdValidator(config.comparisonThreshold)) + resultValidator = ThresholdValidator(config.comparisonThreshold) ) } ?: Dropshots() } @@ -743,7 +786,9 @@ Let's say we want to enable some custom Dropshots Config for some Previews, for } } ``` -- Create the corresponding Parameterized Test: + +3. Create the corresponding Parameterized Test: + ```kotlin @RunWith(ParameterizedTestRunner::class) class PreviewParameterizedTests( @@ -784,12 +829,14 @@ Let's say we want to enable some custom Dropshots Config for some Previews, for } } ``` + - Run these Dropshots tests together with the existing ones by executing the corresponding command e.g. `./gradlew yourModule:connectedAndroidTest -Pdropshots.record` > [!WARNING] > Beware that Locale Strings in Preview Infos, unlike AndroidUiTestingUtils, use The BCP-47 tag but with + instead of - as separators, and have the prefix b+. Therefore, the BCP-47 tag "zh-Hans-CN" would be written as "b+zh+Hans+CN" instead. -> So for this case, you'd have to convert locale "b+zh+Hans+CN" to "zh-Hans-CN" in order to use it with AndroidUiTestingUtils, for instance as showcased above:
+> So for this case, you'd have to convert locale "b+zh+Hans+CN" to "zh-Hans-CN" in order to use it with AndroidUiTestingUtils, for instance as showcased above:
> `val locale = preview.previewInfo.locale.removePrefix("b+").replace("+", "-").ifBlank { "en" }` +
## Advanced Usage ### Screenshot File Names @@ -798,6 +845,9 @@ These are `AndroidPreviewScreenshotIdBuilder`, `GlancePreviewScreenshotIdBuilder By default, these classes do not include the Preview Info in the screenshot file name if it is the same as its default value, but it can be configured to behave differently. That means, for @Preview(showBackground = false), showBackground would not be included in the screenshot file name since it is the default. +
+Click to see screenshot naming configuration example + ```kotlin AndroidPreviewScreenshotIdBuilder(preview) .ignoreClassName() @@ -844,11 +894,15 @@ class MyClass { } ``` createScreenshotIdFor(preview) will generate the following id: `"MyClass.MyComposable.FONT_1_5f_WITHOUT_BACKGROUND"` +
### Parsing Preview Device String (Android Previews) Since 0.4.0, ComposablePreviewScanner also provides `DevicePreviewInfoParser.parse(device: String)` which returns a `Device` object containing all the necessary information to support different devices in your Roborazzi & Paparazzi screenshot tests! +
+Click to see device string parsing examples + It can parse ALL possible combinations of "device strings" up to Android Studio Quail, namely: ```kotlin // The over 80 devices supported either by id and/or name, for instance: @@ -862,12 +916,13 @@ It can parse ALL possible combinations of "device strings" up to Android Studio @Preview(device = "spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160") // in dp ``` For further info on how to use them, see [Roborazzi](#roborazzi) and [Paparazzi](#paparazzi) sections respectively. +
## How it works This library is written on top of [ClassGraph](https://github.com/classgraph/classgraph), an uber-fast parallelized classpath scanner. ClassGraph can scan everything that is available either at bytecode level or at runtime. -This is also the case of annotations without retention or with either `AnnotationRetention.BINARY` or `AnnotationRetention.RUNTIME`, like Android Composable Previews +This is also the case of annotations without retention or with either `AnnotationRetention.BINARY` or `AnnotationRetention.RUNTIME`, like Android, Glance and Wear Composable Previews ```kotlin package androidx.compose.ui.tooling.preview @@ -876,94 +931,69 @@ annotation class Preview( // Preview code here ... ) ``` - -However, those with `AnnotationRetention.SOURCE` are not visible to Classgraph. Such annotations are mainly used for IDE tooling, and that is the case for the Compose-Desktop Preview annotation. -```kotlin -package androidx.compose.desktop.ui.tooling.preview - -@Retention(AnnotationRetention.SOURCE) -annotation class Preview -``` - +__ ## Glance Previews Support You can find executable examples in this repo with different screenshot libraries: - [Roborazzi](tests/src/test/java/sergio/sastre/composable/preview/scanner/tests/roborazzi/RoborazziGlanceComposablePreviewInvokeTests.kt) - [Paparazzi](tests/src/test/java/sergio/sastre/composable/preview/scanner/tests/paparazzi/PaparazziGlanceComposablePreviewInvokeTests.kt) - [Android-Testify](tests/src/androidTest/java/sergio/sastre/composable/preview/scanner/screenshots/AndroidTestifyGlanceComposablePreviewScannerInstrumentationTest.kt) (Check the [instrumentation-screenshot-tests](#instrumentation-screenshot-tests) section before) -To write such screenshot tests you have to: +
+Click to see Glance implementation details +To write such screenshot tests you have to: 1. Add `:glance` dependency for ComposablePreviewScanner e.g. `io.github.sergio-sastre.ComposablePreviewScanner:glance:`. This contains some utils to correctly set the size of the Composable as well as the size of the device. Take a look at the executable examples above to see how they are used. 2. Ensure `targetSdk` is set to any value in the gradle file1. Otherwise you can see some discrepancies between the Preview and the generated screenshot file for Glance `@Preview`s without `widthDp`. 3. Write the Parameterized screenshot test like in the examples above. 1 Unfortunately, Paparazzi is not able to always render screenshots accurately for Glance `@Preview`s without `widthDp`. +
-## Compose Multiplatform Support -Starting with Compose Multiplatform 1.10.0-beta02, Common and Desktop @Preview annotations are deprecated. Instead, Android `@Preview` can now be used across `common` and `desktop` platforms. -ComposablePreviewScanner 0.8.0+ fully supports this modern setup. You can use the `AndroidComposablePreviewScanner` to scan for `@Preview` annotations across all relevant source sets, including commonMain.
-For example, to scan previews located in both a platform-specific (androidMain or desktopMain) and a shared (commonMain) source set, you would configure the scanner like this: - -```kotlin -AndroidComposablePreviewScanner() - .scanPackageTrees( - "package.tree.android.or.desktop", - "package.tree.common" - ) -``` - -> [!NOTE] -> Screenshot tests must run on a platform supported by your screenshot library.
-> • Android: Paparazzi and Roborazzi are supported.
-> • Desktop: Roborazzi only.
-> • Common: no library runs directly in common; run your tests from an Android or Desktop target instead. ComposablePreviewScanner can still find the Previews in common target packages.
- -You can find executable examples with Roborazzi here: -- [Android @Previews in common](https://github.com/sergio-sastre/roborazzi/blob/droidcon/preview_tests/sample-generate-preview-common/src/androidUnitTest/kotlin/com/github/takahirom/preview/tests/AndroidPreviewTest.kt) -- [Android @Previews in desktop](https://github.com/sergio-sastre/roborazzi/blob/droidcon/preview_tests/sample-generate-preview-desktop/src/desktopTest/kotlin/AndroidPreviewTest.kt) - -If you are still using the deprecated Common or Desktop `@Preview` annotations, see [README_DEPRECATED.md](README_DEPRECATED.md) for guidance. +# Conferences & Community +
+Click to see talks, blog posts, and books -# Resources ## Tech talks -In these tech-talks have also been mentioned the benefits of using ComposablePreviewScanner: -- DroidKaigi 2024 [in JA 🇯🇵 with EN 🇬🇧 slides]:
- [Understand the mechanism! Let's do screenshots testing of Compose Previews with various variations](https://www.youtube.com/watch?app=desktop&v=c4AxUXTQgw4) by [Sumio Toyama](https://x.com/sumio_tym)
-- Droidcon Lisbon 2024:
- [Composable Preview Driven Development: TDD-fying your UI with ease!](https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://www.youtube.com/watch%3Fv%3DcDqdosrS83k&ved=2ahUKEwjprPGKqaiPAxWxSvEDHdSRBKkQwqsBegQIFRAG&usg=AOvVaw2ZfX6fYQbNI4Op6KN0d5i5) by Sergio Sastre
-- [“Fast Feedback loops & Composable Preview Scanner”](https://www.youtube.com/watch?v=SphQelcGdHk) with the Skool Android Community by Sergio Sastre
-- Droidcon Lisbon & Berlin 2025:
+The benefits and usage of ComposablePreviewScanner have been featured at major Android conferences: +- **Droidcon Lisbon & Berlin 2025**:
[Let's @Preview the future: Automating Screenshot Testing in Compose Multiplatform](https://www.youtube.com/watch?v=zYsNXrf2-Lo) by Sergio Sastre +- **Droidcon Lisbon 2024**:
+ [Composable Preview Driven Development: TDD-fying your UI with ease!](https://www.youtube.com/watch?v=cDqdosrS83k) by Sergio Sastre +- **DroidKaigi 2024** [JA 🇯🇵 / EN 🇬🇧 slides]:
+ [Understand the mechanism! Let's do screenshots testing of Compose Previews with various variations](https://www.youtube.com/watch?app=desktop&v=c4AxUXTQgw4) by [Sumio Toyama](https://x.com/sumio_tym) +- **Skool Android Community**:
+ [“Fast Feedback loops & Composable Preview Scanner”](https://www.youtube.com/watch?v=SphQelcGdHk) by Sergio Sastre ## Blog posts - [Automating screens verification with Roborazzi and GitHub Actions](https://medium.com/@matiasdelbel/automating-screens-verification-with-roborazzi-and-github-actions-473b3301a5c0) by Matías del Bel - [Implementing Screenshot Testing in the Unlimited Android App Was Tougher Than Expected](https://blog.kinto-technologies.com/posts/2024-12-13-Introducing-Screenshot-Testing-in-UnlimitedApp-en/) by KINTO Technologies -# Testing -The core of ComposablePreviewScanner has been (and it's being) developed using Test-Driven Development (TDD).
-I strongly believe this approach is one of the key reasons the library has very few known bugs although it's widely used with over 150k monthly downloads. +## Books +- [Mastering Android Screenshot Testing](https://alexzh.com/books/mastering-android-screenshot-testing/) by Alex Zhukovich +
-However, some tests have specific preconditions and may be skipped if those aren't met.
-For example, when running tests to retrieve @Previews from a SourceSet other than main, such as screenshotTest or androidTest, -the corresponding compiled classes must be generated first via the corresponding Gradle task.
+# Engineering Quality +The core of ComposablePreviewScanner is developed using **Test-Driven Development (TDD)** to ensure maximum stability and reliability across the diverse Compose ecosystem. This rigorous approach is a primary reason for its high adoption rate and minimal bug reports despite handling complex bytecode scanning. -Moreover, Paparazzi & Roborazzi tests also play a key role: -1. Each of these libraries uses a different mechanism to download Android resources for running tests. ComposablePreviewScanner also loads certain classes by using ClassLoaders, and for those classes to be available it is necessary that Paparazzi and Roborazzi already downloaded them to [avoid issues like this one](https://github.com/sergio-sastre/ComposablePreviewScanner/issues/27). These tests help catch and avoid such errors. -2. They help avoid errors in @Composable invocations. Since they can only occur within the context of a @Composable function and standard unit tests cannot access Android resources (e.g. Composable framework), it is hard to verify their correctness without UI tests. +To maintain strict binary compatibility for library consumers, the project uses **Metalava** to track and enforce API standards. -To streamline this process and support my TDD workflow, I’ve created custom Gradle tasks that handle these prerequisites automatically, -saving time and reducing friction during development.
-They can also help you in case you fork this library and make some code adjustments, to ensure everything still works as expected.
+## Automated Verification +To ensure continuous quality, the following custom Gradle tasks handle environment prerequisites and integration testing. +
+Click to see the list of custom Gradle tasks -These custom gradle tasks are the following:
+These tasks are executed locally during development and automatically in our **CI pipeline** for every PR: 1. API logic tests:`./gradlew :tests:testApi` 2. SourceSet logic tests: `./gradlew :tests:testSourceSets` 3. Paparazzi integration tests: `./gradlew :tests:paparazziPreviews` and `./gradlew :tests:paparazziPreviews -Pverify=true` 4. Roborazzi integration tests: `./gradlew :tests:roborazziPreviews` and `./gradlew :tests:roborazziPreviews -Pverify=true` Custom gradle tasks for Android-testify integration tests (i.e. instrumentation screenshot testing libraries) coming soon +
# Troubleshooting +
+Click to see solution for common issues ## Slow JVM Screenshot tests JVM Screenshot tests can consume significant memory, and ComposablePreviewScanner may require even more RAM when scanning large sets of subpackages. @@ -979,9 +1009,8 @@ testOptions.unitTests { This adjustment helps reduce test execution time during large scans. ## java.io.FileNotFoundException (File name too long) - -`java.io.FileNotFoundException: ... (File name too long)`
-This is more likely to happen when using Paparazzi. By default, Paparazzi additionally prefixes the screenshot file named internally instead of just using the `name` we pass to its `snapshot()` method, and this results sometimes in the final screenshot file name being longer than allowed.

+`java.io.FileNotFoundException: ... (File name too long)`
+This is more likely to happen when using Paparazzi. By default, Paparazzi additionally prefixes the screenshot file named internally instead of just using the `name` we pass to its `snapshot()` method, and this results sometimes in the final screenshot file name being longer than allowed.

That is why it is recommended to [set a custom SnapshotHandler](#paparazzi) in the Paparazzi Test Rule. But if you're still experiencing such issues, consider: @@ -989,10 +1018,9 @@ But if you're still experiencing such issues, consider: 2. Avoid `AndroidPreviewScreenshotIdBuilder` and use `paparazzi.snapshot {}` instead of `paparazzi.snapshot(name = screenshotId)` ## java.lang.IllegalArgumentException: Generated method name contains invalid characters - Some libraries restrict the characters allowed in filenames and may alter the provided screenshot name (e.g., Paparazzi 1.3.5+ like reported in this issue [here](https://github.com/cashapp/paparazzi/issues/1963)). -This is especially problematic when the `TestParameterInjector` test runner is used.
-To avoid issues, `ComposablePreviewScanner`s ScreenshotIdBuilders should be used with the standard JUnit4 `Parameterized` test runner, and invalid characters should be encoded if needed:
+This is especially problematic when the `TestParameterInjector` test runner is used.
+To avoid issues, `ComposablePreviewScanner`s ScreenshotIdBuilders should be used with the standard JUnit4 `Parameterized` test runner, and invalid characters should be encoded if needed:
ComposablePreviewScanner 0.8.0+ ```kotlin @@ -1017,7 +1045,6 @@ AndroidPreviewScreenshotIdBuilder(preview) > Use the JUnit4 `Parameterized` test runner for valid results. ## Cannot inline bytecode built with JVM target 17 - ```text Task compileDebugUnitTestKotlin FAILED e: file:... Cannot inline bytecode built with JVM target 17 into bytecode that is being built with JVM target 11. Specify proper '-jvm-target' option. @@ -1033,7 +1060,6 @@ kotlin { ``` ## GooglePlayServicesMissingManifestValueException - ```text com.google.android.gms.common.GooglePlayServicesMissingManifestValueException: A required meta-data tag in your app's AndroidManifest.xml does not exist. You must have the following declaration within the element: ``` @@ -1049,6 +1075,18 @@ internal fun MapScreenPreview() { } } ``` +
+ +# Roadmap +- [ ] **Support for Wear Previews** +- [ ] **KSP support**: This facilitates running Screenshot tests for Previews in `common` on iOS with Roborazzi. +- [ ] **Better integration in Gradle tasks**: This helps provide better support for instrumentation testing libraries. +- [ ] **Speed and memory consumption improvements** + +# Governance & Contributing +Contributions are welcome! Please refer to the [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on how to submit pull requests and report issues. + +As mentioned in the [Engineering Quality](#engineering-quality) section, this project uses **Metalava** to ensure strict binary compatibility for all library consumers. -

+

Composable Preview Scanner logo modified from one by Freepik - Flaticon diff --git a/README_DEPRECATED.md b/README_DEPRECATED.md index d52da6d0..bd144cd8 100644 --- a/README_DEPRECATED.md +++ b/README_DEPRECATED.md @@ -1,10 +1,12 @@ # Deprecated Modules +![composable_preview_scanner_overview_deprecated.png](composable_preview_scanner_overview_deprecated.png) > [!WARNING] > The `:common` and `:jvm` modules are deprecated and will be removed in version 0.10.0. > Starting with Compose Multiplatform 1.10.0-beta02, Common and Desktop `@Preview` annotations are deprecated in favour of the Android `@Preview` annotation (`androidx.compose.ui.tooling.preview.Preview`), which can be used across `common` and `desktop` platforms as well. > After migrating to these Android `@Preview`s, please migrate to using `AndroidComposablePreviewScanner` as described in the main [README.md](README.md). + ### Common Previews (Deprecated) You can find executable examples here: - [Roborazzi](https://github.com/sergio-sastre/roborazzi/blob/droidcon/preview_tests/sample-generate-preview-common/src/androidUnitTest/kotlin/com/github/takahirom/preview/tests/CommonPreviewTest.kt) @@ -28,20 +30,31 @@ Assuming that you have: Here is how you could also run screenshot tests for those Compose Multiplatform `@Previews` together, for instance, with Roborazzi (would also work with Paparazzi or any Instrumentation-based library). -1. Add `:common` dependency for ComposablePreviewScanner e.g. `io.github.sergio-sastre.ComposablePreviewScanner:common:`. +1. Add `:common` dependency for ComposablePreviewScanner: +```kotlin +// Maven Central +testImplementation("io.github.sergio-sastre.ComposablePreviewScanner:common:") + +// JitPack +testImplementation("com.github.sergio-sastre.ComposablePreviewScanner:common:") +``` 2. Add an additional Parameterized screenshot test for these Compose Multiplatform `@Previews`. This is basically the same as in the corresponding [Paparazzi](README.md#paparazzi), [Roborazzi](README.md#roborazzi), or [Instrumentation screenshot tests](README.md#instrumentation-screenshot-tests) sections, but use `CommonComposablePreviewScanner` and `CommonPreviewScreenshotIdBuilder`. 3. Run these screenshot tests by executing the corresponding command e.g. for android: `./gradlew yourModule:recordRoborazziDebug` ### Desktop Previews (Deprecated) You can find [a video on how to set it with Roborazzi here](https://www.youtube.com/watch?v=zYsNXrf2-Lo&t=23m52s), and the [repo used in the video here](https://github.com/sergio-sastre/roborazzi/tree/droidcon/preview_tests). -As we've seen in the previous section [How it works](README.md#how-it-works), Compose-Desktop previews are still not visible to ClassGraph since they use `AnnotationRetention.SOURCE`. -There is [already an open issue](https://youtrack.jetbrains.com/issue/CMP-5675) to change it to `AnnotationRetention.BINARY`, which would allow ClassGraph to find them. +Compose-Desktop previews have `AnnotationRetention.SOURCE` and therefore it is not visible to ClassGraph. +However, it is also possible to workaround this limitation with ComposablePreviewScanner as follows. -In the meanwhile, it is also possible to workaround this limitation with ComposablePreviewScanner as follows. +1. Add `:jvm` dependency from ComposablePreviewScanner 0.2.0+ and use Roborazzi: +```kotlin +// Maven Central +testImplementation("io.github.sergio-sastre.ComposablePreviewScanner:jvm:") -1. Add `:jvm` dependency from ComposablePreviewScanner 0.2.0+ and use Roborazzi, since it is the only Screenshot Testing Library that supports Compose-Desktop - `testImplementation("io.github.sergio-sastre.ComposablePreviewScanner:jvm:")` +// JitPack +testImplementation("com.github.sergio-sastre.ComposablePreviewScanner:jvm:") +``` 2. Configure Roborazzi as described [in the corresponding "Multiplatform support" section](https://github.com/takahirom/roborazzi?tab=readme-ov-file#multiplatform-support) diff --git a/composable_preview_scanner_new_logo.png b/composable_preview_scanner_new_logo.png new file mode 100644 index 00000000..2f35eba9 Binary files /dev/null and b/composable_preview_scanner_new_logo.png differ diff --git a/composable_preview_scanner_overview.png b/composable_preview_scanner_overview.png index da2f918f..ed56f0ba 100644 Binary files a/composable_preview_scanner_overview.png and b/composable_preview_scanner_overview.png differ diff --git a/composable_preview_scanner_overview_deprecated.png b/composable_preview_scanner_overview_deprecated.png new file mode 100644 index 00000000..da2f918f Binary files /dev/null and b/composable_preview_scanner_overview_deprecated.png differ