'
+} >> reports/report.html
+
+ for file in $files; do
+ # Get the file name and insert newlines every 100 characters
+ fileName=$(basename "$file" | sed -r 's/(.{100})/\1 /g')
+ echo "
$(basename "$file")
" >> reports/report.html
+ echo "
" >> reports/report.html
+ done
+{
+ echo '
'
+ echo '
'
+ echo ''
+ echo ''
+ echo ''
+ echo ''
+} >> reports/report.html
+
+echo "Report: "
+cat reports/report.html
diff --git a/.github/workflows/compare_screenshots.yml b/.github/workflows/compare_screenshots.yml
new file mode 100644
index 000000000..aeb5dba0c
--- /dev/null
+++ b/.github/workflows/compare_screenshots.yml
@@ -0,0 +1,49 @@
+name: Compare Screenshots
+
+on:
+ workflow_dispatch:
+ pull_request:
+
+jobs:
+ CompareScreenshots:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v3
+
+ - name: Setup Gradle
+ uses: gradle/gradle-build-action@v2
+
+ - name: Verify Screenshots (roborazzi)
+ run: 'bash ./gradlew verifyRoborazziDebug'
+
+ - id: generate-html
+ name: Generate Html Report
+ if: failure()
+ uses: ./.github/actions/generate-html-report
+
+ - name: Generate screenshots tests reports tar.gz
+ if: failure()
+ run: |
+ tar cvzf mistica-screenshots-tests-report.tar.gz reports || echo "No screenshots tests reports found"
+ shell: bash
+
+ - name: Checkout Telefonica/github-actions repo
+ if: failure()
+ uses: actions/checkout@v3
+ with:
+ repository: Telefonica/github-actions
+ token: "${{ secrets.NOVUM_PRIVATE_REPOS }}"
+ path: .github/shared-actions
+
+ - name: Upload reports to azure
+ if: failure()
+ uses: ./.github/shared-actions/azure/upload-to-storage
+ with:
+ azure-account-name: ${{secrets.AZURE_ACCOUNT_NAME}}
+ azure-account-key: ${{secrets.AZURE_ACCOUNT_KEY}}
+ globs: |
+ mistica-screenshots-tests-report.tar.gz
+ generate-summary: true
diff --git a/.github/workflows/update_screenshot_baseline.yml b/.github/workflows/update_screenshot_baseline.yml
new file mode 100644
index 000000000..500f96b23
--- /dev/null
+++ b/.github/workflows/update_screenshot_baseline.yml
@@ -0,0 +1,24 @@
+name: "Update screenshot baseline"
+on:
+ workflow_dispatch:
+
+jobs:
+ screenshots_baseline:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v2
+
+ - name: Run Roborazzi Record
+ run: 'bash ./gradlew clean recordRoborazziDebug'
+
+ - name: Check Git status
+ run: 'git status'
+ shell: bash
+
+ - name: Commit and push screenshots baseline
+ id: commitAndPushScreenshotsBaseline
+ uses: EndBug/add-and-commit@v7
+ with:
+ message: 'Updated screenshots baseline'
+ add: './**/screenshots/*'
diff --git a/build.gradle b/build.gradle
index 26d786926..1c2bc6323 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,6 +10,7 @@ buildscript {
accompanist_version = "0.30.1"
coil_version = '2.2.2'
constraintComposeVersion = '1.0.1'
+ roborazzi_version = "1.4.0"
}
repositories {
google()
@@ -27,6 +28,7 @@ buildscript {
plugins {
id 'org.jetbrains.kotlin.android' version '1.5.21' apply false
id 'io.github.gradle-nexus.publish-plugin' version '1.1.0' apply false
+ id "io.github.takahirom.roborazzi" version '1.4.0' apply false
}
allprojects {
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f02a74898..b619500b1 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Fri May 19 13:07:06 CEST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/library/build.gradle b/library/build.gradle
index c9e35f5fa..ccf7740d3 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -3,6 +3,7 @@ plugins {
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'maven-publish'
+ id 'io.github.takahirom.roborazzi'
}
android {
@@ -51,6 +52,15 @@ android {
sarifReport true
checkDependencies true
}
+
+ testOptions {
+ unitTests {
+ includeAndroidResources = true
+ all {
+ systemProperty 'robolectric.graphicsMode', 'NATIVE'
+ }
+ }
+ }
}
task sourceJar(type: Jar) {
@@ -88,7 +98,15 @@ dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version"
+ testImplementation 'androidx.compose.ui:ui-test-manifest:1.0.5'
+ testImplementation 'org.robolectric:robolectric:4.10.3'
+ testImplementation "io.github.takahirom.roborazzi:roborazzi:$roborazzi_version"
+ testImplementation "io.github.takahirom.roborazzi:roborazzi-compose:$roborazzi_version"
testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0"
+ testImplementation "androidx.test:rules:1.5.0"
+ testImplementation "androidx.test:core-ktx:1.5.0"
+ testImplementation 'androidx.test.ext:junit-ktx:1.1.5'
+ testImplementation 'androidx.test.espresso:espresso-core:3.2.0'
debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
}
diff --git a/library/screenshots/check_TextInput.png b/library/screenshots/check_TextInput.png
new file mode 100644
index 000000000..443fc8a23
Binary files /dev/null and b/library/screenshots/check_TextInput.png differ
diff --git a/library/screenshots/check_TextInputDisabled.png b/library/screenshots/check_TextInputDisabled.png
new file mode 100644
index 000000000..6b342981f
Binary files /dev/null and b/library/screenshots/check_TextInputDisabled.png differ
diff --git a/library/screenshots/check_TextInputDisabled_dark.png b/library/screenshots/check_TextInputDisabled_dark.png
new file mode 100644
index 000000000..6a0907d09
Binary files /dev/null and b/library/screenshots/check_TextInputDisabled_dark.png differ
diff --git a/library/screenshots/check_TextInputWithError.png b/library/screenshots/check_TextInputWithError.png
new file mode 100644
index 000000000..3d3a86dd2
Binary files /dev/null and b/library/screenshots/check_TextInputWithError.png differ
diff --git a/library/screenshots/check_TextInputWithError_dark.png b/library/screenshots/check_TextInputWithError_dark.png
new file mode 100644
index 000000000..648dc145f
Binary files /dev/null and b/library/screenshots/check_TextInputWithError_dark.png differ
diff --git a/library/screenshots/check_TextInput_dark.png b/library/screenshots/check_TextInput_dark.png
new file mode 100644
index 000000000..353f090b0
Binary files /dev/null and b/library/screenshots/check_TextInput_dark.png differ
diff --git a/library/screenshots/check_the_button_screenshot_BlauBrand.png b/library/screenshots/check_the_button_screenshot_BlauBrand.png
new file mode 100644
index 000000000..3886ccc05
Binary files /dev/null and b/library/screenshots/check_the_button_screenshot_BlauBrand.png differ
diff --git a/library/screenshots/check_the_button_screenshot_BlauBrand_dark.png b/library/screenshots/check_the_button_screenshot_BlauBrand_dark.png
new file mode 100644
index 000000000..6a5f0e69c
Binary files /dev/null and b/library/screenshots/check_the_button_screenshot_BlauBrand_dark.png differ
diff --git a/library/screenshots/check_the_button_screenshot_MovistarBrand.png b/library/screenshots/check_the_button_screenshot_MovistarBrand.png
new file mode 100644
index 000000000..ee97984bd
Binary files /dev/null and b/library/screenshots/check_the_button_screenshot_MovistarBrand.png differ
diff --git a/library/screenshots/check_the_button_screenshot_MovistarBrand_dark.png b/library/screenshots/check_the_button_screenshot_MovistarBrand_dark.png
new file mode 100644
index 000000000..8bb4ba2dd
Binary files /dev/null and b/library/screenshots/check_the_button_screenshot_MovistarBrand_dark.png differ
diff --git a/library/screenshots/check_the_button_screenshot_O2Brand.png b/library/screenshots/check_the_button_screenshot_O2Brand.png
new file mode 100644
index 000000000..6fab6c741
Binary files /dev/null and b/library/screenshots/check_the_button_screenshot_O2Brand.png differ
diff --git a/library/screenshots/check_the_button_screenshot_O2Brand_dark.png b/library/screenshots/check_the_button_screenshot_O2Brand_dark.png
new file mode 100644
index 000000000..46769318e
Binary files /dev/null and b/library/screenshots/check_the_button_screenshot_O2Brand_dark.png differ
diff --git a/library/screenshots/check_the_button_screenshot_TelefonicaBrand.png b/library/screenshots/check_the_button_screenshot_TelefonicaBrand.png
new file mode 100644
index 000000000..16b0f073b
Binary files /dev/null and b/library/screenshots/check_the_button_screenshot_TelefonicaBrand.png differ
diff --git a/library/screenshots/check_the_button_screenshot_TelefonicaBrand_dark.png b/library/screenshots/check_the_button_screenshot_TelefonicaBrand_dark.png
new file mode 100644
index 000000000..2976d3f7b
Binary files /dev/null and b/library/screenshots/check_the_button_screenshot_TelefonicaBrand_dark.png differ
diff --git a/library/screenshots/check_the_button_screenshot_VivoBrand.png b/library/screenshots/check_the_button_screenshot_VivoBrand.png
new file mode 100644
index 000000000..c13a91227
Binary files /dev/null and b/library/screenshots/check_the_button_screenshot_VivoBrand.png differ
diff --git a/library/screenshots/check_the_button_screenshot_VivoBrand_dark.png b/library/screenshots/check_the_button_screenshot_VivoBrand_dark.png
new file mode 100644
index 000000000..b6f80006b
Binary files /dev/null and b/library/screenshots/check_the_button_screenshot_VivoBrand_dark.png differ
diff --git a/library/screenshots/check_the_password_is_initially_not_visible.png b/library/screenshots/check_the_password_is_initially_not_visible.png
new file mode 100644
index 000000000..caf321eb2
Binary files /dev/null and b/library/screenshots/check_the_password_is_initially_not_visible.png differ
diff --git a/library/screenshots/check_the_password_is_not_visible_after_toggling_twice_the_visibility_button.png b/library/screenshots/check_the_password_is_not_visible_after_toggling_twice_the_visibility_button.png
new file mode 100644
index 000000000..caf321eb2
Binary files /dev/null and b/library/screenshots/check_the_password_is_not_visible_after_toggling_twice_the_visibility_button.png differ
diff --git a/library/screenshots/check_the_password_is_visible_after_the_visibility_button_is_clicked.png b/library/screenshots/check_the_password_is_visible_after_the_visibility_button_is_clicked.png
new file mode 100644
index 000000000..7511f95c0
Binary files /dev/null and b/library/screenshots/check_the_password_is_visible_after_the_visibility_button_is_clicked.png differ
diff --git a/library/src/test/AndroidManifest.xml b/library/src/test/AndroidManifest.xml
new file mode 100644
index 000000000..c2944977c
--- /dev/null
+++ b/library/src/test/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/library/src/test/java/com/telefonica/mistica/DummyActivity.kt b/library/src/test/java/com/telefonica/mistica/DummyActivity.kt
new file mode 100644
index 000000000..f84cb814d
--- /dev/null
+++ b/library/src/test/java/com/telefonica/mistica/DummyActivity.kt
@@ -0,0 +1,13 @@
+package com.telefonica.mistica
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import com.telefonica.mistica.R
+
+class DummyActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.test_dummy_activity)
+ }
+}
diff --git a/library/src/test/java/com/telefonica/mistica/compose/button/ButtonBehaviourTest.kt b/library/src/test/java/com/telefonica/mistica/compose/button/ButtonBehaviourTest.kt
new file mode 100644
index 000000000..4deb68dae
--- /dev/null
+++ b/library/src/test/java/com/telefonica/mistica/compose/button/ButtonBehaviourTest.kt
@@ -0,0 +1,64 @@
+package com.telefonica.mistica.compose.button
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.dp
+import com.telefonica.mistica.compose.theme.MisticaTheme
+import com.telefonica.mistica.compose.theme.brand.MovistarBrand
+import org.junit.Assert.assertTrue
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+internal class ButtonBehaviourTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun `check the button is clicked`() = test {
+ `given Button`()
+
+ `when the button is clicked`()
+
+ `then the onClickListener has been invoked`()
+ }
+
+ private fun TestScope.`given Button`() {
+ `when Button`()
+ }
+
+ private fun TestScope.`when Button`() {
+ composeTestRule.setContent {
+ MisticaTheme(brand = MovistarBrand) {
+ Button(
+ text = textValue,
+ onClickListener = onClickListener,
+ modifier = Modifier.padding(16.dp)
+ )
+ }
+ }
+ }
+
+ private fun TestScope.`when the button is clicked`() {
+ composeTestRule.onNodeWithText(textValue).performClick()
+ }
+
+ private fun TestScope.`then the onClickListener has been invoked`() {
+ assertTrue(clicked)
+ }
+
+ private fun test(block: TestScope.() -> Unit) {
+ TestScope().block()
+ }
+
+ private class TestScope {
+ val textValue = "textValue"
+ var clicked = false
+ val onClickListener: () -> Unit = { clicked = true }
+ }
+}
\ No newline at end of file
diff --git a/library/src/test/java/com/telefonica/mistica/compose/button/ButtonKtTest.kt b/library/src/test/java/com/telefonica/mistica/compose/button/ButtonKtTest.kt
new file mode 100644
index 000000000..6f55080c4
--- /dev/null
+++ b/library/src/test/java/com/telefonica/mistica/compose/button/ButtonKtTest.kt
@@ -0,0 +1,68 @@
+package com.telefonica.mistica.compose.button
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.Surface
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.unit.dp
+import com.telefonica.mistica.compose.theme.MisticaTheme
+import com.telefonica.mistica.compose.theme.brand.BlauBrand
+import com.telefonica.mistica.compose.theme.brand.Brand
+import com.telefonica.mistica.compose.theme.brand.MovistarBrand
+import com.telefonica.mistica.compose.theme.brand.O2Brand
+import com.telefonica.mistica.compose.theme.brand.TelefonicaBrand
+import com.telefonica.mistica.compose.theme.brand.VivoBrand
+import com.telefonica.mistica.testutils.ScreenshotsTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.ParameterizedRobolectricTestRunner
+
+@RunWith(ParameterizedRobolectricTestRunner::class)
+internal class ButtonKtTest(private val brand: Brand, private val darkTheme: Boolean): ScreenshotsTest() {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun `check the button screenshot`() {
+ `when Button`(brand, darkTheme)
+
+ `then screenshot is OK`(brand, darkTheme)
+ }
+
+ private fun `when Button`(brand: Brand = MovistarBrand, darkTheme: Boolean) {
+ composeTestRule.setContent {
+ MisticaTheme(brand = brand, darkTheme = darkTheme) {
+ Surface {
+ Button(
+ text = "textValue",
+ onClickListener = { },
+ modifier = Modifier.padding(16.dp)
+ )
+ }
+ }
+ }
+ }
+
+ private fun `then screenshot is OK`(brand: Brand, darkTheme: Boolean) {
+ compareScreenshot(composeTestRule.onRoot(), brand, darkTheme)
+ }
+
+ companion object {
+ @JvmStatic
+ @ParameterizedRobolectricTestRunner.Parameters(name = "Input: {0}")
+ fun brands() = listOf(
+ arrayOf(MovistarBrand, false),
+ arrayOf(VivoBrand, false),
+ arrayOf(O2Brand, false),
+ arrayOf(BlauBrand, false),
+ arrayOf(TelefonicaBrand, false),
+ arrayOf(MovistarBrand, true),
+ arrayOf(VivoBrand, true),
+ arrayOf(O2Brand, true),
+ arrayOf(BlauBrand, true),
+ arrayOf(TelefonicaBrand, true),
+ )
+ }
+}
diff --git a/library/src/test/java/com/telefonica/mistica/compose/input/PasswordInputKtTest.kt b/library/src/test/java/com/telefonica/mistica/compose/input/PasswordInputKtTest.kt
new file mode 100644
index 000000000..579ad96e5
--- /dev/null
+++ b/library/src/test/java/com/telefonica/mistica/compose/input/PasswordInputKtTest.kt
@@ -0,0 +1,80 @@
+package com.telefonica.mistica.compose.input
+
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
+import com.telefonica.mistica.compose.theme.MisticaTheme
+import com.telefonica.mistica.compose.theme.brand.MovistarBrand
+import com.telefonica.mistica.testutils.ScreenshotsTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+internal class PasswordInputKtTest: ScreenshotsTest() {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun `check the password is initially not visible`() = test {
+ `when PasswordInput`()
+
+ `then screenshot is OK`()
+ }
+
+ @Test
+ fun `check the password is visible after the visibility button is clicked`() = test {
+ `given PasswordInput`()
+
+ `when the visibility button is clicked`()
+
+ `then screenshot is OK`()
+ }
+
+ @Test
+ fun `check the password is not visible after toggling twice the visibility button`() = test {
+ `given PasswordInput`()
+
+ `when the visibility button is clicked`(times = 2)
+
+ `then screenshot is OK`()
+ }
+
+ private fun TestScope.`given PasswordInput`() {
+ `when PasswordInput`()
+ }
+
+ private fun TestScope.`when PasswordInput`() {
+ composeTestRule.setContent {
+ MisticaTheme(brand = MovistarBrand) {
+ PasswordInput(
+ value = textValue,
+ onValueChange = onValueChanged,
+ label = "label",
+ )
+ }
+ }
+ }
+
+ private fun `when the visibility button is clicked`(times: Int = 1) {
+ repeat(times) {
+ composeTestRule.onNodeWithTag(TextInputTestTags.PASSWORD_VISIBILITY_TOGGLE).performClick()
+ }
+ }
+
+ private fun `then screenshot is OK`() {
+ compareScreenshot(composeTestRule.onRoot())
+ }
+
+ private fun test(block: TestScope.() -> Unit) {
+ TestScope().block()
+ }
+
+ private class TestScope {
+ val textValue = "textValue"
+ var valueChanged = false
+ val onValueChanged: (String) -> Unit = { valueChanged = true }
+ }
+}
diff --git a/library/src/test/java/com/telefonica/mistica/input/TextInputTest.kt b/library/src/test/java/com/telefonica/mistica/input/TextInputTest.kt
new file mode 100644
index 000000000..7454dedd2
--- /dev/null
+++ b/library/src/test/java/com/telefonica/mistica/input/TextInputTest.kt
@@ -0,0 +1,73 @@
+package com.telefonica.mistica.input
+
+import android.widget.FrameLayout
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.matcher.ViewMatchers
+import androidx.test.ext.junit.rules.activityScenarioRule
+import com.telefonica.mistica.DummyActivity
+import com.telefonica.mistica.R
+import com.telefonica.mistica.testutils.ScreenshotsTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+
+@RunWith(RobolectricTestRunner::class)
+internal class TextInputTest: ScreenshotsTest() {
+ @get:Rule
+ val rule = activityScenarioRule()
+
+ @Test
+ fun `check TextInput`() {
+ checkTextInput()
+ }
+
+ @Config(qualifiers = "+night")
+ @Test
+ fun `check TextInput dark`() {
+ checkTextInput()
+ }
+
+ @Test
+ fun `check TextInputWithError`() {
+ checkTextInput {
+ error = "Whatever error"
+ }
+ }
+
+ @Config(qualifiers = "+night")
+ @Test
+ fun `check TextInputWithError dark`() {
+ checkTextInput {
+ error = "Whatever error"
+ }
+ }
+
+ @Test
+ fun `check TextInputDisabled`() {
+ checkTextInput {
+ isEnabled = false
+ }
+ }
+
+ @Config(qualifiers = "+night")
+ @Test
+ fun `check TextInputDisabled dark`() {
+ checkTextInput {
+ isEnabled = false
+ }
+ }
+
+ private fun checkTextInput(onTextInput: TextInput.() -> Unit = {}) {
+ rule.scenario.onActivity { activity ->
+ val wrapper: FrameLayout = activity.findViewById(R.id.dummy_activity_wrapper)
+ val textInput = TextInput(activity)
+ wrapper.addView(textInput)
+ textInput.text = "Hello android devs!"
+ textInput.onTextInput()
+
+ compareScreenshot(onView(ViewMatchers.withId(R.id.dummy_activity_wrapper)))
+ }
+ }
+}
diff --git a/library/src/test/java/com/telefonica/mistica/testutils/ScreenshotUtils.kt b/library/src/test/java/com/telefonica/mistica/testutils/ScreenshotUtils.kt
new file mode 100644
index 000000000..29b78f2ba
--- /dev/null
+++ b/library/src/test/java/com/telefonica/mistica/testutils/ScreenshotUtils.kt
@@ -0,0 +1,20 @@
+package com.telefonica.mistica.testutils
+
+import com.telefonica.mistica.compose.theme.brand.Brand
+
+object ScreenshotUtils {
+ fun getScreenshotName(brand: Brand? = null, extra: String = ""): String {
+ return """screenshots/
+ ${TestUtils.findRunningTestMethodName()}
+ ${brandName(brand, prepend = "_")}${extra}.png"""
+ .replace("\\s+".toRegex(), "")
+ }
+
+ private fun brandName(brand: Brand?, prepend: String = "", append: String = ""): String {
+ return if (brand != null) {
+ "$prepend${brand::class.java.simpleName}$append"
+ } else {
+ ""
+ }
+ }
+}
diff --git a/library/src/test/java/com/telefonica/mistica/testutils/ScreenshotsTest.kt b/library/src/test/java/com/telefonica/mistica/testutils/ScreenshotsTest.kt
new file mode 100644
index 000000000..826672af8
--- /dev/null
+++ b/library/src/test/java/com/telefonica/mistica/testutils/ScreenshotsTest.kt
@@ -0,0 +1,34 @@
+package com.telefonica.mistica.testutils
+
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.test.espresso.ViewInteraction
+import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
+import com.github.takahirom.roborazzi.captureRoboImage
+import com.telefonica.mistica.compose.theme.brand.Brand
+import org.robolectric.annotation.Config
+
+@Config(sdk = [33], qualifiers = RobolectricDeviceQualifiers.Pixel5)
+open class ScreenshotsTest {
+ fun compareScreenshot(
+ node: SemanticsNodeInteraction,
+ brand: Brand? = null,
+ darkTheme: Boolean = false,
+ extra: String = "",
+ ) {
+ node.captureRoboImage(screenshotName(brand, darkTheme, extra))
+ }
+
+ fun compareScreenshot(
+ node: ViewInteraction,
+ brand: Brand? = null,
+ darkTheme: Boolean = false,
+ extra: String = "",
+ ) {
+ node.captureRoboImage(screenshotName(brand, darkTheme, extra))
+ }
+
+ private fun screenshotName(brand: Brand?, darkTheme: Boolean, extra: String) = ScreenshotUtils.getScreenshotName(
+ brand = brand,
+ extra = if (darkTheme) "_dark$extra" else extra
+ )
+}
diff --git a/library/src/test/java/com/telefonica/mistica/testutils/TestUtils.kt b/library/src/test/java/com/telefonica/mistica/testutils/TestUtils.kt
new file mode 100644
index 000000000..b0a24a910
--- /dev/null
+++ b/library/src/test/java/com/telefonica/mistica/testutils/TestUtils.kt
@@ -0,0 +1,24 @@
+package com.telefonica.mistica.testutils
+
+import org.junit.Test
+
+object TestUtils {
+ fun findRunningTestMethodName(): String {
+ val stackTrace = Thread.currentThread().stackTrace
+ for (element in stackTrace) {
+ val methodName = element.methodName
+ val className = element.className
+ val testClass = Class.forName(className)
+ val methods = testClass.declaredMethods
+ for (method in methods) {
+ if (method.name == methodName && method.isAnnotationPresent(Test::class.java)) {
+ return methodName.replace(
+ oldValue = " ",
+ newValue = "_"
+ )
+ }
+ }
+ }
+ throw IllegalStateException("Could not find running test method name")
+ }
+}
\ No newline at end of file
diff --git a/library/src/test/res/layout/test_dummy_activity.xml b/library/src/test/res/layout/test_dummy_activity.xml
new file mode 100644
index 000000000..d032cf331
--- /dev/null
+++ b/library/src/test/res/layout/test_dummy_activity.xml
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/screenshots.md b/screenshots.md
new file mode 100644
index 000000000..d3196f1f0
--- /dev/null
+++ b/screenshots.md
@@ -0,0 +1,42 @@
+## Screenshot testing
+Screenshot tests compares current screenshots of the app with a set of desired ones. To achieve that we need to perform 2 tasks:
+
+ 1. Generate and store a set of desired screenshots (also known as baseline).
+ These screenshots will be generated using a reference device to avoid differences based on Android devices/versions
+ 2. Take screenshots of the app with this reference device and compare them with the baseline.
+ Then generate reports with differences detected
+
+To perform these tasks we use [Roborazzi](https://github.com/takahirom/roborazzi) library which uses gradle tasks to generate baseline and store screenshots
+
+## Add a new screenshot
+To create a new screenshot create a new unitTest with Roboelectric in the test folder of the module to test:
+ @RunWith(RobolectricTestRunner::class)
+or
+ @RunWith(ParameterizedRobolectricTestRunner::class)
+depending if it's a parametrized test or not. Use
+ @RunWith(Enclosed::class)
+in case multiple of them must be on the same file.
+
+Then use `captureRoboImage()` function to take the screenshot and use `ScreenshotUtils.getScreenshotName()` to set the name of the test.:
+
+ composeTestRule.onRoot()
+ .captureRoboImage(ScreenshotUtils.getScreenshotName())
+
+For traditional views run an activity or check `TextInputTest.kt` example on how to use a dummy activity to load them inside.
+
+## Check that changes in the screen are detected
+`Compare Screenshots` job will run in each pr and will run the `verifyRoborazziDebug` gradle task that checks the screenshots. In case this fails it will
+generate a report and upload it to azure.
+
+## Access to screenshots test report
+At the bottom of the summary of the action that has failed you'll see that a report has been uploaded to azure.
+
+Once that compressed file is downloaded an html report is there with the differences detected between the current screenshot (left side) and the
+newly generated one (right side).
+
+## Update screenshots baseline
+Either run the ci job `Update screenshot baseline` that will create the images and perform a commit for you or run the gradle task `recordRoborazziDebug` and
+this way it allows you to check everything is ok before uploading the images.
+
+
+