diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000..0542767eff
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+**/snapshots/**/*.png filter=lfs diff=lfs merge=lfs -text
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 02e8ab62b2..678a4268af 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -23,3 +23,12 @@ jobs:
- uses: actions/checkout@v3
- name: Run tests
run: ./gradlew test $CI_GRADLE_ARG_PROPERTIES
+
+ - name: Archive test results on error
+ if: failure()
+ uses: actions/upload-artifact@v3
+ with:
+ name: screenshot-results
+ path: |
+ **/out/failures/
+ **/build/reports/tests/*UnitTest/
diff --git a/.github/workflows/validate-lfs.yml b/.github/workflows/validate-lfs.yml
new file mode 100644
index 0000000000..417b95ce2c
--- /dev/null
+++ b/.github/workflows/validate-lfs.yml
@@ -0,0 +1,15 @@
+name: Validate Git LFS
+
+on: [pull_request]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ name: Validate
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ lfs: 'true'
+
+ - run: |
+ ./tools/git/validate_lfs.sh
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 07d0ff2d65..29d2db71ae 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -16,6 +16,9 @@
* limitations under the License.
*/
+import extension.allFeatures
+import extension.allLibraries
+
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
@@ -125,6 +128,7 @@ android {
}
}
+ // Waiting for https://github.com/google/ksp/issues/37
applicationVariants.all {
kotlin.sourceSets {
getByName(name) {
@@ -156,19 +160,9 @@ knit {
}
dependencies {
- implementation(project(":libraries:designsystem"))
- implementation(project(":libraries:matrix"))
- implementation(project(":libraries:matrixui"))
- implementation(project(":libraries:core"))
- implementation(project(":libraries:architecture"))
- implementation(project(":features:onboarding"))
- implementation(project(":features:login"))
- implementation(project(":features:logout"))
- implementation(project(":features:roomlist"))
- implementation(project(":features:messages"))
- implementation(project(":features:rageshake"))
- implementation(project(":features:preferences"))
- implementation(project(":libraries:di"))
+ allLibraries()
+ allFeatures()
+ implementation(project(":tests:uitests"))
implementation(project(":anvilannotations"))
anvil(project(":anvilcodegen"))
@@ -184,7 +178,4 @@ dependencies {
implementation(libs.dagger)
kapt(libs.dagger.compiler)
-
- implementation(libs.showkase)
- ksp(libs.showkase.processor)
}
diff --git a/app/src/main/kotlin/io/element/android/x/root/RootView.kt b/app/src/main/kotlin/io/element/android/x/root/RootView.kt
index 7aa3046719..b767fb164f 100644
--- a/app/src/main/kotlin/io/element/android/x/root/RootView.kt
+++ b/app/src/main/kotlin/io/element/android/x/root/RootView.kt
@@ -16,6 +16,7 @@
package io.element.android.x.root
+import android.app.Activity
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
@@ -23,14 +24,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
-import androidx.core.content.ContextCompat
-import com.airbnb.android.showkase.models.Showkase
import io.element.android.x.component.ShowkaseButton
import io.element.android.x.features.rageshake.crash.ui.CrashDetectionEvents
import io.element.android.x.features.rageshake.crash.ui.CrashDetectionView
import io.element.android.x.features.rageshake.detection.RageshakeDetectionEvents
import io.element.android.x.features.rageshake.detection.RageshakeDetectionView
-import io.element.android.x.getBrowserIntent
+import io.element.android.x.tests.uitests.openShowkase
@Composable
fun RootView(
@@ -57,7 +56,7 @@ fun RootView(
ShowkaseButton(
isVisible = state.isShowkaseButtonVisible,
onCloseClicked = { eventSink(RootEvents.HideShowkaseButton) },
- onClick = { ContextCompat.startActivity(context, Showkase.getBrowserIntent(context), null) }
+ onClick = { openShowkase(context as Activity) }
)
RageshakeDetectionView(
state = state.rageshakeDetectionState,
diff --git a/docs/screenshot_testing.md b/docs/screenshot_testing.md
new file mode 100644
index 0000000000..cefa08676f
--- /dev/null
+++ b/docs/screenshot_testing.md
@@ -0,0 +1,45 @@
+# Screenshot testing
+
+
+
+* [Overview](#overview)
+* [Setup](#setup)
+* [Recording](#recording)
+* [Verifying](#verifying)
+* [Contributing](#contributing)
+
+
+
+## Overview
+
+- Screenshot tests are tests which record the content of a rendered screen and verify subsequent runs to check if the screen renders differently.
+- ElementX 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).
+- The screenshot verification occurs on every pull request as part of the `tests.yml` workflow.
+
+## Setup
+
+- Install Git LFS through your package manager of choice (`brew install git-lfs` | `yay -S git-lfs`).
+- Install the Git LFS hooks into the project.
+
+```bash
+# with element-android as the current working directory
+git lfs install --local
+```
+
+- If installed correctly, `git push` and `git pull` will now include LFS content.
+
+## Recording
+
+- `./gradlew recordPaparazziDebug`
+- Paparazzi will generate images in `:tests:uitests/src/test/snapshots`, which will need to be committed to the repository using Git LFS.
+
+## Verifying
+
+- `./gradlew verifyPaparazziDebug`
+- In the case of failure, Paparazzi will generate images in `:tests:uitests/out/failure`. The images will show the expected and actual screenshots along with a delta of the two images.
+
+## Contributing
+
+- Creating Previewable Composable will automatically creates new screenshot tests.
+- After creating the new test, record and commit the newly rendered screens.
+- `./tools/git/validate_lfs.sh` can be run to ensure everything is working correctly with Git LFS, the CI also runs this check.
diff --git a/features/login/src/main/kotlin/io/element/android/x/features/login/changeserver/ChangeServerView.kt b/features/login/src/main/kotlin/io/element/android/x/features/login/changeserver/ChangeServerView.kt
index 80dc4f2e0b..1ac77b16b8 100644
--- a/features/login/src/main/kotlin/io/element/android/x/features/login/changeserver/ChangeServerView.kt
+++ b/features/login/src/main/kotlin/io/element/android/x/features/login/changeserver/ChangeServerView.kt
@@ -54,7 +54,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.x.architecture.Async
import io.element.android.x.core.compose.textFieldState
-import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.designsystem.components.VectorIcon
import io.element.android.x.features.login.R
import io.element.android.x.features.login.error.changeServerError
@@ -183,9 +182,7 @@ fun ChangeServerView(
@Composable
@Preview
fun ChangeServerContentPreview() {
- ElementXTheme {
- ChangeServerView(
- state = ChangeServerState(homeserver = "matrix.org"),
- )
- }
+ ChangeServerView(
+ state = ChangeServerState(homeserver = "matrix.org"),
+ )
}
diff --git a/features/login/src/main/kotlin/io/element/android/x/features/login/root/LoginRootScreen.kt b/features/login/src/main/kotlin/io/element/android/x/features/login/root/LoginRootScreen.kt
index 6e0f38886a..665064f270 100644
--- a/features/login/src/main/kotlin/io/element/android/x/features/login/root/LoginRootScreen.kt
+++ b/features/login/src/main/kotlin/io/element/android/x/features/login/root/LoginRootScreen.kt
@@ -59,7 +59,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.x.core.compose.textFieldState
-import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.features.login.error.loginError
import io.element.android.x.matrix.core.SessionId
import io.element.android.x.ui.strings.R as StringR
@@ -224,11 +223,9 @@ fun LoginRootScreen(
@Composable
@Preview
fun LoginContentPreview() {
- ElementXTheme(darkTheme = false) {
- LoginRootScreen(
- state = LoginRootState(
- homeserver = "matrix.org",
- ),
- )
- }
+ LoginRootScreen(
+ state = LoginRootState(
+ homeserver = "matrix.org",
+ ),
+ )
}
diff --git a/features/logout/src/main/kotlin/io/element/android/x/features/logout/LogoutPreferenceScreen.kt b/features/logout/src/main/kotlin/io/element/android/x/features/logout/LogoutPreferenceScreen.kt
index 18c4e73b1e..5f76d9e3b4 100644
--- a/features/logout/src/main/kotlin/io/element/android/x/features/logout/LogoutPreferenceScreen.kt
+++ b/features/logout/src/main/kotlin/io/element/android/x/features/logout/LogoutPreferenceScreen.kt
@@ -25,7 +25,6 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.x.architecture.Async
-import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.designsystem.components.ProgressDialog
import io.element.android.x.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.x.designsystem.components.preferences.PreferenceCategory
@@ -92,7 +91,5 @@ fun LogoutPreferenceContent(
@Composable
@Preview
fun LogoutContentPreview() {
- ElementXTheme(darkTheme = false) {
- LogoutPreferenceView(LogoutPreferenceState())
- }
+ LogoutPreferenceView(LogoutPreferenceState())
}
diff --git a/features/messages/src/main/kotlin/io/element/android/x/features/messages/textcomposer/MessageComposerView.kt b/features/messages/src/main/kotlin/io/element/android/x/features/messages/textcomposer/MessageComposerView.kt
index b5592ecceb..e5555bf477 100644
--- a/features/messages/src/main/kotlin/io/element/android/x/features/messages/textcomposer/MessageComposerView.kt
+++ b/features/messages/src/main/kotlin/io/element/android/x/features/messages/textcomposer/MessageComposerView.kt
@@ -18,6 +18,7 @@ package io.element.android.x.features.messages.textcomposer
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import io.element.android.x.designsystem.LocalIsDarkTheme
import io.element.android.x.textcomposer.TextComposer
@Composable
@@ -50,6 +51,7 @@ fun MessageComposerView(
onComposerTextChange = ::onComposerTextChange,
composerCanSendMessage = state.isSendButtonVisible,
composerText = state.text?.charSequence?.toString(),
+ isInDarkMode = LocalIsDarkTheme.current,
modifier = modifier
)
}
diff --git a/features/messages/src/main/kotlin/io/element/android/x/features/messages/timeline/TimelineView.kt b/features/messages/src/main/kotlin/io/element/android/x/features/messages/timeline/TimelineView.kt
index 677487f5a4..b2ecc490b3 100644
--- a/features/messages/src/main/kotlin/io/element/android/x/features/messages/timeline/TimelineView.kt
+++ b/features/messages/src/main/kotlin/io/element/android/x/features/messages/timeline/TimelineView.kt
@@ -356,7 +356,7 @@ class MessagesItemGroupPositionToMessagesTimelineItemContentProvider :
)
@Suppress("PreviewPublic")
-@Preview(showBackground = true)
+@Preview
@Composable
fun TimelineItemsPreview(
@PreviewParameter(MessagesTimelineItemContentProvider::class)
diff --git a/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/bugreport/BugReportView.kt b/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/bugreport/BugReportView.kt
index b22cc7c48c..009ca812ef 100644
--- a/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/bugreport/BugReportView.kt
+++ b/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/bugreport/BugReportView.kt
@@ -53,7 +53,6 @@ import coil.request.ImageRequest
import io.element.android.x.architecture.Async
import io.element.android.x.core.compose.LogCompositions
import io.element.android.x.core.compose.textFieldState
-import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.designsystem.components.LabelledCheckbox
import io.element.android.x.designsystem.components.dialogs.ErrorDialog
import io.element.android.x.ui.strings.R as StringR
@@ -213,9 +212,7 @@ fun BugReportView(
@Composable
@Preview
fun BugReportContentPreview() {
- ElementXTheme(darkTheme = false) {
- BugReportView(
- state = BugReportState(),
- )
- }
+ BugReportView(
+ state = BugReportState(),
+ )
}
diff --git a/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/crash/ui/CrashDetectionScreen.kt b/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/crash/ui/CrashDetectionScreen.kt
index 13d174468b..494cc0d843 100644
--- a/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/crash/ui/CrashDetectionScreen.kt
+++ b/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/crash/ui/CrashDetectionScreen.kt
@@ -20,7 +20,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.x.core.compose.LogCompositions
-import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.x.ui.strings.R as StringR
@@ -66,9 +65,7 @@ fun CrashDetectionContent(
@Preview
@Composable
fun CrashDetectionContentPreview() {
- ElementXTheme {
- CrashDetectionContent(
- state = CrashDetectionState()
- )
- }
+ CrashDetectionContent(
+ state = CrashDetectionState()
+ )
}
diff --git a/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/detection/RageshakeDetectionView.kt b/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/detection/RageshakeDetectionView.kt
index 6f647758ce..c438c0a600 100644
--- a/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/detection/RageshakeDetectionView.kt
+++ b/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/detection/RageshakeDetectionView.kt
@@ -28,7 +28,6 @@ import io.element.android.x.core.compose.OnLifecycleEvent
import io.element.android.x.core.hardware.vibrate
import io.element.android.x.core.screenshot.ImageResult
import io.element.android.x.core.screenshot.screenshot
-import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.x.ui.strings.R as StringR
@@ -98,7 +97,5 @@ fun RageshakeDialogContent(
@Preview
@Composable
fun RageshakeDialogContentPreview() {
- ElementXTheme {
- RageshakeDialogContent()
- }
+ RageshakeDialogContent()
}
diff --git a/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/preferences/RageshakePreferencesView.kt b/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/preferences/RageshakePreferencesView.kt
index f5ce13440e..43f7f94d03 100644
--- a/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/preferences/RageshakePreferencesView.kt
+++ b/features/rageshake/src/main/kotlin/io/element/android/x/features/rageshake/preferences/RageshakePreferencesView.kt
@@ -75,6 +75,12 @@ fun RageshakePreferencesView(
@Composable
@Preview
-fun RageshakePreferencesPreview() {
+fun RageshakePreferencesViewPreview() {
RageshakePreferencesView(RageshakePreferencesState(isEnabled = true, isSupported = true, sensitivity = 0.5f))
}
+
+@Composable
+@Preview
+fun RageshakePreferenceNotSupportedPreview() {
+ RageshakePreferencesView(RageshakePreferencesState(isEnabled = true, isSupported = false, sensitivity = 0.5f))
+}
diff --git a/features/roomlist/src/main/kotlin/io/element/android/x/features/roomlist/RoomListView.kt b/features/roomlist/src/main/kotlin/io/element/android/x/features/roomlist/RoomListView.kt
index 97d849415c..d5c51b1ea3 100644
--- a/features/roomlist/src/main/kotlin/io/element/android/x/features/roomlist/RoomListView.kt
+++ b/features/roomlist/src/main/kotlin/io/element/android/x/features/roomlist/RoomListView.kt
@@ -37,7 +37,6 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Velocity
import io.element.android.x.core.compose.LogCompositions
-import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.features.roomlist.components.RoomListTopBar
import io.element.android.x.features.roomlist.components.RoomSummaryRow
@@ -150,30 +149,13 @@ private fun RoomListRoomSummary.contentType() = isPlaceholder
@Preview
@Composable
-fun PreviewableRoomListView() {
- ElementXTheme(darkTheme = false) {
- RoomListView(
- roomSummaries = stubbedRoomSummaries(),
- matrixUser = MatrixUser(id = UserId("@id"), username = "User#1", avatarData = AvatarData("U")),
- onRoomClicked = {},
- filter = "filter",
- onFilterChanged = {},
- onScrollOver = {}
- )
- }
-}
-
-@Preview
-@Composable
-fun PreviewableDarkRoomListView() {
- ElementXTheme(darkTheme = true) {
- RoomListView(
- roomSummaries = stubbedRoomSummaries(),
- matrixUser = MatrixUser(id = UserId("@id"), username = "User#1", avatarData = AvatarData("U")),
- onRoomClicked = {},
- filter = "filter",
- onFilterChanged = {},
- onScrollOver = {}
- )
- }
+fun RoomListViewPreview() {
+ RoomListView(
+ roomSummaries = stubbedRoomSummaries(),
+ matrixUser = MatrixUser(id = UserId("@id"), username = "User#1", avatarData = AvatarData("U")),
+ onRoomClicked = {},
+ filter = "filter",
+ onFilterChanged = {},
+ onScrollOver = {}
+ )
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 244f1618f1..06bb16908e 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -38,6 +38,8 @@ test_junitext = "1.1.3"
test_barista = "4.2.0"
test_hamcrest = "2.2"
test_orchestrator = "1.4.1"
+test_paparazzi = "1.2.0"
+test_parameter_injector = "1.8"
#other
coil = "2.2.2"
@@ -110,6 +112,7 @@ test_mockk = { module = "io.mockk:mockk", version.ref = "test_mockk" }
test_barista = { module = "com.adevinta.android:barista", version.ref = "test_barista" }
test_hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "test_hamcrest" }
test_orchestrator = { module = "androidx.test:orchestrator", version.ref = "test_orchestrator" }
+test_parameter_injector = { module = "com.google.testparameterinjector:test-parameter-injector", version.ref = "test_parameter_injector" }
# Others
coil = { module = "io.coil-kt:coil", version.ref = "coil" }
@@ -148,3 +151,4 @@ dependencygraph = { id = "com.savvasdalkitsis.module-dependency-graph", version.
dependencycheck = { id = "org.owasp.dependencycheck", version.ref = "dependencycheck" }
stem = { id = "com.likethesalad.stem", version.ref = "stem" }
stemlibrary = { id = "com.likethesalad.stem-library", version.ref = "stem" }
+paparazzi = { id = "app.cash.paparazzi", version.ref = "test_paparazzi" }
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/avatar/Avatar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/avatar/Avatar.kt
index 73451441e6..e2465e3f13 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/avatar/Avatar.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/avatar/Avatar.kt
@@ -97,6 +97,6 @@ private fun InitialsAvatar(
@Preview
@Composable
-fun InitialsAvatar() {
- InitialsAvatar(AvatarData("A"))
+fun InitialsAvatarPreview() {
+ Avatar(AvatarData(name = "A"))
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceCategory.kt b/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceCategory.kt
index 74b13ffa6b..e74645950c 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceCategory.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceCategory.kt
@@ -53,7 +53,7 @@ fun PreferenceCategory(
}
@Composable
-@Preview(showBackground = false)
+@Preview
fun PreferenceCategoryPreview() {
PreferenceCategory(
title = "Category title",
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceScreen.kt b/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceScreen.kt
index 8fd26193a3..2d2e42c9df 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceScreen.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceScreen.kt
@@ -111,7 +111,7 @@ fun PreferenceTopAppBar(
}
@Composable
-@Preview(showBackground = false)
+@Preview
fun PreferenceScreenPreview() {
PreferenceView(
title = "Preference screen"
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceSlide.kt b/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceSlide.kt
index 672f94fbcb..af6641f13d 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceSlide.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceSlide.kt
@@ -85,7 +85,7 @@ fun PreferenceSlide(
}
@Composable
-@Preview(showBackground = false)
+@Preview
fun PreferenceSlidePreview() {
PreferenceSlide(
title = "Slide",
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceSwitch.kt b/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceSwitch.kt
index ab2757936d..4dc6609a41 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceSwitch.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceSwitch.kt
@@ -76,7 +76,7 @@ fun PreferenceSwitch(
}
@Composable
-@Preview(showBackground = false)
+@Preview
fun PreferenceSwitchPreview() {
PreferenceSwitch(
title = "Switch",
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceText.kt b/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceText.kt
index 70c3419bc0..5922a1a92c 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceText.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/x/designsystem/components/preferences/PreferenceText.kt
@@ -64,7 +64,7 @@ fun PreferenceText(
}
@Composable
-@Preview(showBackground = false)
+@Preview
fun PreferenceTextPreview() {
PreferenceText(
title = "Title",
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/x/matrix/ui/components/MatrixUserHeader.kt b/libraries/matrixui/src/main/kotlin/io/element/android/x/matrix/ui/components/MatrixUserHeader.kt
index 928c0f032b..08acd79a0c 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/x/matrix/ui/components/MatrixUserHeader.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/x/matrix/ui/components/MatrixUserHeader.kt
@@ -33,7 +33,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.designsystem.components.avatar.Avatar
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.designsystem.components.avatar.AvatarSize
@@ -84,29 +83,25 @@ fun MatrixUserHeader(
@Preview
@Composable
fun MatrixUserHeaderPreview() {
- ElementXTheme {
- MatrixUserHeader(
- MatrixUser(
- id = UserId("@alice:server.org"),
- username = "Alice",
- avatarUrl = null,
- avatarData = AvatarData("Alice")
- )
+ MatrixUserHeader(
+ MatrixUser(
+ id = UserId("@alice:server.org"),
+ username = "Alice",
+ avatarUrl = null,
+ avatarData = AvatarData("Alice")
)
- }
+ )
}
@Preview
@Composable
fun MatrixUserHeaderNoUsernamePreview() {
- ElementXTheme {
- MatrixUserHeader(
- MatrixUser(
- id = UserId("@alice:server.org"),
- username = null,
- avatarUrl = null,
- avatarData = AvatarData("Alice")
- )
+ MatrixUserHeader(
+ MatrixUser(
+ id = UserId("@alice:server.org"),
+ username = null,
+ avatarUrl = null,
+ avatarData = AvatarData("Alice")
)
- }
+ )
}
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/x/matrix/ui/components/MatrixUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/x/matrix/ui/components/MatrixUserRow.kt
index 6fbde8bd64..e121ced17c 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/x/matrix/ui/components/MatrixUserRow.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/x/matrix/ui/components/MatrixUserRow.kt
@@ -33,7 +33,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.designsystem.components.avatar.Avatar
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.matrix.core.UserId
@@ -88,14 +87,12 @@ fun MatrixUserRow(
@Preview
@Composable
fun MatrixUserRowPreview() {
- ElementXTheme {
- MatrixUserRow(
- MatrixUser(
- id = UserId("@alice:server.org"),
- username = "Alice",
- avatarUrl = null,
- avatarData = AvatarData("Alice")
- )
+ MatrixUserRow(
+ MatrixUser(
+ id = UserId("@alice:server.org"),
+ username = "Alice",
+ avatarUrl = null,
+ avatarData = AvatarData("Alice")
)
- }
+ )
}
diff --git a/libraries/textcomposer/src/main/kotlin/io/element/android/x/textcomposer/TextComposer.kt b/libraries/textcomposer/src/main/kotlin/io/element/android/x/textcomposer/TextComposer.kt
index 8bb7aea485..6dbc649acb 100644
--- a/libraries/textcomposer/src/main/kotlin/io/element/android/x/textcomposer/TextComposer.kt
+++ b/libraries/textcomposer/src/main/kotlin/io/element/android/x/textcomposer/TextComposer.kt
@@ -18,7 +18,6 @@ package io.element.android.x.textcomposer
import android.graphics.Color
import android.net.Uri
-import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -42,6 +41,7 @@ fun TextComposer(
composerText: String?,
composerMode: MessageComposerMode,
composerCanSendMessage: Boolean,
+ isInDarkMode: Boolean,
modifier: Modifier = Modifier,
onSendMessage: (String) -> Unit = {},
onFullscreenToggle: () -> Unit = {},
@@ -51,7 +51,6 @@ fun TextComposer(
if (LocalInspectionMode.current) {
FakeComposer(modifier)
} else {
- val isInDarkMode = isSystemInDarkTheme()
AndroidView(
modifier = modifier,
factory = { context ->
@@ -156,5 +155,6 @@ fun TextComposerPreview() {
onCloseSpecialMode = {},
composerCanSendMessage = true,
composerText = "Message",
+ isInDarkMode = true,
)
}
diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt
index e134c26f79..7918f07686 100644
--- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt
+++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt
@@ -20,6 +20,7 @@ import gradle.kotlin.dsl.accessors._4b7ad2363fc1fce7c774e054dc9a9300.androidTest
import gradle.kotlin.dsl.accessors._4b7ad2363fc1fce7c774e054dc9a9300.debugImplementation
import gradle.kotlin.dsl.accessors._4b7ad2363fc1fce7c774e054dc9a9300.implementation
import org.gradle.kotlin.dsl.DependencyHandlerScope
+import org.gradle.kotlin.dsl.project
/**
* Dependencies used by all the modules
@@ -48,3 +49,21 @@ fun DependencyHandlerScope.composeDependencies() {
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5")
}
+fun DependencyHandlerScope.allLibraries() {
+ implementation(project(":libraries:designsystem"))
+ implementation(project(":libraries:matrix"))
+ implementation(project(":libraries:matrixui"))
+ implementation(project(":libraries:core"))
+ implementation(project(":libraries:architecture"))
+ implementation(project(":libraries:di"))
+}
+
+fun DependencyHandlerScope.allFeatures() {
+ implementation(project(":features:onboarding"))
+ implementation(project(":features:login"))
+ implementation(project(":features:logout"))
+ implementation(project(":features:roomlist"))
+ implementation(project(":features:messages"))
+ implementation(project(":features:rageshake"))
+ implementation(project(":features:preferences"))
+}
diff --git a/plugins/src/main/kotlin/io.element.android-compose-library.gradle.kts b/plugins/src/main/kotlin/io.element.android-compose-library.gradle.kts
index 33df0c0835..2b07dd3285 100644
--- a/plugins/src/main/kotlin/io.element.android-compose-library.gradle.kts
+++ b/plugins/src/main/kotlin/io.element.android-compose-library.gradle.kts
@@ -30,6 +30,14 @@ plugins {
android {
androidConfig(project)
composeConfig()
+ // Waiting for https://github.com/google/ksp/issues/37
+ libraryVariants.all {
+ kotlin.sourceSets {
+ getByName(name) {
+ kotlin.srcDir("build/generated/ksp/$name/kotlin")
+ }
+ }
+ }
}
dependencies {
diff --git a/plugins/src/main/kotlin/io.element.android-library.gradle.kts b/plugins/src/main/kotlin/io.element.android-library.gradle.kts
index b1b15c7e10..755a00f316 100644
--- a/plugins/src/main/kotlin/io.element.android-library.gradle.kts
+++ b/plugins/src/main/kotlin/io.element.android-library.gradle.kts
@@ -27,6 +27,14 @@ plugins {
android {
androidConfig(project)
+ // Waiting for https://github.com/google/ksp/issues/37
+ libraryVariants.all {
+ kotlin.sourceSets {
+ getByName(name) {
+ kotlin.srcDir("build/generated/ksp/$name/kotlin")
+ }
+ }
+ }
}
dependencies {
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 2e1ed9af84..af4fe223dd 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -50,6 +50,7 @@ include(":features:rageshake")
include(":features:preferences")
include(":libraries:designsystem")
include(":libraries:di")
+include(":tests:uitests")
include(":anvilannotations")
include(":anvilcodegen")
include(":libraries:architecture")
diff --git a/tests/uitests/.gitignore b/tests/uitests/.gitignore
new file mode 100644
index 0000000000..42afabfd2a
--- /dev/null
+++ b/tests/uitests/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/tests/uitests/build.gradle.kts b/tests/uitests/build.gradle.kts
new file mode 100644
index 0000000000..e8f2ca47ad
--- /dev/null
+++ b/tests/uitests/build.gradle.kts
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+import extension.allFeatures
+import extension.allLibraries
+
+// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
+@Suppress("DSL_SCOPE_VIOLATION")
+plugins {
+ id("io.element.android-compose-library")
+ alias(libs.plugins.ksp)
+ alias(libs.plugins.paparazzi)
+}
+
+android {
+ namespace = "io.element.android.x.tests.uitests"
+}
+
+dependencies {
+ testImplementation(libs.test.junit)
+ testImplementation(libs.test.parameter.injector)
+ androidTestImplementation(libs.test.junitext)
+ ksp(libs.showkase.processor)
+ kspTest(libs.showkase.processor)
+
+ implementation(libs.showkase)
+ ksp(libs.showkase.processor)
+
+ allLibraries()
+ allFeatures()
+}
diff --git a/tests/uitests/consumer-rules.pro b/tests/uitests/consumer-rules.pro
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/uitests/proguard-rules.pro b/tests/uitests/proguard-rules.pro
new file mode 100644
index 0000000000..ff59496d81
--- /dev/null
+++ b/tests/uitests/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.kts.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/tests/uitests/src/main/AndroidManifest.xml b/tests/uitests/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..122869829c
--- /dev/null
+++ b/tests/uitests/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+
+
+
+
diff --git a/app/src/main/kotlin/io/element/android/x/ElementRootModule.kt b/tests/uitests/src/main/kotlin/io/element/android/x/tests/uitests/ElementXShowkaseRootModule.kt
similarity index 88%
rename from app/src/main/kotlin/io/element/android/x/ElementRootModule.kt
rename to tests/uitests/src/main/kotlin/io/element/android/x/tests/uitests/ElementXShowkaseRootModule.kt
index ab59ae72fe..6250bcaba8 100644
--- a/app/src/main/kotlin/io/element/android/x/ElementRootModule.kt
+++ b/tests/uitests/src/main/kotlin/io/element/android/x/tests/uitests/ElementXShowkaseRootModule.kt
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package io.element.android.x
+package io.element.android.x.tests.uitests
import com.airbnb.android.showkase.annotation.ShowkaseRoot
import com.airbnb.android.showkase.annotation.ShowkaseRootModule
@ShowkaseRoot
-class ElementRootModule : ShowkaseRootModule
+class ElementXShowkaseRootModule : ShowkaseRootModule
diff --git a/tests/uitests/src/main/kotlin/io/element/android/x/tests/uitests/ShowkaseButton.kt b/tests/uitests/src/main/kotlin/io/element/android/x/tests/uitests/ShowkaseButton.kt
new file mode 100644
index 0000000000..ba8f111f13
--- /dev/null
+++ b/tests/uitests/src/main/kotlin/io/element/android/x/tests/uitests/ShowkaseButton.kt
@@ -0,0 +1,66 @@
+/*
+ * 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 io.element.android.x.tests.uitests
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material3.Button
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun ShowkaseButton(
+ modifier: Modifier = Modifier,
+ onClick: () -> Unit = {},
+) {
+ var isShowkaseButtonVisible by remember { mutableStateOf(BuildConfig.DEBUG) }
+
+ if (isShowkaseButtonVisible) {
+ Button(
+ modifier = modifier
+ .padding(top = 32.dp),
+ onClick = onClick
+ ) {
+ Text(text = "Showkase Browser")
+ IconButton(
+ modifier = Modifier
+ .padding(start = 8.dp)
+ .size(16.dp),
+ onClick = { isShowkaseButtonVisible = false },
+ ) {
+ Icon(imageVector = Icons.Filled.Close, contentDescription = "")
+ }
+ }
+ }
+}
+
+@Preview(group = "Buttons", name = "Showkase button")
+@Composable
+fun ShowkaseButtonPreview() {
+ ShowkaseButton()
+}
diff --git a/tests/uitests/src/main/kotlin/io/element/android/x/tests/uitests/ShowkaseNavigation.kt b/tests/uitests/src/main/kotlin/io/element/android/x/tests/uitests/ShowkaseNavigation.kt
new file mode 100644
index 0000000000..4ed7328ad3
--- /dev/null
+++ b/tests/uitests/src/main/kotlin/io/element/android/x/tests/uitests/ShowkaseNavigation.kt
@@ -0,0 +1,24 @@
+/*
+ * 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 io.element.android.x.tests.uitests
+
+import android.app.Activity
+import com.airbnb.android.showkase.models.Showkase
+
+fun openShowkase(activity: Activity) {
+ activity.startActivity(Showkase.getBrowserIntent(activity))
+}
diff --git a/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/BaseDeviceConfig.kt b/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/BaseDeviceConfig.kt
new file mode 100644
index 0000000000..a46dceaa8e
--- /dev/null
+++ b/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/BaseDeviceConfig.kt
@@ -0,0 +1,26 @@
+/*
+ * 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 io.element.android.x.tests.uitests
+
+import app.cash.paparazzi.DeviceConfig
+
+enum class BaseDeviceConfig(
+ val deviceConfig: DeviceConfig,
+) {
+ NEXUS_5(DeviceConfig.NEXUS_5),
+ // PIXEL_C(DeviceConfig.PIXEL_C),
+}
diff --git a/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/ColorTestPreview.kt b/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/ColorTestPreview.kt
new file mode 100644
index 0000000000..9dd2c563d8
--- /dev/null
+++ b/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/ColorTestPreview.kt
@@ -0,0 +1,42 @@
+/*
+ * 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 io.element.android.x.tests.uitests
+
+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 fun toString(): String = "Color_${showkaseBrowserColor.colorGroup}_${showkaseBrowserColor.colorName}"
+}
diff --git a/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/ComponentTestPreview.kt b/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/ComponentTestPreview.kt
new file mode 100644
index 0000000000..598acfadb0
--- /dev/null
+++ b/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/ComponentTestPreview.kt
@@ -0,0 +1,28 @@
+/*
+ * 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 io.element.android.x.tests.uitests
+
+import androidx.compose.runtime.Composable
+import com.airbnb.android.showkase.models.ShowkaseBrowserComponent
+
+class ComponentTestPreview(
+ private val showkaseBrowserComponent: ShowkaseBrowserComponent
+) : TestPreview {
+ @Composable
+ override fun Content() = showkaseBrowserComponent.component()
+ override fun toString(): String = showkaseBrowserComponent.componentKey
+}
diff --git a/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/ScreenshotTest.kt b/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/ScreenshotTest.kt
new file mode 100644
index 0000000000..3d1804884d
--- /dev/null
+++ b/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/ScreenshotTest.kt
@@ -0,0 +1,121 @@
+/*
+ * 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 io.element.android.x.tests.uitests
+
+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 app.cash.paparazzi.Paparazzi
+import com.airbnb.android.showkase.models.Showkase
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import io.element.android.x.designsystem.ElementXTheme
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+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.
+ */
+@RunWith(TestParameterInjector::class)
+class ScreenshotTest {
+
+ object PreviewProvider : TestParameter.TestParameterValuesProvider {
+ override fun provideValues(): List {
+ 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
+ }
+ }
+
+ @get:Rule
+ val paparazzi = Paparazzi(
+ maxPercentDifference = 0.0,
+ )
+
+ @Test
+ fun preview_tests(
+ @TestParameter(valuesProvider = PreviewProvider::class) componentTestPreview: TestPreview,
+ @TestParameter baseDeviceConfig: BaseDeviceConfig,
+ @TestParameter(value = ["1.0"/*, "1.5"*/]) fontScale: Float,
+ @TestParameter(value = ["light", "dark"]) theme: String,
+ @TestParameter(value = ["en" /*"fr", "de", "ru"*/]) localeStr: String,
+ ) {
+ paparazzi.unsafeUpdateConfig(
+ deviceConfig = baseDeviceConfig.deviceConfig.copy(
+ softButtons = false,
+ )
+ )
+ 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(localeStr.toLocale()))
+ },
+ // Needed so that UI that uses it don't crash during screenshot tests
+ LocalOnBackPressedDispatcherOwner provides object : OnBackPressedDispatcherOwner {
+ override fun getLifecycle() = lifecycleOwner.lifecycle
+
+ override fun getOnBackPressedDispatcher() = OnBackPressedDispatcher()
+ }
+ ) {
+ ElementXTheme(darkTheme = (theme == "dark")) {
+ Box(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
+ componentTestPreview.Content()
+ }
+ }
+ }
+ }
+ }
+}
+
+private fun String.toLocale(): Locale {
+ return when (this) {
+ "en" -> Locale.ENGLISH
+ "fr" -> Locale.FRANCE
+ "de" -> Locale.GERMAN
+ else -> Locale.Builder().setLanguage(this).build()
+ }
+}
diff --git a/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/TestPreview.kt b/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/TestPreview.kt
new file mode 100644
index 0000000000..6398a418bf
--- /dev/null
+++ b/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/TestPreview.kt
@@ -0,0 +1,24 @@
+/*
+ * 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 io.element.android.x.tests.uitests
+
+import androidx.compose.runtime.Composable
+
+interface TestPreview {
+ @Composable
+ fun Content()
+}
diff --git a/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/TypographyTestPreview.kt b/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/TypographyTestPreview.kt
new file mode 100644
index 0000000000..0ff497d5d8
--- /dev/null
+++ b/tests/uitests/src/test/kotlin/io/element/android/x/tests/uitests/TypographyTestPreview.kt
@@ -0,0 +1,48 @@
+/*
+ * 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 io.element.android.x.tests.uitests
+
+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 fun toString(): String = "Typo_${showkaseBrowserTypography.typographyGroup}_${showkaseBrowserTypography.typographyName}"
+}
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Color_Material Design_DarkGrey,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Color_Material Design_DarkGrey,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..44aabba74b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Color_Material Design_DarkGrey,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:457c973681addb003e8f84ce3706361cfe0bb88845799838971da910476a4ba8
+size 4371
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Color_Material Design_DarkGrey,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Color_Material Design_DarkGrey,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..2ce803608f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Color_Material Design_DarkGrey,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0c51ed0a004218e97af5f34e3011e0c4e4b50cccf0eff1417234186bdb786839
+size 4983
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Color_Material Design_LightGrey,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Color_Material Design_LightGrey,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..35c67a316c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Color_Material Design_LightGrey,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f2c05b0f79fea3df1147a454202f1ca2973d48939e4fc296be171ad98f272333
+size 4371
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Color_Material Design_LightGrey,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Color_Material Design_LightGrey,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..253036ceac
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Color_Material Design_LightGrey,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1042b183b2703322af7a43d959992340b47618b27d706cfba8cf1556e70f793e
+size 4984
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Typo_Element_Body Large,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Typo_Element_Body Large,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..786ac1495b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Typo_Element_Body Large,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1d1b2622226f026d0231020c389b6e99a1c50cd0bc84d77df6060e390c8521fb
+size 7056
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Typo_Element_Body Large,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Typo_Element_Body Large,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..0434852b77
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Typo_Element_Body Large,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:77dcd8feab45cb713f3ee6cdc8ad86189e73176e0eff88494a2cb109431ddb33
+size 7097
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Typo_Element_Headline Small,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Typo_Element_Headline Small,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..ab15d2cfd5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Typo_Element_Headline Small,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:de0b7b271b6057db6ef81888356583bdc3432e4f26a52c58586f88043de20021
+size 9476
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Typo_Element_Headline Small,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Typo_Element_Headline Small,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..02e426ff7b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[Typo_Element_Headline Small,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d02db7b3881676d8b194e6871e178af8af99e09bd5cc15b865a25bd405e3fc33
+size 9445
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.avatar_null_DefaultGroup_InitialsAvatarPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.avatar_null_DefaultGroup_InitialsAvatarPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..2c7e9fbd5d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.avatar_null_DefaultGroup_InitialsAvatarPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9c0fb883d948d8177cd32eb81b24e7b104acd7b6b120b73d6693088f947701da
+size 8295
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.avatar_null_DefaultGroup_InitialsAvatarPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.avatar_null_DefaultGroup_InitialsAvatarPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..55a3da0f93
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.avatar_null_DefaultGroup_InitialsAvatarPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4a3e98c7f5dfc7bc02aa68d4c6da8ccd6b1e4a583f98900037417e69e5c184f4
+size 8058
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.dialogs_null_DefaultGroup_ConfirmationDialogPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.dialogs_null_DefaultGroup_ConfirmationDialogPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..29165db48c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.dialogs_null_DefaultGroup_ConfirmationDialogPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f6d88788a186b08d8e2834c39efb1c6476cf51aec70aa08dc806eb69e2d98d9a
+size 20004
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.dialogs_null_DefaultGroup_ConfirmationDialogPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.dialogs_null_DefaultGroup_ConfirmationDialogPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..4193346f3c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.dialogs_null_DefaultGroup_ConfirmationDialogPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:45860d45aefad7cfd7d7b9f92146799860a5ede7e815d5b28b606fc566b3838c
+size 18344
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.dialogs_null_DefaultGroup_ErrorDialogPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.dialogs_null_DefaultGroup_ErrorDialogPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..8bba8846db
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.dialogs_null_DefaultGroup_ErrorDialogPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:39d924e3718d7f045c15888373a96ec85d4afd0ef405bf25edc453fddaacdc55
+size 13082
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.dialogs_null_DefaultGroup_ErrorDialogPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.dialogs_null_DefaultGroup_ErrorDialogPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..49b0df4d6f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.dialogs_null_DefaultGroup_ErrorDialogPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5e5bf72f9d90cbe4b4136a4a2e30168895a3354c6f35ed1d4fc77bfe2987fdaf
+size 12565
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceCategoryPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceCategoryPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..2d55ca4648
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceCategoryPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4e23f8342179fc1a88c278f8e81ddb026034958a6b2fdbaae06ff26dc282d483
+size 11654
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceCategoryPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceCategoryPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..ed6c1b4a47
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceCategoryPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:64ed4f47adfb9f111552f44d7a1f3edf58ec1ab64527c2a5dd08b286b7437317
+size 15076
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceScreenPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceScreenPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..452b95b971
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceScreenPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e258d66c313c6fd1e757978c408600328220aa4bcd374b19d202d2a45b1db3bb
+size 18677
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceScreenPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceScreenPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..33c3649a85
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceScreenPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2e1f1e25446962151e621f230e4043ad3a649a7e71b65527fe73158478a4caaf
+size 18709
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceSlidePreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceSlidePreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..302811af53
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceSlidePreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eb61547b9e5b4666abd74145086ae2ee846dced3099f9b36dd2534047caf3266
+size 8465
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceSlidePreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceSlidePreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..a0f13fdf19
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceSlidePreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e52fc3b6131d68424f77ecbf226ec80ad6d4e97a22fe8dbbf38cff301d918dc3
+size 8619
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceSwitchPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceSwitchPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..707aefe0fb
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceSwitchPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d5db2a9a3f2d9bec3d74b45892ee453c51afc3b3b41d6ec1624d72a8377176b4
+size 7095
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceSwitchPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceSwitchPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..e7643a53f5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceSwitchPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c01eabc9eb8d607d77070deb227365e8840e02b50b1adb0cb1f24f4d693a68be
+size 7114
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceTextPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceTextPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..2fd0494ca5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceTextPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b9a36ce6978aa3182e519789a8ea7e1399ae63da630fa7f6727248ba8ba010f7
+size 4837
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceTextPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceTextPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..5e9da5266d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components.preferences_null_DefaultGroup_PreferenceTextPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:421660fccaa66dc7190393ba57660a9bc4a439011bd2f7c1ade8b6a293c489bf
+size 5843
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components_null_DefaultGroup_LabelledCheckboxPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components_null_DefaultGroup_LabelledCheckboxPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..9aa044dfae
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components_null_DefaultGroup_LabelledCheckboxPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f331a07e5b56c550023b9c4eff3afd71fece8dd4d649fd1e2aaa178c27557fda
+size 5007
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components_null_DefaultGroup_LabelledCheckboxPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components_null_DefaultGroup_LabelledCheckboxPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..630f485ec8
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components_null_DefaultGroup_LabelledCheckboxPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:312b8aed8761fc31f445fee012a020e754c1ca60365f1651c2ffce8310f9ca3c
+size 7401
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components_null_DefaultGroup_ProgressDialogPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components_null_DefaultGroup_ProgressDialogPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..9c2e5c3087
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components_null_DefaultGroup_ProgressDialogPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e302ba0695a7663e1abcb98d47cecf5b78968d76202841f9e251313d3dd8c579
+size 10232
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components_null_DefaultGroup_ProgressDialogPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components_null_DefaultGroup_ProgressDialogPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..2a54849c8d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.designsystem.components_null_DefaultGroup_ProgressDialogPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:08918409e8daa9f2d3498fe9f463323e737e59ec8472a50b6bf5ec13a1abacfc
+size 9576
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.login.changeserver_null_DefaultGroup_ChangeServerContentPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.login.changeserver_null_DefaultGroup_ChangeServerContentPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..f168f5fb81
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.login.changeserver_null_DefaultGroup_ChangeServerContentPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0689e18b72a961ecbea6011c73568703ba255375fdb9bbe34b2e94f08c379cc7
+size 27841
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.login.changeserver_null_DefaultGroup_ChangeServerContentPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.login.changeserver_null_DefaultGroup_ChangeServerContentPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..3ad3babb7b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.login.changeserver_null_DefaultGroup_ChangeServerContentPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:555b8b55dc2873ab2963ae65ac13e11913794d2e344ec01d53eebe48cbfbcb39
+size 27048
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.login.root_null_DefaultGroup_LoginContentPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.login.root_null_DefaultGroup_LoginContentPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..815940ac34
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.login.root_null_DefaultGroup_LoginContentPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bc2f6c7463b55716720344a2e873774ea09d98717add30b748a54f1e0374ec04
+size 24751
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.login.root_null_DefaultGroup_LoginContentPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.login.root_null_DefaultGroup_LoginContentPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..fcd50de62f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.login.root_null_DefaultGroup_LoginContentPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:40bbb72871c1eb6f1a9bae19ef5cd63d66eb7e2479eb72733bfa88f912e11957
+size 24353
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.logout_null_DefaultGroup_LogoutContentPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.logout_null_DefaultGroup_LogoutContentPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..89df9fb9e1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.logout_null_DefaultGroup_LogoutContentPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:accc42a849df3c0d8a16de0e21842139558d8c68cdcf9efcccf6b58a7cb3b4b3
+size 4742
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.logout_null_DefaultGroup_LogoutContentPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.logout_null_DefaultGroup_LogoutContentPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..bc6c0a076b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.logout_null_DefaultGroup_LogoutContentPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:48045852c9c677d54db29baa1484eb281aa9678518c4a47ae05e43b4604a77c5
+size 8369
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__0,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__0,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..4676f3504b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__0,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3d5fb95fb866a51e2dbc26239dc70e37a92bddc79d4edc027226948dfe323312
+size 30189
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__0,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__0,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..54c51dda5c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__0,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5b7d3c2cf8d466f6e25404596079fb6254ad6e9fb3ba3f0f72ef1c0ba019dc60
+size 31165
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__1,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__1,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..6dfe58cbdc
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__1,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1be16bc1a73ef5bb7af740b8720c508dbc202d4748d60fb2e10fabcfdeacc75c
+size 43668
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__1,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__1,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..7b8a310f6f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__1,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:02c410850ed4b8658a21c4a60b62757baa9799d4c6dee4d53d2e6b2ca76f7b61
+size 41942
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__2,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__2,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..2c2a002ae5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__2,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:19bcd322392a4d27742a25989810e38d4ac6a38edfb5a228bc13364ed36e7101
+size 31691
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__2,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__2,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..7344b3e35e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__2,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0a0ea920c88ab127513918055621a75528de35d69296727a8be7368df4c848e9
+size 33040
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__3,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__3,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..b4dc232172
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__3,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3ea0809dcddfc5df7aaba26f4ed74bebe404c86f5923c78541518fb7560e897e
+size 55699
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__3,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__3,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..89d55c25a3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__3,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cc36b26cbaab9ee19c898a27bc5bc0a04c77e4004933c47fc69608df103e08d9
+size 52964
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__4,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__4,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..9b684aa226
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__4,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e50733c48b4cb37fc82c228a7013c7aa74e263b13014cb57c7bb1ca0c2ea91c6
+size 28631
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__4,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__4,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..1d930443fe
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__4,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0500e499fb319057b0ca5feb9db9e429266a2832521b429d502d1f62804ee23f
+size 29222
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__5,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__5,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..bf5d223a28
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__5,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ab7cb0da1b64d2c6d87578debbc336feedbe203f6331726503a084101b9a10e6
+size 51186
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__5,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__5,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..0a01c81f08
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.messages.timeline_null_DefaultGroup_TimelineItemsPreview__5,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5535df19747bdec380b79eec8b9d8e2d7e6ed765fe0bb4e4c1c2ffeb4de46854
+size 49115
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.preferences.root_null_DefaultGroup_PreferencesContentPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.preferences.root_null_DefaultGroup_PreferencesContentPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..738199e997
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.preferences.root_null_DefaultGroup_PreferencesContentPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a588e13eaa5fef1f93443445c15256037a70771260e24ca4ee6700e55519bf50
+size 27107
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.preferences.root_null_DefaultGroup_PreferencesContentPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.preferences.root_null_DefaultGroup_PreferencesContentPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..9270e28bd0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.preferences.root_null_DefaultGroup_PreferencesContentPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:58f4e6a4bed5bb2fe4cf2556ee04984dc3efb78ccc4bc501f98f733d1d849289
+size 26636
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.bugreport_null_DefaultGroup_BugReportContentPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.bugreport_null_DefaultGroup_BugReportContentPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..91b32ac1a3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.bugreport_null_DefaultGroup_BugReportContentPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9cf66a0074d1d7842ee176eb6a5e655fe9ed734a95078731cdacd4d7d8f2a80c
+size 49391
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.bugreport_null_DefaultGroup_BugReportContentPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.bugreport_null_DefaultGroup_BugReportContentPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..2b78c65466
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.bugreport_null_DefaultGroup_BugReportContentPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8720a78b76ddac0f5194fd27a991f871cd49e5d51c999cf807493a790ad642c3
+size 48739
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.crash.ui_null_DefaultGroup_CrashDetectionContentPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.crash.ui_null_DefaultGroup_CrashDetectionContentPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..706c00952e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.crash.ui_null_DefaultGroup_CrashDetectionContentPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e04b3a5a4db346d18666fc4df81fdbb1fa84dc526020fdc257c33190e122c11f
+size 29757
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.crash.ui_null_DefaultGroup_CrashDetectionContentPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.crash.ui_null_DefaultGroup_CrashDetectionContentPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..a6318eea0e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.crash.ui_null_DefaultGroup_CrashDetectionContentPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:af0d4d6e3dcda60a7aa4aa20df2b012359c21d51bc9b94e4ed04b19b1d8f38e4
+size 28409
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.detection_null_DefaultGroup_RageshakeDialogContentPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.detection_null_DefaultGroup_RageshakeDialogContentPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..20a47c41b4
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.detection_null_DefaultGroup_RageshakeDialogContentPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a4c1e1b89f3692c1441ec502afe20c1b1d3746c996435ba7abfd624ee88995dc
+size 34328
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.detection_null_DefaultGroup_RageshakeDialogContentPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.detection_null_DefaultGroup_RageshakeDialogContentPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..1a442816eb
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.detection_null_DefaultGroup_RageshakeDialogContentPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c74aecba85977483d6415f6cacc2a45199f763773fde1a590a96d43d83b2de0e
+size 32407
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.preferences_null_DefaultGroup_RageshakePreferenceNotSupportedPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.preferences_null_DefaultGroup_RageshakePreferenceNotSupportedPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..003c8fc202
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.preferences_null_DefaultGroup_RageshakePreferenceNotSupportedPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0a0c3a23a06fa723cf6474fda8f8fb88f876b78d7293121ffb438eddacb834f8
+size 4895
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.preferences_null_DefaultGroup_RageshakePreferenceNotSupportedPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.preferences_null_DefaultGroup_RageshakePreferenceNotSupportedPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..b6139bebf3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.preferences_null_DefaultGroup_RageshakePreferenceNotSupportedPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0b5eacc8332a8ec38df9a710d60b9ec87ecab771c764f0ae5f4e7e7dc6fa7cd7
+size 19974
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.preferences_null_DefaultGroup_RageshakePreferencesViewPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.preferences_null_DefaultGroup_RageshakePreferencesViewPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..e78a01a2ea
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.preferences_null_DefaultGroup_RageshakePreferencesViewPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:685ec863756abd3036b9db5303684b7a347ae148973670bfbedca421b0d0c464
+size 15532
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.preferences_null_DefaultGroup_RageshakePreferencesViewPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.preferences_null_DefaultGroup_RageshakePreferencesViewPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..0241c610bc
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.rageshake.preferences_null_DefaultGroup_RageshakePreferencesViewPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:07c3417b4b61c9c269e8701b0f48ee68f429882f9da53b2a6440c181b62083ec
+size 21942
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.roomlist_null_DefaultGroup_RoomListViewPreview_,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.roomlist_null_DefaultGroup_RoomListViewPreview_,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..d3499bc2fc
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.roomlist_null_DefaultGroup_RoomListViewPreview_,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b76198faaae0e5bdc4d7ace41cad029f48603c2ee5dcb8e246cfeb524c1d39ad
+size 35856
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.roomlist_null_DefaultGroup_RoomListViewPreview_,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.roomlist_null_DefaultGroup_RoomListViewPreview_,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..fad4fef94a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.features.roomlist_null_DefaultGroup_RoomListViewPreview_,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c1aea2bc7f73fd2b3a7d11fcab917e61025dcd536bb912a4bfd7d0ceb33e726a
+size 33553
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.tests.uitests_null_Buttons_Showkasebutton_null,NEXUS_5,1,dark,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.tests.uitests_null_Buttons_Showkasebutton_null,NEXUS_5,1,dark,en].png
new file mode 100644
index 0000000000..687e51b32c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.tests.uitests_null_Buttons_Showkasebutton_null,NEXUS_5,1,dark,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:35d660e370ec4f7f4e0165418a2981f1afe057cac312ee9cad1c4a4dcc02f189
+size 10386
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.tests.uitests_null_Buttons_Showkasebutton_null,NEXUS_5,1,light,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.tests.uitests_null_Buttons_Showkasebutton_null,NEXUS_5,1,light,en].png
new file mode 100644
index 0000000000..836e7c7486
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.x.tests.uitests_ScreenshotTest_preview_tests[io.element.android.x.tests.uitests_null_Buttons_Showkasebutton_null,NEXUS_5,1,light,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dfe01261ef45999f2ffa17069ae8488af38677362550a735b26d7dedee48c75a
+size 9786
diff --git a/tools/detekt/license.template b/tools/detekt/license.template
index c35822ad85..63b899da9b 100644
--- a/tools/detekt/license.template
+++ b/tools/detekt/license.template
@@ -1,11 +1,11 @@
/\*
- \* Copyright \(c\) 20\d\d New Vector Ltd
+(.*\n)* \* Copyright \(c\) 20\d\d New Vector Ltd(.*\n)*
\*
\* 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
+ \* http(s)?://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,
diff --git a/tools/git/validate_lfs.sh b/tools/git/validate_lfs.sh
new file mode 100755
index 0000000000..ce121057b6
--- /dev/null
+++ b/tools/git/validate_lfs.sh
@@ -0,0 +1,29 @@
+#! /bin/bash
+
+#
+# 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.
+#
+# Based on https://cashapp.github.io/paparazzi/#git-lfs
+
+# Compare the output of `git ls-files ':(attr:filter=lfs)'` against `git lfs ls-files`
+# If there's no diff we assume the files have been committed using git lfs
+diff <(git ls-files ':(attr:filter=lfs)' | sort) <(git lfs ls-files -n | sort) >/dev/null
+
+ret=$?
+if [[ $ret -ne 0 ]]; then
+ echo >&2 "Detected files committed without using Git LFS."
+ echo >&2 "Install git lfs (eg brew install git-lfs) and run 'git lfs install --local' within the root repository directory and re-commit your files."
+ exit 1
+fi