This commit is contained in:
Benoit Marty 2025-10-06 10:16:29 +02:00
parent 9938b74669
commit d4e295807f
40 changed files with 678 additions and 1 deletions

1
.gitattributes vendored
View file

@ -1,4 +1,5 @@
screenshots/**/*.png filter=lfs diff=lfs merge=lfs -text screenshots/**/*.png filter=lfs diff=lfs merge=lfs -text
libraries/compound/screenshots/** filter=lfs diff=lfs merge=lfs -text
**/snapshots/**/*.png filter=lfs diff=lfs merge=lfs -text **/snapshots/**/*.png filter=lfs diff=lfs merge=lfs -text
**/docs/images-lfs/*.png filter=lfs diff=lfs merge=lfs -text **/docs/images-lfs/*.png filter=lfs diff=lfs merge=lfs -text
libraries/mediaupload/impl/src/test/assets/* filter=lfs diff=lfs merge=lfs -text libraries/mediaupload/impl/src/test/assets/* filter=lfs diff=lfs merge=lfs -text

View file

@ -56,6 +56,9 @@ echo "Deleting previous screenshots"
echo "Record screenshots" echo "Record screenshots"
./gradlew recordPaparazziDebug --stacktrace $GRADLE_ARGS ./gradlew recordPaparazziDebug --stacktrace $GRADLE_ARGS
echo "Record screenshots (Compound)"
./gradlew :libraries:compound:recordRoborazziDebug --stacktrace -PpreDexEnable=false --max-workers 4 --warn $GRADLE_ARGS
echo "Committing changes" echo "Committing changes"
git config http.sslVerify false git config http.sslVerify false

View file

@ -78,6 +78,7 @@ jobs:
name: tests-and-screenshot-tests-results name: tests-and-screenshot-tests-results
path: | path: |
**/build/paparazzi/failures/ **/build/paparazzi/failures/
**/build/roborazzi/failures/
**/build/reports/tests/*UnitTest/ **/build/reports/tests/*UnitTest/
# https://github.com/codecov/codecov-action # https://github.com/codecov/codecov-action

View file

@ -15,6 +15,7 @@ plugins {
alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.ksp) apply false alias(libs.plugins.ksp) apply false
alias(libs.plugins.dependencycheck) apply false alias(libs.plugins.dependencycheck) apply false
alias(libs.plugins.roborazzi) apply false
alias(libs.plugins.dependencyanalysis) alias(libs.plugins.dependencyanalysis)
alias(libs.plugins.detekt) alias(libs.plugins.detekt)
alias(libs.plugins.ktlint) alias(libs.plugins.ktlint)
@ -180,16 +181,22 @@ tasks.register("runQualityChecks") {
// Make sure to delete old screenshots before recording new ones // Make sure to delete old screenshots before recording new ones
subprojects { subprojects {
val snapshotsDir = File("${project.projectDir}/src/test/snapshots") val snapshotsDir = File("${project.projectDir}/src/test/snapshots")
val snapshotsDir2 = File("${project.projectDir}/screenshots")
val removeOldScreenshotsTask = tasks.register("removeOldSnapshots") { val removeOldScreenshotsTask = tasks.register("removeOldSnapshots") {
onlyIf { snapshotsDir.exists() } onlyIf { snapshotsDir.exists() || snapshotsDir2.exists() }
doFirst { doFirst {
println("Delete previous screenshots located at $snapshotsDir\n") println("Delete previous screenshots located at $snapshotsDir\n")
snapshotsDir.deleteRecursively() snapshotsDir.deleteRecursively()
println("Delete previous screenshots located at $snapshotsDir2\n")
snapshotsDir2.deleteRecursively()
} }
} }
tasks.findByName("recordPaparazzi")?.dependsOn(removeOldScreenshotsTask) tasks.findByName("recordPaparazzi")?.dependsOn(removeOldScreenshotsTask)
tasks.findByName("recordPaparazziDebug")?.dependsOn(removeOldScreenshotsTask) tasks.findByName("recordPaparazziDebug")?.dependsOn(removeOldScreenshotsTask)
tasks.findByName("recordPaparazziRelease")?.dependsOn(removeOldScreenshotsTask) tasks.findByName("recordPaparazziRelease")?.dependsOn(removeOldScreenshotsTask)
tasks.findByName("recordRoborazzi")?.dependsOn(removeOldScreenshotsTask)
tasks.findByName("recordRoborazziDebug")?.dependsOn(removeOldScreenshotsTask)
tasks.findByName("recordRoborazziRelease")?.dependsOn(removeOldScreenshotsTask)
} }
subprojects { subprojects {

View file

@ -54,3 +54,7 @@ ksp.allow.all.target.configuration=false
# Used to prevent detekt from reusing invalid cached rules # Used to prevent detekt from reusing invalid cached rules
detekt.use.worker.api=true detekt.use.worker.api=true
# Let test include roborazzi verification
# https://github.com/takahirom/roborazzi?tab=readme-ov-file#roborazzitest
roborazzi.test.verify=true

View file

@ -32,6 +32,7 @@ accompanist = "0.37.3"
# Test # Test
test_core = "1.7.0" test_core = "1.7.0"
roborazzi = "1.46.1"
# Jetbrain # Jetbrain
datetime = "0.7.1" datetime = "0.7.1"
@ -226,6 +227,11 @@ google_autoservice_annotations = { module = "com.google.auto.service:auto-servic
# Miscellaneous # Miscellaneous
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" } androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
# Test
test_roborazzi = { module = "io.github.takahirom.roborazzi:roborazzi", version.ref = "roborazzi" }
test_roborazzi_compose = { module = "io.github.takahirom.roborazzi:roborazzi-compose", version.ref = "roborazzi" }
test_roborazzi_junit = { module = "io.github.takahirom.roborazzi:roborazzi-junit-rule", version.ref = "roborazzi" }
[bundles] [bundles]
[plugins] [plugins]
@ -243,6 +249,7 @@ dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12"
dependencycheck = "org.owasp.dependencycheck:12.1.6" dependencycheck = "org.owasp.dependencycheck:12.1.6"
dependencyanalysis = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependencyAnalysis" } dependencyanalysis = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependencyAnalysis" }
paparazzi = "app.cash.paparazzi:2.0.0-alpha02" paparazzi = "app.cash.paparazzi:2.0.0-alpha02"
roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" }
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
firebaseAppDistribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" } firebaseAppDistribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" }
knit = { id = "org.jetbrains.kotlinx.knit", version = "0.5.0" } knit = { id = "org.jetbrains.kotlinx.knit", version = "0.5.0" }

View file

@ -1,3 +1,5 @@
import extension.testCommonDependencies
/* /*
* Copyright 2022, 2025 New Vector Ltd. * Copyright 2022, 2025 New Vector Ltd.
* *
@ -7,12 +9,21 @@
plugins { plugins {
id("io.element.android-compose-library") id("io.element.android-compose-library")
alias(libs.plugins.roborazzi)
} }
android { android {
namespace = "io.element.android.compound" namespace = "io.element.android.compound"
testOptions {
unitTests.isIncludeAndroidResources = true
}
dependencies { dependencies {
implementation(libs.showkase) implementation(libs.showkase)
testCommonDependencies(libs)
testImplementation(libs.test.roborazzi)
testImplementation(libs.test.roborazzi.compose)
testImplementation(libs.test.roborazzi.junit)
} }
} }

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:caf2de32caf0fa5368da899297e2eabd5a0c6891dd94f81295ef8a933d79ce16
size 10751

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:80e44e94d7b23af2ec4fd1c5a871851ae2567b40e478b30145de199076f20e95
size 11296

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6bc48b9f792da838f9fc2c2a630cbbbb906de851a138cc1ac8b7bf67b801ad84
size 211300

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e85fd2c67ff42829b8580ab5c0c2af1fee40973f5c0b34ef7de00e3663cee8e4
size 223041

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:406b62991171a146e891f95bcad0321ebc60cf1fe2cabc9caedbb17fb062af13
size 224320

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:578e9b5a38791e2686a7b9ba5c461eb1d1fb29dfbe950bf46c113ad75ceac175
size 327758

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4cab40fc0506c8f2a2efafb1199e85f1da3ebacb49b176e9105e3f95175f85ee
size 325565

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:174f9d4ee70a29c0c8c2a01a15daeb14281530678ff7d7fb19a208bfd789533a
size 309210

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7598b98462c015f2bf74b3ea3ad95fc0220b2efb9bb81ac56025cf6a158e3f8a
size 308976

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ac84a7175c4a4897aa28eddcf722b7997c6576f612eb38fa09ffabcf7be11e00
size 119496

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6ff6dfdfab51332cad3bdfa351a4d0e305de5f899853575a8514858cc871e904
size 83609

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ca38f5f23c282a6dc4c01a54705f71bf8c927aeac9c1df0a1f3abe50c10b1b85
size 89336

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:03bdb2f3de01d40b8f85ba87b395cdab2e5225aded2f40a0892077798bca6066
size 22328

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3e9d676a1ef20a7228e985f62d346265ff9c31d1860a219540f012c063c9345e
size 33652

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d27f813acc9e8dc3960f5205809c8c0d2ba0fa51fd9e3ca07964b866b125e87d
size 110171

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:85969829577e158bdd1d0f21c8b3a2334dcde79cb50d5e2331d06d5423332be2
size 160754

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ca64da1dc373dc49503ee525ae3331e73d0ab12053993a1ef19dcab1e67b08c4
size 159123

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:01622bca20a132ec5a874fc1a2d0ffd45e7ce6d7849c4d607d79c7bc51d6c6a9
size 163322

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:87c0c4ff42d17137d554708ce33f40f214ec608eca4ca87af0b2adab63de6bb7
size 162891

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4be10c3bb9900d27a3b406eca0cb902b0ff9cdf90e8e3cf1ae7760aa7c5d47d9
size 377446

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c166e5371bb1922a9c016438a3cdfd0d68197237969d53a04f92baf6d53c4ac0
size 164925

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d936948cbad69d6935f2d2738d33682a55f044dfb1af8b5c9b8323c5f4318971
size 163558

View file

@ -0,0 +1,33 @@
/*
* Copyright 2023, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.compound.screenshot
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.captureRoboImage
import io.element.android.compound.screenshot.utils.screenshotFile
import io.element.android.compound.theme.AvatarColorsPreviewDark
import io.element.android.compound.theme.AvatarColorsPreviewLight
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class AvatarColorsTest {
@Test
@Config(sdk = [35], qualifiers = "xxhdpi")
fun screenshots() {
captureRoboImage(file = screenshotFile("Avatar Colors - Light.png")) {
AvatarColorsPreviewLight()
}
captureRoboImage(file = screenshotFile("Avatar Colors - Dark.png")) {
AvatarColorsPreviewDark()
}
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright 2023, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.compound.screenshot
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.captureRoboImage
import io.element.android.compound.previews.IconsCompoundPreviewDark
import io.element.android.compound.previews.IconsCompoundPreviewLight
import io.element.android.compound.previews.IconsCompoundPreviewRtl
import io.element.android.compound.previews.IconsPreview
import io.element.android.compound.screenshot.utils.screenshotFile
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
import kotlinx.collections.immutable.toImmutableList
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class CompoundIconTest {
@Test
@Config(sdk = [35], qualifiers = "w1024dp-h2048dp")
fun screenshots() {
captureRoboImage(file = screenshotFile("Compound Icons - Light.png")) {
IconsCompoundPreviewLight()
}
captureRoboImage(file = screenshotFile("Compound Icons - Rtl.png")) {
IconsCompoundPreviewRtl()
}
captureRoboImage(file = screenshotFile("Compound Icons - Dark.png")) {
IconsCompoundPreviewDark()
}
captureRoboImage(file = screenshotFile("Compound Vector Icons - Light.png")) {
val content: List<@Composable ColumnScope.() -> Unit> = CompoundIcons.all.map {
@Composable { Icon(imageVector = it, contentDescription = null) }
}
ElementTheme {
IconsPreview(
title = "Compound Vector Icons",
content = content.toImmutableList()
)
}
}
captureRoboImage(file = screenshotFile("Compound Vector Icons - Dark.png")) {
val content: List<@Composable ColumnScope.() -> Unit> = CompoundIcons.all.map {
@Composable { Icon(imageVector = it, contentDescription = null) }
}
ElementTheme(darkTheme = true) {
IconsPreview(
title = "Compound Vector Icons",
content = content.toImmutableList()
)
}
}
}
}

View file

@ -0,0 +1,65 @@
/*
* Copyright 2023, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.compound.screenshot
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.captureRoboImage
import io.element.android.compound.screenshot.utils.screenshotFile
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.TypographyTokens
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class CompoundTypographyTest {
@Test
@Config(sdk = [35], qualifiers = "h2048dp-xxhdpi")
fun screenshots() {
captureRoboImage(file = screenshotFile("Compound Typography.png")) {
ElementTheme {
Surface {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
with(TypographyTokens) {
TypographyTokenPreview(fontHeadingXlBold, "Heading XL Bold")
TypographyTokenPreview(fontHeadingXlRegular, "Heading XL Regular")
TypographyTokenPreview(fontHeadingLgBold, "Heading LG Bold")
TypographyTokenPreview(fontHeadingLgRegular, "Heading LG Regular")
TypographyTokenPreview(fontHeadingMdBold, "Heading MD Bold")
TypographyTokenPreview(fontHeadingMdRegular, "Heading MD Regular")
TypographyTokenPreview(fontHeadingSmMedium, "Heading SM Medium")
TypographyTokenPreview(fontHeadingSmRegular, "Heading SM Regular")
TypographyTokenPreview(fontBodyLgMedium, "Body LG Medium")
TypographyTokenPreview(fontBodyLgRegular, "Body LG Regular")
TypographyTokenPreview(fontBodyMdMedium, "Body MD Medium")
TypographyTokenPreview(fontBodyMdRegular, "Body MD Regular")
TypographyTokenPreview(fontBodySmMedium, "Body SM Medium")
TypographyTokenPreview(fontBodySmRegular, "Body SM Regular")
TypographyTokenPreview(fontBodyXsMedium, "Body XS Medium")
TypographyTokenPreview(fontBodyXsRegular, "Body XS Regular")
}
}
}
}
}
}
@Composable
private fun TypographyTokenPreview(style: TextStyle, text: String) {
Text(text = text, style = style)
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright 2023, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.compound.screenshot
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.captureRoboImage
import io.element.android.compound.screenshot.utils.screenshotFile
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.theme.ForcedDarkElementTheme
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class ForcedDarkElementThemeTest {
@Test
@Config(sdk = [35], qualifiers = "xxhdpi")
fun screenshots() {
captureRoboImage(file = screenshotFile("ForcedDarkElementTheme.png")) {
ElementTheme {
Surface {
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text(text = "Outside")
ForcedDarkElementTheme {
Surface {
Box(modifier = Modifier.fillMaxSize()) {
Text(text = "Inside ForcedDarkElementTheme", modifier = Modifier.align(Alignment.Center))
}
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright 2023, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.compound.screenshot
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.captureRoboImage
import io.element.android.compound.previews.ColorPreview
import io.element.android.compound.screenshot.utils.screenshotFile
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.theme.LinkColor
import io.element.android.compound.theme.SnackBarLabelColorDark
import io.element.android.compound.theme.SnackBarLabelColorLight
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class LegacyColorsTest {
@Test
@Config(sdk = [35], qualifiers = "xxhdpi")
fun screenshots() {
captureRoboImage(file = screenshotFile("Legacy Colors.png")) {
ElementTheme {
Surface {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Legacy Colors")
Spacer(modifier = Modifier.height(10.dp))
LegacyColorPreview(
color = LinkColor,
name = "Link"
)
LegacyColorPreview(
color = SnackBarLabelColorLight,
name = "SnackBar Label - Light"
)
LegacyColorPreview(
color = SnackBarLabelColorDark,
name = "SnackBar Label - Dark"
)
}
}
}
}
}
@Composable
private fun LegacyColorPreview(color: Color, name: String) {
ColorPreview(
backgroundColor = Color.White,
foregroundColor = Color.Black,
name = name,
color = color
)
}
}

View file

@ -0,0 +1,96 @@
/*
* Copyright 2023, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.compound.screenshot
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.captureRoboImage
import io.element.android.compound.screenshot.utils.screenshotFile
import io.element.android.compound.theme.ColorsSchemeDarkHcPreview
import io.element.android.compound.theme.ColorsSchemeDarkPreview
import io.element.android.compound.theme.ColorsSchemeLightHcPreview
import io.element.android.compound.theme.ColorsSchemeLightPreview
import io.element.android.compound.theme.ElementTheme
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class MaterialColorSchemeTest {
@Test
@Config(sdk = [35], qualifiers = "h2048dp-xhdpi")
fun screenshots() {
captureRoboImage(file = screenshotFile("Material3 Colors - Light.png")) {
ElementTheme {
Surface {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "M3 Light colors",
style = TextStyle.Default.copy(fontSize = 18.sp),
)
Spacer(modifier = Modifier.height(12.dp))
ColorsSchemeLightPreview()
}
}
}
}
captureRoboImage(file = screenshotFile("Material3 Colors - Light HC.png")) {
ElementTheme {
Surface {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "M3 Light HC colors",
style = TextStyle.Default.copy(fontSize = 18.sp),
)
Spacer(modifier = Modifier.height(12.dp))
ColorsSchemeLightHcPreview()
}
}
}
}
captureRoboImage(file = screenshotFile("Material3 Colors - Dark.png")) {
ElementTheme {
Surface {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "M3 Dark colors",
style = TextStyle.Default.copy(fontSize = 18.sp),
)
Spacer(modifier = Modifier.height(12.dp))
ColorsSchemeDarkPreview()
}
}
}
}
captureRoboImage(file = screenshotFile("Material3 Colors - Dark HC.png")) {
ElementTheme {
Surface {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "M3 Dark HC colors",
style = TextStyle.Default.copy(fontSize = 18.sp),
)
Spacer(modifier = Modifier.height(12.dp))
ColorsSchemeDarkHcPreview()
}
}
}
}
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.compound.screenshot
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.captureRoboImage
import io.element.android.compound.screenshot.utils.screenshotFile
import io.element.android.compound.theme.MaterialTextPreview
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class MaterialTextTest {
@Test
@Config(sdk = [35], qualifiers = "w480dp-h1200dp-xxhdpi")
fun screenshots() {
captureRoboImage(file = screenshotFile("MaterialText Colors.png")) {
MaterialTextPreview()
}
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2023, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.compound.screenshot
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.captureRoboImage
import io.element.android.compound.previews.TypographyPreview
import io.element.android.compound.screenshot.utils.screenshotFile
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class MaterialTypographyTest {
@Test
@Config(sdk = [35], qualifiers = "h2048dp-xxhdpi")
fun screenshots() {
captureRoboImage(file = screenshotFile("Material Typography.png")) {
TypographyPreview()
}
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright 2023, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.compound.screenshot
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.captureRoboImage
import io.element.android.compound.previews.ColorsSchemePreview
import io.element.android.compound.screenshot.utils.screenshotFile
import io.element.android.compound.theme.ElementTheme
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class MaterialYouThemeTest {
@Test
@Config(sdk = [35], qualifiers = "h2048dp-xhdpi")
fun screenshots() {
captureRoboImage(file = screenshotFile("MaterialYou Theme - Light.png")) {
ElementTheme(dynamicColor = true) {
Surface {
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text(text = "Material You Theme - Light")
Spacer(modifier = Modifier.height(12.dp))
ColorsSchemePreview(Color.White, Color.Black, ElementTheme.materialColors)
}
}
}
}
captureRoboImage(file = screenshotFile("MaterialYou Theme - Dark.png")) {
ElementTheme(dynamicColor = true, darkTheme = true) {
Surface {
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text(text = "Material You Theme - Dark")
Spacer(modifier = Modifier.height(12.dp))
ColorsSchemePreview(Color.White, Color.Black, ElementTheme.materialColors)
}
}
}
}
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright 2023, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.compound.screenshot
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.captureRoboImage
import io.element.android.compound.previews.CompoundSemanticColorsDark
import io.element.android.compound.previews.CompoundSemanticColorsDarkHc
import io.element.android.compound.previews.CompoundSemanticColorsLight
import io.element.android.compound.previews.CompoundSemanticColorsLightHc
import io.element.android.compound.screenshot.utils.screenshotFile
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class SemanticColorsTest {
@Config(sdk = [35], qualifiers = "h2000dp-xhdpi")
@Test
fun screenshots() {
captureRoboImage(file = screenshotFile("Compound Semantic Colors - Light.png")) {
CompoundSemanticColorsLight()
}
captureRoboImage(file = screenshotFile("Compound Semantic Colors - Light HC.png")) {
CompoundSemanticColorsLightHc()
}
captureRoboImage(file = screenshotFile("Compound Semantic Colors - Dark.png")) {
CompoundSemanticColorsDark()
}
captureRoboImage(file = screenshotFile("Compound Semantic Colors - Dark HC.png")) {
CompoundSemanticColorsDarkHc()
}
}
}

View file

@ -0,0 +1,18 @@
/*
* Copyright 2023, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.compound.screenshot.utils
import java.io.File
/**
* Returns a [File] object for a screenshot with the given [filename].
* This is to ensure we have a consistent location for all screenshots.
*/
fun screenshotFile(filename: String): File {
return File("screenshots", filename)
}