diff --git a/build.gradle b/build.gradle index d6617f2..748a87c 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,7 @@ plugins { id 'org.jetbrains.kotlin.plugin.serialization' version '2.3.0' apply false id 'org.jetbrains.kotlin.plugin.parcelize' version '2.3.0' apply false id 'com.google.devtools.ksp' version '2.3.4' apply false + id 'io.github.takahirom.roborazzi' version '1.42.0' apply false id 'com.google.gms.google-services' version '4.4.2' apply false id 'io.gitlab.arturbosch.detekt' version '1.23.8' apply false } diff --git a/core/designsystem/build.gradle b/core/designsystem/build.gradle index 14b024e..2e0dea8 100644 --- a/core/designsystem/build.gradle +++ b/core/designsystem/build.gradle @@ -2,6 +2,7 @@ plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlin.plugin.compose' + id 'io.github.takahirom.roborazzi' } android { @@ -22,6 +23,12 @@ android { kotlinOptions { jvmTarget = '17' } + // Roborazzi/Robolectric: рендер Compose в PNG на JVM (Fd-сверка без эмулятора). + testOptions { + unitTests { + includeAndroidResources = true + } + } } dependencies { @@ -32,4 +39,14 @@ dependencies { implementation libs.compose.material3 implementation libs.coil.compose implementation libs.coil.svg + + // Screenshot-тесты (JVM) + testImplementation composeBom + testImplementation libs.junit + testImplementation libs.robolectric + testImplementation libs.roborazzi + testImplementation libs.roborazzi.compose + testImplementation libs.roborazzi.junit.rule + testImplementation libs.compose.ui.test.junit4 + debugImplementation libs.compose.ui.test.manifest } diff --git a/core/designsystem/src/test/java/ru/forbion/f7cloud/core/designsystem/ScreenshotProofTest.kt b/core/designsystem/src/test/java/ru/forbion/f7cloud/core/designsystem/ScreenshotProofTest.kt new file mode 100644 index 0000000..f46b63d --- /dev/null +++ b/core/designsystem/src/test/java/ru/forbion/f7cloud/core/designsystem/ScreenshotProofTest.kt @@ -0,0 +1,50 @@ +package ru.forbion.f7cloud.core.designsystem + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.unit.dp +import com.github.takahirom.roborazzi.captureRoboImage +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +/** + * Пилот Roborazzi: рендер Compose designsystem в PNG на JVM (без эмулятора). + * Каркас Fd-сверки: экран → PNG → сравнение с веб-мобильной forbion. + */ +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config(sdk = [34], qualifiers = "w390dp-h844dp-xxhdpi") +class ScreenshotProofTest { + + @get:Rule + val composeRule = createComposeRule() + + @Test + fun designsystem_primary_button() { + composeRule.setContent { + F7Theme { + Column( + modifier = Modifier.fillMaxSize().background(Color.White).padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + Text("F7 design proof — Roborazzi") + F7PrimaryButton(text = "Сохранить", onClick = {}) + F7SecondaryButton(text = "Отмена", onClick = {}) + } + } + } + composeRule.onRoot().captureRoboImage("src/test/screenshots/proof-primary-button.png") + } +} diff --git a/core/designsystem/src/test/screenshots/proof-primary-button.png b/core/designsystem/src/test/screenshots/proof-primary-button.png new file mode 100644 index 0000000..59ca38c Binary files /dev/null and b/core/designsystem/src/test/screenshots/proof-primary-button.png differ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5f9e27a..6dea56b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,6 +8,8 @@ coreKtx = "1.15.0" # был дрейф 1.13.1 / 1.15.0 lifecycle = "2.8.7" # был дрейф 2.8.1 / 2.8.7 coil = "2.7.0" # был дрейф 2.6.0 / 2.7.0 media3 = "1.4.1" # нативный медиа-слой (видео/аудио) — ExoPlayer +roborazzi = "1.42.0" # screenshot-тесты Compose на JVM (Fd-сверка без эмулятора) +robolectric = "4.14.1" coroutines = "1.10.1" # был дрейф 1.8.1 / 1.10.1 navigationCompose = "2.8.9" room = "2.7.2" @@ -42,6 +44,13 @@ coil-svg = { group = "io.coil-kt", name = "coil-svg", version.ref = "coil" } coil-gif = { group = "io.coil-kt", name = "coil-gif", version.ref = "coil" } media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" } media3-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "media3" } + +roborazzi = { group = "io.github.takahirom.roborazzi", name = "roborazzi", version.ref = "roborazzi" } +roborazzi-compose = { group = "io.github.takahirom.roborazzi", name = "roborazzi-compose", version.ref = "roborazzi" } +roborazzi-junit-rule = { group = "io.github.takahirom.roborazzi", name = "roborazzi-junit-rule", version.ref = "roborazzi" } +robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } +compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } +compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" } coroutines-play-services = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-play-services", version.ref = "coroutines" } okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }