Skip to content

Commit

Permalink
Add screenshot testing with Roborazzi (#70)
Browse files Browse the repository at this point in the history
* Add Roborazzi dependencies

* Upload reports + output as artifacts

* Move Roborazzi handling to common module

* Add transparent tint test

* Add screenshot tests to `haze-jetpack-compose`

* Screenshot test on API 28 and 33
  • Loading branch information
chrisbanes authored Jan 3, 2024
1 parent 29321fc commit 39e39e6
Show file tree
Hide file tree
Showing 31 changed files with 527 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/screenshots/**/*.png filter=lfs diff=lfs merge=lfs -text
11 changes: 11 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
lfs: 'true'

- name: Setup JDK
uses: actions/setup-java@v4
Expand All @@ -34,6 +36,15 @@ jobs:
- name: Build
run: ./gradlew build

- name: Upload reports + Roborazzi outputs
if: failure()
uses: actions/upload-artifact@v4
with:
name: reports
path: |
**/build/reports/**
**/build/outputs/roborazzi/**
deploy:
if: github.ref == 'refs/heads/main'

Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/publish-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
lfs: 'true'

- name: Setup JDK
uses: actions/setup-java@v4
Expand Down
2 changes: 1 addition & 1 deletion .idea/kotlinc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ plugins {
alias(libs.plugins.composeMultiplatform) apply false
alias(libs.plugins.mavenpublish) apply false
alias(libs.plugins.metalava) apply false
alias(libs.plugins.roborazzi) apply false
alias(libs.plugins.dokka)
}
5 changes: 5 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ systemProp.org.gradle.internal.publish.checksums.insecure=true
# Increase timeout when pushing to Sonatype (otherwise we get timeouts)
systemProp.org.gradle.internal.http.socketTimeout=120000

roborazzi.record.filePathStrategy=relativePathFromRoborazziContextOutputDirectory
roborazzi.record.namingStrategy=testClassAndMethod
roborazzi.test.verify=true

##################################
# Publishing
##################################
Expand All @@ -49,3 +53,4 @@ POM_LICENCE_DIST=repo

POM_DEVELOPER_ID=chrisbanes
POM_DEVELOPER_NAME=Chris Banes

11 changes: 11 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
[versions]
agp = "8.2.0"
androidx-test-ext-junit = "1.1.5"
jetpackcompose-compiler = "1.5.6"
compose-multiplatform = "1.5.11"
ktlint = "1.0.1"
kotlin = "1.9.21"
robolectric = "4.10.3"
roborazzi = "1.9.0-alpha-3"
spotless = "6.23.3"

[plugins]
Expand All @@ -16,6 +19,7 @@ composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-mu
dokka = { id = "org.jetbrains.dokka", version = "1.9.10" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
metalava = { id = "me.tylerbwong.gradle.metalava", version = "0.3.5" }
roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" }
spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
mavenpublish = { id = "com.vanniktech.maven.publish", version = "0.26.0" }

Expand All @@ -25,6 +29,13 @@ androidx-compose-ui = "androidx.compose.ui:ui:1.6.0-beta03"
androidx-compose-material3 = "androidx.compose.material3:material3:1.1.2"
androidx-core = "androidx.core:core-ktx:1.12.0"
androidx-activity-compose = "androidx.activity:activity-compose:1.8.2"
androidx-compose-ui-test-manifest = "androidx.compose.ui:ui-test-manifest:1.5.4"
androidx-test-ext-junit = { module = "androidx.test.ext:junit-ktx", version.ref = "androidx-test-ext-junit" }
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
roborazzi-core = { module = "io.github.takahirom.roborazzi:roborazzi", version.ref = "roborazzi"}
roborazzi-compose = { module = "io.github.takahirom.roborazzi:roborazzi-compose", version.ref = "roborazzi"}
roborazzi-composedesktop = { module = "io.github.takahirom.roborazzi:roborazzi-compose-desktop", version.ref = "roborazzi"}
roborazzi-junit = { module = "io.github.takahirom.roborazzi:roborazzi-junit-rule", version.ref = "roborazzi"}

# Build logic dependencies
android-gradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "agp" }
Expand Down
13 changes: 13 additions & 0 deletions haze-jetpack-compose/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,21 @@ plugins {
id("org.jetbrains.dokka")
id("com.vanniktech.maven.publish")
id("me.tylerbwong.gradle.metalava")
id("io.github.takahirom.roborazzi")
}

android {
namespace = "dev.chrisbanes.haze.jetpackcompose"

defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

testOptions {
unitTests.isIncludeAndroidResources = true
}
testBuildType = "release"

buildFeatures {
compose = true
}
Expand All @@ -24,6 +34,9 @@ android {

dependencies {
api(libs.androidx.compose.ui)

testImplementation(kotlin("test"))
testImplementation(projects.internal.screenshotTest)
}

metalava {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,18 @@ internal class HazeNode31(
if (currentValueOf(LocalInspectionMode)) {
// If LocalInspectionMode is true, we're likely running in a preview/screenshot test
// and therefore don't have full access to Android drawing APIs. To avoid crashing we
// no-op and return early.
// just draw the content and return early.
drawContent()
return
}
drawIntoCanvas { canvas ->
// Similar to above, drawRenderNode is only available on hw-accelerated canvases.
// To avoid crashing we just draw the content and return early.
if (!canvas.nativeCanvas.isHardwareAccelerated) {
drawContent()
return@draw
}
}

val contentDrawScope = this

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2024, Christopher Banes and the Haze project contributors
// SPDX-License-Identifier: Apache-2.0

package dev.chrisbanes.haze

import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.graphics.Color
import dev.chrisbanes.haze.test.ScreenshotTest
import dev.chrisbanes.haze.test.screenshot
import kotlin.test.Test

class HazeScreenshotTest : ScreenshotTest() {
@Test
fun creditCard() = screenshot {
MaterialTheme {
CreditCardSample()
}
}

@Test
fun creditCard_transparentTint() = screenshot {
MaterialTheme {
CreditCardSample(tint = Color.Transparent)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright 2024, Christopher Banes and the Haze project contributors
// SPDX-License-Identifier: Apache-2.0

package dev.chrisbanes.haze

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Composable
fun CreditCardSample(tint: Color? = null) {
val hazeState = remember { HazeState() }

Box {
// Background content
Box(
Modifier
.fillMaxSize()
.haze(
state = hazeState,
backgroundColor = Color.Blue,
tint = tint ?: Color.White.copy(alpha = 0.1f),
blurRadius = 8.dp,
),
) {
Spacer(
Modifier
.fillMaxSize()
.background(brush = Brush.linearGradient(colors = listOf(Color.Blue, Color.Cyan))),
)

Text(
text = LorumIspum,
color = LocalContentColor.current.copy(alpha = 0.2f),
modifier = Modifier.padding(24.dp),
)
}

// Our card
Box(
modifier = Modifier
.fillMaxWidth(0.7f)
.aspectRatio(16 / 9f)
.align(Alignment.Center)
.hazeChild(state = hazeState, shape = RoundedCornerShape(16.dp)),
) {
Column(Modifier.padding(32.dp)) {
Text("Bank of Haze")
}
}
}
}

val LorumIspum by lazy {
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sit amet congue mauris, iaculis accumsan eros. Aliquam pulvinar est ac elit vulputate egestas. Vestibulum consequat libero at sem varius, vitae semper urna rhoncus. Aliquam mollis, ipsum a convallis scelerisque, sem dui consequat leo, in tempor risus est ac mi. Nam vel tellus dolor. Nunc lobortis bibendum fermentum. Mauris sed mollis justo, eu tristique elit. Cras semper augue a tortor tempor, vitae vestibulum eros convallis. Curabitur id justo eget tortor iaculis lobortis. Integer pharetra augue ac elit porta iaculis non vitae libero. Nam eros turpis, suscipit at iaculis vitae, malesuada vel arcu. Donec tincidunt porttitor iaculis. Pellentesque non augue magna. Mauris mattis purus vitae mi maximus, id molestie ipsum facilisis. Donec bibendum gravida dolor nec suscipit. Pellentesque tempus felis iaculis, porta diam sed, tristique tortor.
Sed vel tellus vel augue pulvinar semper sit amet eu est. In porta arcu eu sapien luctus scelerisque. In hac habitasse platea dictumst. Aenean varius lobortis malesuada. Sed vitae ornare arcu. Nunc maximus lectus purus, vel aliquet velit facilisis a. Nulla maximus bibendum magna id vulputate. Mauris volutpat lorem et risus porta dignissim. In at elit a est vulputate tincidunt.
Nulla facilisi. Curabitur gravida quam nec massa tempus, sed placerat nunc hendrerit. Duis sit amet cursus ipsum. Phasellus eget congue lacus. Duis vehicula venenatis posuere. Morbi non tempor risus. Aenean bibendum efficitur tortor, eu interdum velit gravida rutrum. Sed tempus elementum libero. Suspendisse dapibus lorem vitae justo congue pellentesque. Phasellus et tellus sagittis, blandit nibh a, porta felis. Proin ornare eget odio eget laoreet. Cras id augue fringilla, molestie ligula sit amet, sollicitudin neque.
Suspendisse vitae bibendum justo, nec egestas mauris. Mauris id metus mi. Morbi ut maximus ex, eu consequat elit. Sed malesuada pellentesque mauris vel molestie. Nulla facilisi. Cras pellentesque metus id nibh sodales gravida. Vivamus a feugiat felis. Vivamus et justo libero. Maecenas ac augue viverra, blandit diam sed, porttitor sapien. Proin eu eros mollis, commodo lectus nec, imperdiet nisi. Proin nulla nulla, vehicula a faucibus sit amet, auctor sed lorem. Mauris ut ipsum sit amet massa posuere maximus eget porttitor nisl. Quisque nunc dolor, pharetra id nunc sit amet, maximus convallis nunc.
Ut magna diam, ullamcorper vel imperdiet at, dignissim sit amet turpis. Duis ut enim eu sapien fringilla placerat. Integer at dui eget leo tincidunt iaculis. Fusce nec elementum turpis. Aenean gravida, ipsum sit amet varius hendrerit, elit nisi hendrerit ex, et porta enim lorem eget mi. Duis convallis dolor a lacinia aliquam. Aliquam erat volutpat.
""".trim()
}
21 changes: 21 additions & 0 deletions haze/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ plugins {
id("org.jetbrains.dokka")
id("com.vanniktech.maven.publish")
id("me.tylerbwong.gradle.metalava")
id("io.github.takahirom.roborazzi")
}

android {
namespace = "dev.chrisbanes.haze"
defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
testOptions {
unitTests.isIncludeAndroidResources = true
}
testBuildType = "release"
}

kotlin {
Expand All @@ -38,6 +46,19 @@ kotlin {
val jvmMain by getting {
dependsOn(skikoMain)
}

val commonTest by getting {
dependencies {
implementation(kotlin("test"))
implementation(projects.internal.screenshotTest)
}
}
}
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions {
freeCompilerArgs += "-Xcontext-receivers"
}
}

Expand Down
3 changes: 3 additions & 0 deletions haze/screenshots/android/HazeScreenshotTest.creditCard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions haze/screenshots/desktop/HazeScreenshotTest.creditCard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2024, Christopher Banes and the Haze project contributors
// SPDX-License-Identifier: Apache-2.0

package dev.chrisbanes.haze

import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.graphics.Color
import dev.chrisbanes.haze.test.ScreenshotTest
import dev.chrisbanes.haze.test.screenshot
import kotlin.test.Test

class HazeScreenshotTest : ScreenshotTest() {
@Test
fun creditCard() = screenshot {
MaterialTheme {
CreditCardSample()
}
}

@Test
fun creditCard_transparentTint() = screenshot {
MaterialTheme {
CreditCardSample(tint = Color.Transparent)
}
}
}
Loading

0 comments on commit 39e39e6

Please sign in to comment.