Browse Source

Improve screenshot testing with ComposablePreviewScanner (#3125)

* Use ComposablePreviewScanner to rework how screenshot testing works
* Add test sharding
* Update screenshots
* Fixes for Element Gallery

---------

Co-authored-by: ElementBot <benoitm+elementbot@element.io>
Co-authored-by: Benoit Marty <benoit@matrix.org>
pull/3133/head
Jorge Martin Espinosa 4 months ago committed by GitHub
parent
commit
b0cebf5ca0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      .github/workflows/scripts/recordScreenshots.sh
  2. 2
      app/build.gradle.kts
  3. 1
      appconfig/build.gradle.kts
  4. 3
      appnav/build.gradle.kts
  5. 8
      build.gradle.kts
  6. 2
      docs/screenshot_testing.md
  7. 3
      features/analytics/api/build.gradle.kts
  8. 2
      features/analytics/impl/build.gradle.kts
  9. 2
      features/call/impl/build.gradle.kts
  10. 3
      features/createroom/impl/build.gradle.kts
  11. 3
      features/ftue/impl/build.gradle.kts
  12. 1
      features/ftue/test/build.gradle.kts
  13. 3
      features/invite/impl/build.gradle.kts
  14. 3
      features/joinroom/impl/build.gradle.kts
  15. 2
      features/leaveroom/api/build.gradle.kts
  16. 2
      features/location/api/build.gradle.kts
  17. 2
      features/location/impl/build.gradle.kts
  18. 2
      features/lockscreen/impl/build.gradle.kts
  19. 2
      features/login/impl/build.gradle.kts
  20. 3
      features/logout/api/build.gradle.kts
  21. 2
      features/logout/impl/build.gradle.kts
  22. 3
      features/messages/impl/build.gradle.kts
  23. 3
      features/migration/impl/build.gradle.kts
  24. 3
      features/networkmonitor/api/build.gradle.kts
  25. 2
      features/onboarding/impl/build.gradle.kts
  26. 3
      features/poll/api/build.gradle.kts
  27. 3
      features/poll/impl/build.gradle.kts
  28. 2
      features/preferences/impl/build.gradle.kts
  29. 3
      features/rageshake/api/build.gradle.kts
  30. 2
      features/rageshake/impl/build.gradle.kts
  31. 3
      features/roomaliasresolver/impl/build.gradle.kts
  32. 3
      features/roomdetails/impl/build.gradle.kts
  33. 3
      features/roomdirectory/impl/build.gradle.kts
  34. 2
      features/roomlist/impl/build.gradle.kts
  35. 3
      features/securebackup/impl/build.gradle.kts
  36. 3
      features/share/impl/build.gradle.kts
  37. 3
      features/signedout/impl/build.gradle.kts
  38. 3
      features/userprofile/impl/build.gradle.kts
  39. 3
      features/userprofile/shared/build.gradle.kts
  40. 3
      features/verifysession/impl/build.gradle.kts
  41. 2
      features/viewfolder/impl/build.gradle.kts
  42. 1
      libraries/dateformatter/impl/build.gradle.kts
  43. 1
      libraries/designsystem/build.gradle.kts
  44. 1
      libraries/featureflag/impl/build.gradle.kts
  45. 2
      libraries/featureflag/ui/build.gradle.kts
  46. 3
      libraries/matrixui/build.gradle.kts
  47. 1
      libraries/mediaupload/api/build.gradle.kts
  48. 1
      libraries/mediaupload/impl/build.gradle.kts
  49. 3
      libraries/mediaviewer/api/build.gradle.kts
  50. 3
      libraries/permissions/api/build.gradle.kts
  51. 3
      libraries/permissions/impl/build.gradle.kts
  52. 2
      libraries/roomselect/impl/build.gradle.kts
  53. 3
      libraries/textcomposer/impl/build.gradle.kts
  54. 2
      libraries/troubleshoot/impl/build.gradle.kts
  55. 2253
      screenshots/html/data.js
  56. 4
      screenshots/html/script.js
  57. 1
      services/analytics/impl/build.gradle.kts
  58. 3
      services/apperror/impl/build.gradle.kts
  59. 1
      services/appnavstate/impl/build.gradle.kts
  60. 7
      settings.gradle.kts
  61. 1
      tests/testutils/build.gradle.kts
  62. 6
      tests/uitests/build.gradle.kts
  63. 2
      tests/uitests/src/test/kotlin/base/BaseDeviceConfig.kt
  64. 63
      tests/uitests/src/test/kotlin/base/ComposablePreviewProvider.kt
  65. 141
      tests/uitests/src/test/kotlin/base/ScreenshotTest.kt
  66. 57
      tests/uitests/src/test/kotlin/translations/TranslationsScreenshotTest.kt
  67. 44
      tests/uitests/src/test/kotlin/ui/ColorTestPreview.kt
  68. 46
      tests/uitests/src/test/kotlin/ui/ComponentTestPreview.kt
  69. 31
      tests/uitests/src/test/kotlin/ui/PreviewProvider.kt
  70. 46
      tests/uitests/src/test/kotlin/ui/PreviewShard1Test.kt
  71. 46
      tests/uitests/src/test/kotlin/ui/PreviewShard2Test.kt
  72. 46
      tests/uitests/src/test/kotlin/ui/PreviewShard3Test.kt
  73. 46
      tests/uitests/src/test/kotlin/ui/PreviewShard4Test.kt
  74. 49
      tests/uitests/src/test/kotlin/ui/S.kt
  75. 125
      tests/uitests/src/test/kotlin/ui/ScreenshotTest.kt
  76. 52
      tests/uitests/src/test/kotlin/ui/T.kt
  77. 41
      tests/uitests/src/test/kotlin/ui/TestPreview.kt
  78. 50
      tests/uitests/src/test/kotlin/ui/TypographyTestPreview.kt
  79. 0
      tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_0_en.png
  80. 0
      tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_1_en.png
  81. 0
      tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_2_en.png
  82. 0
      tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_0_en.png
  83. 0
      tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_1_en.png
  84. 0
      tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_2_en.png
  85. 0
      tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateView_Day_0_en.png
  86. 0
      tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateView_Night_0_en.png
  87. 0
      tests/uitests/src/test/snapshots/images/appnav.room.joined_LoadingRoomNodeView_Day_0_en.png
  88. 0
      tests/uitests/src/test/snapshots/images/appnav.room.joined_LoadingRoomNodeView_Day_1_en.png
  89. 0
      tests/uitests/src/test/snapshots/images/appnav.room.joined_LoadingRoomNodeView_Night_0_en.png
  90. 0
      tests/uitests/src/test/snapshots/images/appnav.room.joined_LoadingRoomNodeView_Night_1_en.png
  91. 0
      tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_0_en.png
  92. 0
      tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_1_en.png
  93. 0
      tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_2_en.png
  94. 0
      tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_0_en.png
  95. 0
      tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_1_en.png
  96. 0
      tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_2_en.png
  97. 0
      tests/uitests/src/test/snapshots/images/features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en.png
  98. 0
      tests/uitests/src/test/snapshots/images/features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en.png
  99. 0
      tests/uitests/src/test/snapshots/images/features.analytics.impl_AnalyticsOptInView_Day_0_en.png
  100. 0
      tests/uitests/src/test/snapshots/images/features.analytics.impl_AnalyticsOptInView_Night_0_en.png
  101. Some files were not shown because too many files have changed in this diff Show More

2
.github/workflows/scripts/recordScreenshots.sh

@ -65,7 +65,7 @@ echo "Deleting previous screenshots" @@ -65,7 +65,7 @@ echo "Deleting previous screenshots"
./gradlew removeOldSnapshots --stacktrace --warn
echo "Record screenshots"
./gradlew recordPaparazziDebug --stacktrace --warn
./gradlew recordPaparazziDebug --stacktrace
echo "Committing changes"
git config http.sslVerify false

2
app/build.gradle.kts

@ -31,7 +31,6 @@ plugins { @@ -31,7 +31,6 @@ plugins {
id("io.element.android-compose-application")
alias(libs.plugins.kotlin.android)
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
alias(libs.plugins.kapt)
// When using precompiled plugins, we need to apply the firebase plugin like this
id(libs.plugins.firebaseAppDistribution.get().pluginId)
@ -283,6 +282,5 @@ dependencies { @@ -283,6 +282,5 @@ dependencies {
testImplementation(libs.test.turbine)
testImplementation(projects.libraries.matrix.test)
ksp(libs.showkase.processor)
koverDependencies()
}

1
appconfig/build.gradle.kts

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
plugins {
id("io.element.android-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
}
android {

3
appnav/build.gradle.kts

@ -21,7 +21,6 @@ import extension.allFeaturesApi @@ -21,7 +21,6 @@ import extension.allFeaturesApi
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
alias(libs.plugins.kapt)
id("kotlin-parcelize")
}
@ -78,6 +77,4 @@ dependencies { @@ -78,6 +77,4 @@ dependencies {
testImplementation(projects.services.analytics.test)
testImplementation(libs.test.appyx.junit)
testImplementation(libs.test.arch.core)
ksp(libs.showkase.processor)
}

8
build.gradle.kts

@ -160,15 +160,15 @@ allprojects { @@ -160,15 +160,15 @@ allprojects {
// Record all the languages?
if (project.hasProperty("allLanguagesNoEnglish")) {
// Do not record English language
exclude("ui/S.class")
exclude("ui/*.class")
} else if (project.hasProperty("allLanguages").not()) {
// Do not record other languages
exclude("ui/T.class")
exclude("translations/*.class")
}
} else {
// Disable screenshot tests by default
exclude("ui/S.class")
exclude("ui/T.class")
exclude("ui/*.class")
exclude("translations/*.class")
}
}
}

2
docs/screenshot_testing.md

@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
## Overview
- Screenshot tests are tests which record the content of a rendered screen and verify subsequent runs to check if the screen renders differently.
- Element X uses [Paparazzi](https://github.com/cashapp/paparazzi) to render, record and verify Composable. All Composable Preview will be use to make screenshot test, thanks to the usage of [Showkase](https://github.com/airbnb/Showkase).
- Element X uses [Paparazzi](https://github.com/cashapp/paparazzi) to render, record and verify Composables. All internal/public Composable Preview will be used for screenshot tests, thanks to the usage of [ComposablePreviewScanner](https://github.com/sergio-sastre/ComposablePreviewScanner).
- The screenshot verification occurs on every pull request as part of the `tests.yml` workflow.
## Setup

3
features/analytics/api/build.gradle.kts

@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
*/
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
}
android {
@ -27,6 +26,4 @@ dependencies { @@ -27,6 +26,4 @@ dependencies {
implementation(projects.libraries.designsystem)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.uiStrings)
ksp(libs.showkase.processor)
}

2
features/analytics/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -42,7 +41,6 @@ dependencies { @@ -42,7 +41,6 @@ dependencies {
implementation(projects.appconfig)
implementation(libs.androidx.datastore.preferences)
implementation(libs.androidx.browser)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)

2
features/call/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
alias(libs.plugins.kotlin.serialization)
}
@ -57,7 +56,6 @@ dependencies { @@ -57,7 +56,6 @@ dependencies {
implementation(libs.network.retrofit)
implementation(libs.serialization.json)
api(projects.features.call.api)
ksp(libs.showkase.processor)
testImplementation(libs.coroutines.test)
testImplementation(libs.molecule.runtime)

3
features/createroom/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -71,6 +70,4 @@ dependencies { @@ -71,6 +70,4 @@ dependencies {
testImplementation(projects.tests.testutils)
testImplementation(libs.androidx.compose.ui.test.junit)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
ksp(libs.showkase.processor)
}

3
features/ftue/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -64,6 +63,4 @@ dependencies { @@ -64,6 +63,4 @@ dependencies {
testImplementation(projects.features.lockscreen.test)
testImplementation(projects.services.toolbox.test)
testImplementation(projects.tests.testutils)
ksp(libs.showkase.processor)
}

1
features/ftue/test/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}

3
features/invite/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -52,6 +51,4 @@ dependencies { @@ -52,6 +51,4 @@ dependencies {
testImplementation(projects.libraries.push.test)
testImplementation(projects.services.analytics.test)
testImplementation(projects.tests.testutils)
ksp(libs.showkase.processor)
}

3
features/joinroom/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -59,6 +58,4 @@ dependencies { @@ -59,6 +58,4 @@ dependencies {
testImplementation(projects.tests.testutils)
testImplementation(libs.androidx.compose.ui.test.junit)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
ksp(libs.showkase.processor)
}

2
features/leaveroom/api/build.gradle.kts

@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
*/
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
}
android {
@ -27,5 +26,4 @@ dependencies { @@ -27,5 +26,4 @@ dependencies {
implementation(projects.libraries.designsystem)
implementation(projects.libraries.uiStrings)
implementation(projects.libraries.matrix.api)
ksp(libs.showkase.processor)
}

2
features/location/api/build.gradle.kts

@ -18,7 +18,6 @@ import java.util.Properties @@ -18,7 +18,6 @@ import java.util.Properties
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -66,7 +65,6 @@ dependencies { @@ -66,7 +65,6 @@ dependencies {
implementation(projects.libraries.matrixui)
implementation(projects.libraries.uiStrings)
implementation(libs.coil.compose)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.test.truth)

2
features/location/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
}
android {
@ -51,7 +50,6 @@ dependencies { @@ -51,7 +50,6 @@ dependencies {
implementation(libs.dagger)
implementation(projects.anvilannotations)
anvil(projects.anvilcodegen)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)

2
features/lockscreen/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -30,7 +29,6 @@ anvil { @@ -30,7 +29,6 @@ anvil {
}
dependencies {
ksp(libs.showkase.processor)
implementation(projects.anvilannotations)
anvil(projects.anvilcodegen)
api(projects.features.lockscreen.api)

2
features/login/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
alias(libs.plugins.kotlin.serialization)
}
@ -56,7 +55,6 @@ dependencies { @@ -56,7 +55,6 @@ dependencies {
implementation(libs.network.retrofit)
implementation(libs.serialization.json)
api(projects.features.login.api)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.androidx.compose.ui.test.junit)

3
features/logout/api/build.gradle.kts

@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
*/
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
}
android {
@ -26,6 +25,4 @@ dependencies { @@ -26,6 +25,4 @@ dependencies {
implementation(projects.libraries.architecture)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.uiStrings)
ksp(libs.showkase.processor)
}

2
features/logout/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
}
android {
@ -47,7 +46,6 @@ dependencies { @@ -47,7 +46,6 @@ dependencies {
implementation(projects.libraries.uiStrings)
implementation(projects.libraries.dateformatter.api)
api(projects.features.logout.api)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)

3
features/messages/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -104,6 +103,4 @@ dependencies { @@ -104,6 +103,4 @@ dependencies {
testImplementation(projects.features.poll.impl)
testImplementation(libs.androidx.compose.ui.test.junit)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
ksp(libs.showkase.processor)
}

3
features/migration/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
}
android {
@ -35,8 +34,6 @@ dependencies { @@ -35,8 +34,6 @@ dependencies {
implementation(projects.libraries.sessionStorage.api)
implementation(projects.libraries.uiStrings)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)
testImplementation(libs.molecule.runtime)

3
features/networkmonitor/api/build.gradle.kts

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
}
android {
@ -27,6 +26,4 @@ dependencies { @@ -27,6 +26,4 @@ dependencies {
implementation(libs.coroutines.core)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.uiStrings)
ksp(libs.showkase.processor)
}

2
features/onboarding/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -48,7 +47,6 @@ dependencies { @@ -48,7 +47,6 @@ dependencies {
implementation(projects.libraries.testtags)
implementation(projects.libraries.uiStrings)
api(projects.features.onboarding.api)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.androidx.compose.ui.test.junit)

3
features/poll/api/build.gradle.kts

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
}
android {
@ -28,6 +27,4 @@ dependencies { @@ -28,6 +27,4 @@ dependencies {
implementation(projects.libraries.designsystem)
implementation(projects.libraries.uiStrings)
implementation(projects.libraries.matrix.api)
ksp(libs.showkase.processor)
}

3
features/poll/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -62,6 +61,4 @@ dependencies { @@ -62,6 +61,4 @@ dependencies {
testImplementation(projects.features.poll.test)
testImplementation(libs.androidx.compose.ui.test.junit)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
ksp(libs.showkase.processor)
}

2
features/preferences/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -72,7 +71,6 @@ dependencies { @@ -72,7 +71,6 @@ dependencies {
implementation(libs.androidx.browser)
implementation(libs.androidx.datastore.preferences)
api(projects.features.preferences.api)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)

3
features/rageshake/api/build.gradle.kts

@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
*/
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
}
android {
@ -27,6 +26,4 @@ dependencies { @@ -27,6 +26,4 @@ dependencies {
implementation(projects.libraries.designsystem)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.uiStrings)
ksp(libs.showkase.processor)
}

2
features/rageshake/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -55,7 +54,6 @@ dependencies { @@ -55,7 +54,6 @@ dependencies {
implementation(libs.network.okhttp.okhttp)
implementation(libs.coil)
implementation(libs.coil.compose)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.test.robolectric)

3
features/roomaliasresolver/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -56,6 +55,4 @@ dependencies { @@ -56,6 +55,4 @@ dependencies {
testImplementation(projects.tests.testutils)
testImplementation(libs.androidx.compose.ui.test.junit)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
ksp(libs.showkase.processor)
}

3
features/roomdetails/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -82,6 +81,4 @@ dependencies { @@ -82,6 +81,4 @@ dependencies {
testImplementation(projects.services.analytics.test)
testImplementation(libs.androidx.compose.ui.test.junit)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
ksp(libs.showkase.processor)
}

3
features/roomdirectory/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -58,6 +57,4 @@ dependencies { @@ -58,6 +57,4 @@ dependencies {
testImplementation(libs.test.turbine)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.tests.testutils)
ksp(libs.showkase.processor)
}

2
features/roomlist/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -62,7 +61,6 @@ dependencies { @@ -62,7 +61,6 @@ dependencies {
implementation(projects.services.analytics.api)
implementation(libs.androidx.datastore.preferences)
api(projects.features.roomlist.api)
ksp(libs.showkase.processor)
testImplementation(libs.androidx.compose.ui.test.junit)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)

3
features/securebackup/impl/build.gradle.kts

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
alias(libs.plugins.anvil)
id("kotlin-parcelize")
}
@ -61,6 +60,4 @@ dependencies { @@ -61,6 +60,4 @@ dependencies {
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.tests.testutils)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
ksp(libs.showkase.processor)
}

3
features/share/impl/build.gradle.kts

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
alias(libs.plugins.anvil)
id("kotlin-parcelize")
}
@ -65,6 +64,4 @@ dependencies { @@ -65,6 +64,4 @@ dependencies {
testImplementation(projects.libraries.mediaupload.test)
testImplementation(projects.tests.testutils)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
ksp(libs.showkase.processor)
}

3
features/signedout/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -48,6 +47,4 @@ dependencies { @@ -48,6 +47,4 @@ dependencies {
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.sessionStorage.implMemory)
testImplementation(projects.tests.testutils)
ksp(libs.showkase.processor)
}

3
features/userprofile/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -65,6 +64,4 @@ dependencies { @@ -65,6 +64,4 @@ dependencies {
testImplementation(projects.tests.testutils)
testImplementation(libs.androidx.compose.ui.test.junit)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
ksp(libs.showkase.processor)
}

3
features/userprofile/shared/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -66,6 +65,4 @@ dependencies { @@ -66,6 +65,4 @@ dependencies {
testImplementation(projects.tests.testutils)
testImplementation(libs.androidx.compose.ui.test.junit)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
ksp(libs.showkase.processor)
}

3
features/verifysession/impl/build.gradle.kts

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
alias(libs.plugins.anvil)
}
@ -58,6 +57,4 @@ dependencies { @@ -58,6 +57,4 @@ dependencies {
testImplementation(projects.tests.testutils)
testImplementation(libs.androidx.compose.ui.test.junit)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
ksp(libs.showkase.processor)
}

2
features/viewfolder/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -38,7 +37,6 @@ dependencies { @@ -38,7 +37,6 @@ dependencies {
implementation(projects.libraries.designsystem)
implementation(projects.libraries.uiStrings)
api(projects.features.viewfolder.api)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.test.robolectric)

1
libraries/dateformatter/impl/build.gradle.kts

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
plugins {
id("io.element.android-library")
alias(libs.plugins.ksp)
alias(libs.plugins.anvil)
}

1
libraries/designsystem/build.gradle.kts

@ -46,7 +46,6 @@ android { @@ -46,7 +46,6 @@ android {
implementation(projects.libraries.uiStrings)
ksp(libs.showkase.processor)
kspTest(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)

1
libraries/featureflag/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}

2
libraries/featureflag/ui/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -33,5 +32,4 @@ dependencies { @@ -33,5 +32,4 @@ dependencies {
implementation(projects.anvilannotations)
anvil(projects.anvilcodegen)
implementation(projects.libraries.designsystem)
ksp(libs.showkase.processor)
}

3
libraries/matrixui/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -49,8 +48,6 @@ dependencies { @@ -49,8 +48,6 @@ dependencies {
implementation(libs.coil.gif)
implementation(libs.jsoup)
ksp(libs.showkase.processor)
testImplementation(libs.coroutines.test)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)

1
libraries/mediaupload/api/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
}
android {

1
libraries/mediaupload/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
}
android {

3
libraries/mediaviewer/api/build.gradle.kts

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -67,6 +66,4 @@ dependencies { @@ -67,6 +66,4 @@ dependencies {
testImplementation(libs.coroutines.test)
testImplementation(libs.androidx.compose.ui.test.junit)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
ksp(libs.showkase.processor)
}

3
libraries/permissions/api/build.gradle.kts

@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
*/
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
}
android {
@ -26,6 +25,4 @@ dependencies { @@ -26,6 +25,4 @@ dependencies {
implementation(projects.libraries.architecture)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.uiStrings)
ksp(libs.showkase.processor)
}

3
libraries/permissions/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
}
android {
@ -61,6 +60,4 @@ dependencies { @@ -61,6 +60,4 @@ dependencies {
testImplementation(projects.libraries.permissions.test)
testImplementation(projects.services.toolbox.test)
testImplementation(projects.tests.testutils)
ksp(libs.showkase.processor)
}

2
libraries/roomselect/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
@ -40,7 +39,6 @@ dependencies { @@ -40,7 +39,6 @@ dependencies {
implementation(projects.libraries.designsystem)
implementation(projects.libraries.uiStrings)
api(projects.libraries.roomselect.api)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)

3
libraries/textcomposer/impl/build.gradle.kts

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
alias(libs.plugins.anvil)
id("kotlin-parcelize")
}
@ -54,8 +53,6 @@ dependencies { @@ -54,8 +53,6 @@ dependencies {
debugApi(libs.matrix.richtexteditor.compose)
}
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)
testImplementation(libs.molecule.runtime)

2
libraries/troubleshoot/impl/build.gradle.kts

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
alias(libs.plugins.kotlin.serialization)
}
@ -44,7 +43,6 @@ dependencies { @@ -44,7 +43,6 @@ dependencies {
api(projects.libraries.troubleshoot.api)
api(projects.libraries.push.api)
implementation(projects.services.analytics.api)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.test.robolectric)

2253
screenshots/html/data.js

File diff suppressed because it is too large Load Diff

4
screenshots/html/script.js

@ -183,7 +183,7 @@ function getNiceName(name) { @@ -183,7 +183,7 @@ function getNiceName(name) {
for(var i = 0; i < name.length; i++) {
if (name[i] === "_") indices.push(i);
}
return name.substring(indices[2] + 1, indices[3]);
return name.substring(indices[0] + 1, indices[1]);
}
function createMissingImageElement() {
@ -276,7 +276,7 @@ function addTable() { @@ -276,7 +276,7 @@ function addTable() {
} else if(modifiedDayTime >= minModifiedDayTime) {
hasTranslatedFiles = true;
// Foreign file is the same as the english file, replacing the language
const foreignFile = englishFile.replace("en]", `${dataLanguages[languageIndex]}]`).replace("_S_", "_T_")
const foreignFile = englishFile.substring(0, englishFile.length-2) + dataLanguages[languageIndex];
const fullForeignFile = `${dataLanguages[languageIndex]}/${foreignFile}.png`;
td.appendChild(createImageElement(fullForeignFile, modifiedDayTime));
} else {

1
services/analytics/impl/build.gradle.kts

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}

3
services/apperror/impl/build.gradle.kts

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
alias(libs.plugins.anvil)
}
@ -46,6 +45,4 @@ dependencies { @@ -46,6 +45,4 @@ dependencies {
testImplementation(libs.coroutines.test)
testImplementation(libs.test.turbine)
testImplementation(libs.test.truth)
ksp(libs.showkase.processor)
}

1
services/appnavstate/impl/build.gradle.kts

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
plugins {
id("io.element.android-library")
alias(libs.plugins.ksp)
alias(libs.plugins.anvil)
}

7
settings.gradle.kts

@ -27,6 +27,13 @@ pluginManagement { @@ -27,6 +27,13 @@ pluginManagement {
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
maven {
url = URI("https://jitpack.io")
content {
includeModule("com.github.sergio-sastre.ComposablePreviewScanner", "android")
includeModule("com.github.sergio-sastre.ComposablePreviewScanner", "core")
}
}
// Snapshot versions
maven {
url = URI("https://s01.oss.sonatype.org/content/repositories/snapshots")

1
tests/testutils/build.gradle.kts

@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
*/
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
}
android {

6
tests/uitests/build.gradle.kts

@ -20,7 +20,6 @@ import extension.allServicesImpl @@ -20,7 +20,6 @@ import extension.allServicesImpl
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
alias(libs.plugins.paparazzi)
}
@ -58,9 +57,6 @@ dependencies { @@ -58,9 +57,6 @@ dependencies {
)
}
ksp(libs.showkase.processor)
kspTest(libs.showkase.processor)
implementation(libs.showkase)
// TODO There is a Resources.NotFoundException maybe due to the mipmap, even if we have
@ -70,4 +66,6 @@ dependencies { @@ -70,4 +66,6 @@ dependencies {
allLibrariesImpl()
allServicesImpl()
allFeaturesImpl(rootDir, logger)
testImplementation("com.github.sergio-sastre.ComposablePreviewScanner:android:0.1.2")
}

2
tests/uitests/src/test/kotlin/ui/BaseDeviceConfig.kt → tests/uitests/src/test/kotlin/base/BaseDeviceConfig.kt

@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
* limitations under the License.
*/
package ui
package base
import app.cash.paparazzi.DeviceConfig

63
tests/uitests/src/test/kotlin/base/ComposablePreviewProvider.kt

@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("DEPRECATION")
package base
import com.google.testing.junit.testparameterinjector.TestParameter
import sergio.sastre.composable.preview.scanner.android.AndroidComposablePreviewScanner
import sergio.sastre.composable.preview.scanner.android.AndroidPreviewInfo
import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview
object ComposablePreviewProvider : TestParameter.TestParameterValuesProvider {
private val values: List<IndexedValue<ComposablePreview<AndroidPreviewInfo>>> by lazy {
AndroidComposablePreviewScanner()
.scanPackageTrees(
"io.element.android.features",
"io.element.android.libraries",
"io.element.android.services",
"io.element.android.appnav",
"io.element.android.x",
// Make sure we don't import Compound previews by mistake
)
.getPreviews()
.withIndex()
.toList()
}
override fun provideValues(): List<IndexedValue<ComposablePreview<AndroidPreviewInfo>>> = values
}
object Shard1ComposablePreviewProvider : TestParameter.TestParameterValuesProvider {
override fun provideValues(): List<ComposablePreview<AndroidPreviewInfo>> =
ComposablePreviewProvider.provideValues().filter { it.index % 4 == 0 }.map { it.value }
}
object Shard2ComposablePreviewProvider : TestParameter.TestParameterValuesProvider {
override fun provideValues(): List<ComposablePreview<AndroidPreviewInfo>> =
ComposablePreviewProvider.provideValues().filter { it.index % 4 == 1 }.map { it.value }
}
object Shard3ComposablePreviewProvider : TestParameter.TestParameterValuesProvider {
override fun provideValues(): List<ComposablePreview<AndroidPreviewInfo>> =
ComposablePreviewProvider.provideValues().filter { it.index % 4 == 2 }.map { it.value }
}
object Shard4ComposablePreviewProvider : TestParameter.TestParameterValuesProvider {
override fun provideValues(): List<ComposablePreview<AndroidPreviewInfo>> =
ComposablePreviewProvider.provideValues().filter { it.index % 4 == 3 }.map { it.value }
}

141
tests/uitests/src/test/kotlin/base/ScreenshotTest.kt

@ -0,0 +1,141 @@ @@ -0,0 +1,141 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package base
import android.content.res.Configuration
import android.os.LocaleList
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.unit.Density
import app.cash.paparazzi.DeviceConfig
import app.cash.paparazzi.Paparazzi
import app.cash.paparazzi.TestName
import com.android.resources.NightMode
import io.element.android.compound.theme.ElementTheme
import sergio.sastre.composable.preview.scanner.android.AndroidPreviewInfo
import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview
import java.util.Locale
object ScreenshotTest {
val defaultDeviceConfig = BaseDeviceConfig.NEXUS_5.deviceConfig
fun runTest(
paparazzi: Paparazzi,
preview: ComposablePreview<AndroidPreviewInfo>,
localeStr: String,
) {
val locale = localeStr.toLocale()
// Needed for regional settings, as first day of week
Locale.setDefault(locale)
paparazzi.fixScreenshotName(preview, localeStr)
paparazzi.snapshot {
CompositionLocalProvider(
LocalInspectionMode provides true,
LocalDensity provides Density(
density = LocalDensity.current.density,
fontScale = 1.0f,
),
LocalConfiguration provides Configuration().apply {
setLocales(LocaleList(locale))
uiMode = preview.previewInfo.uiMode
},
) {
ElementTheme {
Box(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
) {
preview()
}
}
}
}
}
}
private val testNameField = Paparazzi::class.java.getDeclaredField("testName").apply {
isAccessible = true
}
private fun Paparazzi.fixScreenshotName(preview: ComposablePreview<AndroidPreviewInfo>, locale: String) {
val id = listOf(createScreenshotIdFor(preview), locale)
.filter { it.isNotEmpty() }
.joinToString("_")
val packageName = preview.declaringClass
// Remove common prefix
.replace("io.element.android.", "")
.split(".")
// Remove class name
.dropLast(1)
.joinToString(".")
val testName = TestName(
packageName = packageName,
className = preview.methodName.replace("Preview", ""),
methodName = id
)
testNameField.set(this, testName)
}
private fun String.toLocale(): Locale {
return when (this) {
"en" -> Locale.US
"fr" -> Locale.FRANCE
"de" -> Locale.GERMAN
else -> Locale.Builder().setLanguage(this).build()
}
}
fun createScreenshotIdFor(preview: ComposablePreview<AndroidPreviewInfo>) = buildList {
// `name` here can be `Day`, `Night`, or nothing at all
if (preview.previewInfo.name.isNotEmpty()) {
add(preview.previewInfo.name)
}
if (preview.previewInfo.group.isNotEmpty()) {
add(preview.previewInfo.group)
}
// If it's a day/night preview, we should add an index to be consistent even if there is only version of this composable
val needsIndex = preview.previewInfo.name == "Day" || preview.previewInfo.name == "Night"
if (preview.previewIndex != null || needsIndex) {
add((preview.previewIndex ?: 0).toString())
}
}.joinToString("_")
object PaparazziPreviewRule {
fun createFor(preview: ComposablePreview<AndroidPreviewInfo>, locale: String, deviceConfig: DeviceConfig = ScreenshotTest.defaultDeviceConfig): Paparazzi {
val densityScale = deviceConfig.density.dpiValue / 160f
val customScreenHeight = preview.previewInfo.heightDp.takeIf { it >= 0 }?.let { it * densityScale }?.toInt()
return Paparazzi(
deviceConfig = deviceConfig.copy(
nightMode = when (preview.previewInfo.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES) {
true -> NightMode.NIGHT
false -> NightMode.NOTNIGHT
},
locale = locale,
softButtons = false,
screenHeight = customScreenHeight ?: deviceConfig.screenHeight,
),
maxPercentDifference = 0.01
)
}
}

57
tests/uitests/src/test/kotlin/translations/TranslationsScreenshotTest.kt

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package translations
import android.content.res.Configuration
import base.ComposablePreviewProvider
import base.PaparazziPreviewRule
import base.ScreenshotTest
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import sergio.sastre.composable.preview.scanner.android.AndroidPreviewInfo
import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview
/**
* Test that takes a preview and a locale and runs a screenshot test on it.
*/
@RunWith(TestParameterInjector::class)
class TranslationsScreenshotTest(
@TestParameter(valuesProvider = ComposablePreviewProvider::class)
val indexedPreview: IndexedValue<ComposablePreview<AndroidPreviewInfo>>,
@TestParameter(value = ["de"])
val localeStr: String,
) {
@get:Rule
val paparazziRule = PaparazziPreviewRule.createFor(indexedPreview.value, locale = localeStr)
@Test
fun snapshot() {
val (_, preview) = indexedPreview
// Skip for dark mode screenshots
if (preview.previewInfo.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES) {
return
}
// Skip for design system screenshots
if (preview.previewInfo.name.startsWith("io.element.android.libraries.designsystem")) {
return
}
ScreenshotTest.runTest(paparazzi = paparazziRule, preview = preview, localeStr = localeStr)
}
}

44
tests/uitests/src/test/kotlin/ui/ColorTestPreview.kt

@ -1,44 +0,0 @@ @@ -1,44 +0,0 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.airbnb.android.showkase.models.ShowkaseBrowserColor
class ColorTestPreview(
private val showkaseBrowserColor: ShowkaseBrowserColor
) : TestPreview {
@Composable
override fun Content() {
Box(
modifier = Modifier
.fillMaxWidth()
.height(250.dp)
.background(showkaseBrowserColor.color)
)
}
override val name: String = showkaseBrowserColor.colorName
override fun toString(): String = "Color_${showkaseBrowserColor.colorGroup}_${showkaseBrowserColor.colorName}"
}

46
tests/uitests/src/test/kotlin/ui/ComponentTestPreview.kt

@ -1,46 +0,0 @@ @@ -1,46 +0,0 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ui
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.Dp
import com.airbnb.android.showkase.models.ShowkaseBrowserComponent
class ComponentTestPreview(
private val showkaseBrowserComponent: ShowkaseBrowserComponent
) : TestPreview {
@Composable
override fun Content() = showkaseBrowserComponent.component()
override val name: String = showkaseBrowserComponent.componentName
override fun customHeightDp(): Dp? {
return showkaseBrowserComponent.heightDp?.let { Dp(it.toFloat()) }
}
override fun toString(): String = showkaseBrowserComponent.componentKey
// Strip common package beginning
.replace("io.element.android.features.", "f.")
.replace("io.element.android.libraries.", "l.")
.replace("io.element.android.", "")
// Reduce default group (if present)
.replace("_DefaultGroup_", "_")
// No need to include `Preview` suffix of function name
.replace("Preview_", "_")
// Also for preview annotated with @ElementPreview
.replace("Preview-", "-")
}

31
tests/uitests/src/test/kotlin/ui/PreviewProvider.kt

@ -1,31 +0,0 @@ @@ -1,31 +0,0 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ui
import com.airbnb.android.showkase.models.Showkase
import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider
object PreviewProvider : TestParameterValuesProvider() {
override fun provideValues(context: Context): List<TestPreview> {
val metadata = Showkase.getMetadata()
val components = metadata.componentList.map(::ComponentTestPreview)
val colors = metadata.colorList.map(::ColorTestPreview)
val typography = metadata.typographyList.map(::TypographyTestPreview)
return (components + colors + typography).filter { !it.toString().contains("compound") }
}
}

46
tests/uitests/src/test/kotlin/ui/PreviewShard1Test.kt

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ui
import base.PaparazziPreviewRule
import base.ScreenshotTest
import base.Shard1ComposablePreviewProvider
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import sergio.sastre.composable.preview.scanner.android.AndroidPreviewInfo
import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview
/**
* Test that takes a preview and runs a screenshot test on it.
* It uses a sharded preview provider so multiple 'shards' can run in parallel, optimizing CPU and time usage.
*/
@RunWith(TestParameterInjector::class)
class PreviewShard1Test(
@TestParameter(valuesProvider = Shard1ComposablePreviewProvider::class)
val preview: ComposablePreview<AndroidPreviewInfo>,
) {
@get:Rule
val paparazziRule = PaparazziPreviewRule.createFor(preview, locale = "en")
@Test
fun snapshot() {
ScreenshotTest.runTest(paparazzi = paparazziRule, preview = preview, localeStr = "en")
}
}

46
tests/uitests/src/test/kotlin/ui/PreviewShard2Test.kt

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ui
import base.PaparazziPreviewRule
import base.ScreenshotTest
import base.Shard2ComposablePreviewProvider
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import sergio.sastre.composable.preview.scanner.android.AndroidPreviewInfo
import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview
/**
* Test that takes a preview and runs a screenshot test on it.
* It uses a sharded preview provider so multiple 'shards' can run in parallel, optimizing CPU and time usage.
*/
@RunWith(TestParameterInjector::class)
class PreviewShard2Test(
@TestParameter(valuesProvider = Shard2ComposablePreviewProvider::class)
val preview: ComposablePreview<AndroidPreviewInfo>,
) {
@get:Rule
val paparazziRule = PaparazziPreviewRule.createFor(preview, locale = "en")
@Test
fun snapshot() {
ScreenshotTest.runTest(paparazzi = paparazziRule, preview = preview, localeStr = "en")
}
}

46
tests/uitests/src/test/kotlin/ui/PreviewShard3Test.kt

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ui
import base.PaparazziPreviewRule
import base.ScreenshotTest
import base.Shard3ComposablePreviewProvider
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import sergio.sastre.composable.preview.scanner.android.AndroidPreviewInfo
import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview
/**
* Test that takes a preview and runs a screenshot test on it.
* It uses a sharded preview provider so multiple 'shards' can run in parallel, optimizing CPU and time usage.
*/
@RunWith(TestParameterInjector::class)
class PreviewShard3Test(
@TestParameter(valuesProvider = Shard3ComposablePreviewProvider::class)
val preview: ComposablePreview<AndroidPreviewInfo>,
) {
@get:Rule
val paparazziRule = PaparazziPreviewRule.createFor(preview, locale = "en")
@Test
fun snapshot() {
ScreenshotTest.runTest(paparazzi = paparazziRule, preview = preview, localeStr = "en")
}
}

46
tests/uitests/src/test/kotlin/ui/PreviewShard4Test.kt

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ui
import base.PaparazziPreviewRule
import base.ScreenshotTest
import base.Shard4ComposablePreviewProvider
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import sergio.sastre.composable.preview.scanner.android.AndroidPreviewInfo
import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview
/**
* Test that takes a preview and runs a screenshot test on it.
* It uses a sharded preview provider so multiple 'shards' can run in parallel, optimizing CPU and time usage.
*/
@RunWith(TestParameterInjector::class)
class PreviewShard4Test(
@TestParameter(valuesProvider = Shard4ComposablePreviewProvider::class)
val preview: ComposablePreview<AndroidPreviewInfo>,
) {
@get:Rule
val paparazziRule = PaparazziPreviewRule.createFor(preview, locale = "en")
@Test
fun snapshot() {
ScreenshotTest.runTest(paparazzi = paparazziRule, preview = preview, localeStr = "en")
}
}

49
tests/uitests/src/test/kotlin/ui/S.kt

@ -1,49 +0,0 @@ @@ -1,49 +0,0 @@
/*
* Copyright 2022 The Android Open Source Project
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ui
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import org.junit.Test
import org.junit.runner.RunWith
/**
* Screenshot test for the English version only.
*/
@RunWith(TestParameterInjector::class)
class S : ScreenshotTest() {
/**
* *Note*: keep the method name as short as possible to get shorter filename for generated screenshot.
* Long name was preview_test.
*/
@Test
fun t(
@TestParameter(valuesProvider = PreviewProvider::class) componentTestPreview: TestPreview,
@TestParameter baseDeviceConfig: BaseDeviceConfig,
@TestParameter(value = ["1.0"]) fontScale: Float,
// Need to keep the TestParameter to have filename including the language.
@TestParameter(value = ["en"]) localeStr: String,
) {
doTest(
componentTestPreview = componentTestPreview,
baseDeviceConfig = baseDeviceConfig,
fontScale = fontScale,
localeStr = localeStr,
)
}
}

125
tests/uitests/src/test/kotlin/ui/ScreenshotTest.kt

@ -1,125 +0,0 @@ @@ -1,125 +0,0 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ui
import android.content.res.Configuration
import android.os.LocaleList
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.unit.Density
import androidx.lifecycle.Lifecycle
import app.cash.paparazzi.Paparazzi
import app.cash.paparazzi.detectEnvironment
import com.android.ide.common.rendering.api.SessionParams
import com.android.resources.NightMode
import io.element.android.compound.theme.ElementTheme
import org.junit.Rule
import java.util.Locale
/**
* BMA: Inspired from https://github.com/airbnb/Showkase/blob/master/showkase-screenshot-testing-paparazzi-sample/src/test/java/com/airbnb/android/showkase/screenshot/testing/paparazzi/sample/PaparazziSampleScreenshotTest.kt
*
* Credit to Alex Vanyo for creating this sample in the Now In Android app by Google.
* PR here - https://github.com/android/nowinandroid/pull/101. Modified the test from that PR to
* my own needs for this sample.
*
* *Note*: keep the class name as short as possible to get shorter filename for generated screenshot.
* Long name was ScreenshotTest.
*/
open class ScreenshotTest {
@get:Rule
val paparazzi = Paparazzi(
environment = detectEnvironment(),
maxPercentDifference = 0.01,
renderingMode = SessionParams.RenderingMode.NORMAL,
)
protected fun doTest(
componentTestPreview: TestPreview,
baseDeviceConfig: BaseDeviceConfig,
fontScale: Float,
localeStr: String,
) {
val locale = localeStr.toLocale()
Locale.setDefault(locale) // Needed for regional settings, as first day of week
val densityScale = baseDeviceConfig.deviceConfig.density.dpiValue / 160f
val customScreenHeight = componentTestPreview.customHeightDp()?.value?.let { it * densityScale }?.toInt()
paparazzi.unsafeUpdateConfig(
deviceConfig = baseDeviceConfig.deviceConfig.copy(
softButtons = false,
locale = localeStr,
nightMode = componentTestPreview.isNightMode().let {
when (it) {
true -> NightMode.NIGHT
false -> NightMode.NOTNIGHT
}
},
screenHeight = customScreenHeight ?: baseDeviceConfig.deviceConfig.screenHeight,
),
)
paparazzi.snapshot {
val lifecycleOwner = LocalLifecycleOwner.current
CompositionLocalProvider(
LocalInspectionMode provides true,
LocalDensity provides Density(
density = LocalDensity.current.density,
fontScale = fontScale
),
LocalConfiguration provides Configuration().apply {
setLocales(LocaleList(locale))
uiMode = when (componentTestPreview.isNightMode()) {
true -> Configuration.UI_MODE_NIGHT_YES
false -> Configuration.UI_MODE_NIGHT_NO
}
},
// Needed so that UI that uses it don't crash during screenshot tests
LocalOnBackPressedDispatcherOwner provides object : OnBackPressedDispatcherOwner {
override val lifecycle: Lifecycle get() = lifecycleOwner.lifecycle
override val onBackPressedDispatcher: OnBackPressedDispatcher get() = OnBackPressedDispatcher()
}
) {
ElementTheme {
Box(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
) {
componentTestPreview.Content()
}
}
}
}
}
}
private fun String.toLocale(): Locale {
return when (this) {
"en" -> Locale.US
"fr" -> Locale.FRANCE
"de" -> Locale.GERMAN
else -> Locale.Builder().setLanguage(this).build()
}
}

52
tests/uitests/src/test/kotlin/ui/T.kt

@ -1,52 +0,0 @@ @@ -1,52 +0,0 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ui
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import org.junit.Test
import org.junit.runner.RunWith
/**
* Screenshot test for the Locale other then English.
*/
@RunWith(TestParameterInjector::class)
class T : ScreenshotTest() {
/**
* *Note*: keep the method name as short as possible to get shorter filename for generated screenshot.
* Long name was preview_test.
*/
@SuppressWarnings("MemberNameEqualsClassName")
@Test
fun t(
@TestParameter(valuesProvider = PreviewProvider::class) componentTestPreview: TestPreview,
@TestParameter baseDeviceConfig: BaseDeviceConfig,
@TestParameter(value = ["1.0"]) fontScale: Float,
@TestParameter(value = ["de"]) localeStr: String,
) {
// Only test ComponentTestPreview, and only with the light theme
if (componentTestPreview.isNightMode() || componentTestPreview !is ComponentTestPreview) {
return
}
doTest(
componentTestPreview = componentTestPreview,
baseDeviceConfig = baseDeviceConfig,
fontScale = fontScale,
localeStr = localeStr,
)
}
}

41
tests/uitests/src/test/kotlin/ui/TestPreview.kt

@ -1,41 +0,0 @@ @@ -1,41 +0,0 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ui
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import com.airbnb.android.showkase.models.ShowkaseElementsMetadata
import io.element.android.libraries.designsystem.preview.NIGHT_MODE_NAME
interface TestPreview {
@Composable
fun Content()
val name: String
fun customHeightDp(): Dp? = null
}
/**
* Showkase doesn't put the [Preview.uiMode] parameter in its [ShowkaseElementsMetadata]
* so we have to encode the night mode bit in a preview's name.
*/
fun TestPreview.isNightMode(): Boolean {
// Dark mode previews have name "N" so their component name contains "- N"
return this.name.contains("- $NIGHT_MODE_NAME")
}

50
tests/uitests/src/test/kotlin/ui/TypographyTestPreview.kt

@ -1,50 +0,0 @@ @@ -1,50 +0,0 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ui
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.BasicText
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.airbnb.android.showkase.models.ShowkaseBrowserTypography
import com.airbnb.android.showkase.ui.padding4x
import java.util.Locale
class TypographyTestPreview(
private val showkaseBrowserTypography: ShowkaseBrowserTypography
) : TestPreview {
@Composable
override fun Content() {
BasicText(
text = showkaseBrowserTypography.typographyName.replaceFirstChar {
it.titlecase(Locale.getDefault())
},
modifier = Modifier
.fillMaxWidth()
.padding(padding4x),
style = showkaseBrowserTypography.textStyle.copy(
color = MaterialTheme.colorScheme.onBackground
)
)
}
override val name: String = showkaseBrowserTypography.typographyName
override fun toString(): String = "Typo_${showkaseBrowserTypography.typographyGroup}_${showkaseBrowserTypography.typographyName}"
}

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_LoggedInView_null_LoggedInView-Day-0_0_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_0_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_LoggedInView_null_LoggedInView-Day-0_0_null_1,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_1_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_LoggedInView_null_LoggedInView-Day-0_0_null_2,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_2_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_LoggedInView_null_LoggedInView-Night-0_1_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_0_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_LoggedInView_null_LoggedInView-Night-0_1_null_1,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_1_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_LoggedInView_null_LoggedInView-Night-0_1_null_2,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_2_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_SyncStateView_null_SyncStateView-Day-1_1_null,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateView_Day_0_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_SyncStateView_null_SyncStateView-Night-1_2_null,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateView_Night_0_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room.joined_LoadingRoomNodeView_null_LoadingRoomNodeView-Day-2_2_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.room.joined_LoadingRoomNodeView_Day_0_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room.joined_LoadingRoomNodeView_null_LoadingRoomNodeView-Day-2_2_null_1,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.room.joined_LoadingRoomNodeView_Day_1_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room.joined_LoadingRoomNodeView_null_LoadingRoomNodeView-Night-2_3_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.room.joined_LoadingRoomNodeView_Night_0_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room.joined_LoadingRoomNodeView_null_LoadingRoomNodeView-Night-2_3_null_1,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.room.joined_LoadingRoomNodeView_Night_1_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_RootView_null_RootView-Day-3_3_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_0_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_RootView_null_RootView-Day-3_3_null_1,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_1_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_RootView_null_RootView-Day-3_3_null_2,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_2_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_RootView_null_RootView-Night-3_4_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_0_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_RootView_null_RootView-Night-3_4_null_1,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_1_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_RootView_null_RootView-Night-3_4_null_2,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_2_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.api.preferences_AnalyticsPreferencesView_null_AnalyticsPreferencesView-Day-0_1_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.api.preferences_AnalyticsPreferencesView_null_AnalyticsPreferencesView-Night-0_2_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Day-0_1_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/features.analytics.impl_AnalyticsOptInView_Day_0_en.png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Night-0_2_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/features.analytics.impl_AnalyticsOptInView_Night_0_en.png

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save