P8: Roborazzi golden image pipeline live

Records initial golden set + wires verifyRoborazziDebug into CI. Diffs
uploaded as artifact on failure. ScreenshotTests @Ignore removed.

Root cause of the prior RoboMonitoringInstrumentation:102 failure:
createComposeRule() launches ActivityScenarioRule<ComponentActivity>
which fires a MAIN/LAUNCHER intent, but the merged unit-test manifest
declares androidx.activity.ComponentActivity without a LAUNCHER filter,
so Robolectric's PM returns "Unable to resolve activity for Intent".
Fix: switch to the standalone captureRoboImage(path) { composable }
helper from roborazzi-compose, which registers
RoborazziTransparentActivity with Robolectric's shadow PackageManager
at runtime and bypasses ActivityScenario entirely.

Also pin roborazzi outputDir to src/androidUnitTest/roborazzi so
goldens live in git (not build/) and survive gradle clean.

36 goldens, 540KB total.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-04-18 17:57:35 -05:00
parent 77f32befb8
commit 0c554cce6a
40 changed files with 56 additions and 36 deletions

View File

@@ -1,7 +1,6 @@
@file:OptIn(androidx.compose.material3.ExperimentalMaterial3Api::class)
package com.tt.honeyDue.screenshot
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -30,14 +29,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
import com.github.takahirom.roborazzi.RoborazziRule
import com.github.takahirom.roborazzi.RoborazziOptions
import com.github.takahirom.roborazzi.captureRoboImage
import com.tt.honeyDue.ui.theme.AppThemes
import com.tt.honeyDue.ui.theme.HoneyDueTheme
import com.tt.honeyDue.ui.theme.ThemeColors
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@@ -45,7 +41,7 @@ import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
/**
* Roborazzi-driven screenshot regression tests (P8 scaffolding).
* Roborazzi-driven screenshot regression tests (P8).
*
* Runs entirely on the Robolectric unit-test classpath — no emulator
* required. The goal is to catch accidental UI drift (colour, spacing,
@@ -56,40 +52,27 @@ import org.robolectric.annotation.GraphicsMode
* (light / dark) = 36 images. This is a conservative baseline; the full
* 11-theme matrix would produce 132+ images and is deferred.
*
* Implementation notes:
* - We use the top-level `captureRoboImage(path) { composable }` form
* from roborazzi-compose. That helper registers
* `RoborazziTransparentActivity` at runtime via Robolectric's shadow
* PackageManager, so we don't need `createComposeRule()` /
* `ActivityScenarioRule<ComponentActivity>` and therefore avoid the
* "Unable to resolve activity for Intent MAIN/LAUNCHER cmp=.../ComponentActivity"
* failure that bit the initial scaffolding (RoboMonitoringInstrumentation:102).
* - Goldens land under `composeApp/build/outputs/roborazzi/`, which the
* Roborazzi Gradle plugin picks up for record / verify / compare.
*
* Workflow:
* - Initial record: `./gradlew :composeApp:recordRoborazziDebug`
* - Verify in CI: `./gradlew :composeApp:verifyRoborazziDebug`
* - View diffs: `./gradlew :composeApp:compareRoborazziDebug`
*
* We intentionally build *theme showcase* surfaces locally rather than
* invoking the full production screens (LoginScreen, TasksScreen, etc.)
* because those screens depend on DataManager/network state that can't
* be safely initialized from a Robolectric test. The showcases render
* the same material3 primitives the screens are composed from, so a
* colour/typography regression in Theme.kt will still be caught.
*/
// TEMPORARILY DISABLED: Roborazzi runtime pipeline needs additional setup
// before screenshot tests can run green in CI. Enable via `@Ignore` removal
// once `recordRoborazziDebug` successfully generates the initial golden
// image set and CI is configured to run `verifyRoborazziDebug`.
@org.junit.Ignore("Roborazzi pipeline pending — see docs/screenshot-tests.md")
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(qualifiers = "w360dp-h800dp-mdpi")
class ScreenshotTests {
@get:Rule
val composeRule = createComposeRule()
@get:Rule
val roborazziRule = RoborazziRule(
composeRule = composeRule,
captureRoot = composeRule.onRoot(),
options = RoborazziRule.Options(
outputDirectoryPath = "build/outputs/roborazzi",
),
)
// ---------- Login screen showcase ----------
@Test
@@ -286,12 +269,14 @@ class ScreenshotTests {
darkTheme: Boolean,
content: @Composable () -> Unit,
) {
composeRule.setContent {
captureRoboImage(
filePath = "build/outputs/roborazzi/$name.png",
roborazziOptions = RoborazziOptions(),
) {
HoneyDueTheme(darkTheme = darkTheme, themeColors = theme) {
content()
}
}
composeRule.onRoot().captureRoboImage("build/outputs/roborazzi/$name.png")
}
}