Skip to content

Switch from Roborazzi to Paparazzi for snapshot testing#767

Open
runningcode wants to merge 8 commits intomainfrom
no/switch-roborazzi-to-paparazzi
Open

Switch from Roborazzi to Paparazzi for snapshot testing#767
runningcode wants to merge 8 commits intomainfrom
no/switch-roborazzi-to-paparazzi

Conversation

@runningcode
Copy link
Contributor

@runningcode runningcode commented Mar 18, 2026

Replace Roborazzi plugin with Paparazzi for snapshot generation and wire it to sentry.

This PR also updates to JDK 21 and Gradle 9.1 because paparazzi requires them.

🤖 Generated with Claude Code

)
afterEvaluate {
tasks.named<SentryUploadSnapshotsTask>("sentryUploadSnapshotsRelease") {
dependsOn("recordPaparazziDebug")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The sentryUploadSnapshotsRelease task incorrectly depends on recordPaparazziDebug, causing a build variant mismatch that could mask release-specific visual bugs.
Severity: MEDIUM

Suggested Fix

Change the dependency for the sentryUploadSnapshotsRelease task from tasks.named("recordPaparazziDebug") to tasks.named("recordPaparazziRelease") to ensure snapshots from the correct build variant are generated and uploaded.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: android/app/build.gradle.kts#L140

Potential issue: The Gradle configuration sets up the `sentryUploadSnapshotsRelease`
task to depend on `recordPaparazziDebug`. This creates a mismatch between build
variants, where the task for uploading release snapshots is triggered by the generation
of debug snapshots. Since debug and release builds can have different configurations
(e.g., minification, application ID suffix), the snapshots generated by the debug task
may not accurately represent the visual appearance of the release app. This could lead
to visual regressions specific to the release build being missed during automated
testing, as debug-generated snapshots are uploaded to Sentry's release channel.

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +177 to +180
// testImplementation(libs.robolectric)
// testImplementation(libs.roborazzi)
// testImplementation(libs.roborazzi.compose)
// testImplementation(libs.roborazzi.rule)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Roborazzi dependencies are commented out, but tests still use its API, which will cause compilation failure and break the snapshot generation pipeline.
Severity: CRITICAL

Suggested Fix

Migrate the existing snapshot tests from the Roborazzi API to the Paparazzi API. This includes replacing calls like captureRoboImage() with paparazzi.snapshot() and updating the test class setup to use the Paparazzi JUnit rule instead of the Robolectric test runner.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: android/app/build.gradle.kts#L177-L180

Potential issue: The build configuration was updated to use Paparazzi for snapshot
testing, and the corresponding Roborazzi dependencies were commented out in
`android/app/build.gradle.kts`. However, test files like `BookmarksScreenComposeTest.kt`
and `StoryRowComposeTest.kt` were not migrated and still use the Roborazzi API, such as
`captureRoboImage()`. This mismatch will cause the test suite to fail compilation
because the necessary dependencies are missing. As a result, the `recordPaparazziDebug`
Gradle task will not generate any snapshots, breaking the snapshot testing and upload
pipeline.

@runningcode runningcode force-pushed the no/switch-roborazzi-to-paparazzi branch from 5305a6a to 18adf8b Compare March 19, 2026 07:46
Comment on lines +139 to +141
snapshotsPath.fileProvider(testTask.map { task ->
task.outputs.files.files.first { it.name == "snapshots" }
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The sentryUploadSnapshotsRelease task will fail because it tries to find a "snapshots" directory from test outputs that no longer exist, causing the CI build to crash.
Severity: CRITICAL

Suggested Fix

Since the Paparazzi tests have been removed, the sentryUploadSnapshotsRelease task configuration in android/app/build.gradle.kts and its corresponding invocation in the .github/workflows/android_emerge_snapshots.yml CI workflow should be deleted to prevent build failures.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: android/app/build.gradle.kts#L139-L141

Potential issue: The `sentryUploadSnapshotsRelease` Gradle task is configured to locate
a "snapshots" directory within the outputs of the `testDebugUnitTest` task by calling
`.first { it.name == "snapshots" }`. However, the Paparazzi tests responsible for
generating these snapshots have been removed in this pull request. As a result, no
"snapshots" directory will be found in the task's outputs. This will cause the
`.first()` call to throw a `NoSuchElementException`, leading to a build failure whenever
the CI workflow invokes this task.

Comment on lines +139 to +141
snapshotsPath.fileProvider(testTask.map { task ->
task.outputs.files.files.first { it.name == "snapshots" }
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The sentryUploadSnapshotsRelease task will fail because it incorrectly searches for the "snapshots" directory in the testDebugUnitTest task's outputs, where it doesn't exist.
Severity: CRITICAL

Suggested Fix

Instead of searching the testDebugUnitTest task's outputs, the snapshotsPath should be configured to point directly to the source-controlled snapshot directory. Use project.file("src/test/snapshots") to correctly reference the path.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: android/app/build.gradle.kts#L139-L141

Potential issue: The `sentryUploadSnapshotsRelease` task is configured to find the
Paparazzi snapshots path by searching the outputs of the `testDebugUnitTest` task for a
directory named `"snapshots"`. However, Paparazzi snapshots are stored in the source
directory `src/test/snapshots/`, which is not registered as an output of the test task.
Consequently, the `.first { it.name == "snapshots" }` call will find no matching file
and throw a `NoSuchElementException` at runtime. This will cause the Gradle build to
fail, specifically breaking CI/CD workflows that depend on this task.

@emerge-tools
Copy link

emerge-tools bot commented Mar 19, 2026

🛰️ Build Distribution

Build available for installation

App Name App ID Platform Version Tag Install Page
Hacker News com.emergetools.hackernews android 1.0.6 release 🔗 Install Build

🛸 Powered by Emerge Tools

@sentry
Copy link

sentry bot commented Mar 19, 2026

Sentry Build Distribution

App Name App ID Version Configuration Install Page
Hacker News com.emergetools.hackernews 1.0.6 (17) release Install Build

Comment on lines +65 to +66
- name: Generate Paparazzi snapshots
run: ./gradlew :app:recordPaparazziDebug :app:sentryUploadSnapshotsRelease
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The CI workflow runs recordPaparazziDebug, but all snapshot tests have been removed. This results in zero snapshots being generated, effectively disabling snapshot testing.
Severity: HIGH

Suggested Fix

Re-implement the deleted snapshot tests using the Paparazzi framework. Create new test files with @Test annotated methods that use the Paparazzi rule to capture snapshots of the relevant composables. This will ensure the recordPaparazziDebug task generates the expected snapshots.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: .github/workflows/android_emerge_snapshots.yml#L65-L66

Potential issue: The pull request migrates the snapshot testing framework from Roborazzi
to Paparazzi. However, while the CI workflow in
`.github/workflows/android_emerge_snapshots.yml` is updated to run the
`recordPaparazziDebug` task, all existing snapshot test files were removed without any
Paparazzi-compatible replacements being added. As a result, the `recordPaparazziDebug`
task will execute successfully but will generate zero snapshots because there are no
`@Test` methods to drive the process. This silently disables the entire snapshot testing
pipeline, defeating its purpose.

@emerge-tools
Copy link

emerge-tools bot commented Mar 19, 2026

📸 Snapshot Test

Base build not found

No build was found for the base commit 70e95cf. This is required to generate a snapshot diff for your pull request.

It's possible that you created a branch off the base commit before all of the CI steps have finished processing, e.g. the one that uploads a build to our system. If that's the case, no problem! Just wait and this will eventually resolve.


🛸 Powered by Emerge Tools

uses: actions/setup-java@v5
with:
java-version: "17"
java-version: "21"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

paparazzi 2.0.0-alpha4 requires jdk 21.

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

paparazzi requires Gradle 9.1+

runningcode and others added 8 commits March 20, 2026 10:49
Replace Roborazzi with Paparazzi for snapshot generation and wire the
sentryUploadSnapshotsRelease task to consume Paparazzi's output
directory.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of hardcoding the snapshot directory path, use the output
directory registered by Paparazzi on testDebugUnitTest.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of fragile name matching on "snapshots", use the registered
property name "paparazzi.snapshots.output.dir" to find the output dir.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Gradle public API doesn't expose task output properties by name.
The withPropertyName API is only for up-to-date checking, not lookup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@runningcode runningcode force-pushed the no/switch-roborazzi-to-paparazzi branch from 7057c21 to 57e8120 Compare March 20, 2026 09:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant