Skip to content

Commit 6a23266

Browse files
authored
Merge pull request #5214 from element-hq/feature/bma/a11yScreenshotTest
Introduce a11y screenshot test
2 parents 3c2cc5a + cbb55fe commit 6a23266

File tree

6 files changed

+108
-2
lines changed

6 files changed

+108
-2
lines changed

features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import androidx.compose.ui.graphics.Color
3131
import androidx.compose.ui.input.nestedscroll.nestedScroll
3232
import androidx.compose.ui.platform.LocalLayoutDirection
3333
import androidx.compose.ui.res.stringResource
34+
import androidx.compose.ui.tooling.preview.Preview
3435
import androidx.compose.ui.tooling.preview.PreviewParameter
3536
import androidx.compose.ui.unit.dp
3637
import dev.chrisbanes.haze.hazeEffect
@@ -313,3 +314,22 @@ internal fun HomeViewPreview(@PreviewParameter(HomeStateProvider::class) state:
313314
leaveRoomView = {}
314315
)
315316
}
317+
318+
@Preview
319+
@Composable
320+
internal fun HomeViewA11yPreview() = ElementPreview {
321+
HomeView(
322+
homeState = aHomeState(),
323+
onRoomClick = {},
324+
onSettingsClick = {},
325+
onSetUpRecoveryClick = {},
326+
onConfirmRecoveryKeyClick = {},
327+
onStartChatClick = {},
328+
onRoomSettingsClick = {},
329+
onReportRoomClick = {},
330+
onMenuActionClick = {},
331+
onDeclineInviteAndBlockUser = {},
332+
acceptDeclineInviteView = {},
333+
leaveRoomView = {}
334+
)
335+
}

tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.google.common.truth.Truth.assertThat
1212
import com.lemonappdev.konsist.api.Konsist
1313
import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf
1414
import com.lemonappdev.konsist.api.ext.list.withName
15+
import com.lemonappdev.konsist.api.ext.list.withNameEndingWith
1516
import com.lemonappdev.konsist.api.ext.list.withoutName
1617
import com.lemonappdev.konsist.api.verify.assertEmpty
1718
import com.lemonappdev.konsist.api.verify.assertTrue
@@ -32,6 +33,26 @@ class KonsistPreviewTest {
3233
}
3334
}
3435

36+
@Test
37+
fun `Check functions with 'A11yPreview'`() {
38+
Konsist
39+
.scopeFromProject()
40+
.functions()
41+
.withNameEndingWith("A11yPreview")
42+
.assertTrue(
43+
additionalMessage = "Functions with 'A11yPreview' suffix should have '@Preview' annotation and not '@PreviewsDayNight'," +
44+
" should contain 'ElementPreview' composable," +
45+
" should contain the tested view" +
46+
" and should be internal."
47+
) {
48+
val testedView = it.name.removeSuffix("A11yPreview")
49+
it.text.contains("$testedView(") &&
50+
it.hasAllAnnotationsOf(PreviewsDayNight::class).not() &&
51+
it.text.contains("ElementPreview") &&
52+
it.hasInternalModifier
53+
}
54+
}
55+
3556
@Test
3657
fun `Functions with '@PreviewsDayNight' annotation should contain 'ElementPreview' composable`() {
3758
Konsist

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,26 @@ object ComposablePreviewProvider : TestParameter.TestParameterValuesProvider {
2929
AndroidComposablePreviewScanner()
3030
.scanPackageTrees(*PACKAGE_TREES)
3131
.getPreviews()
32+
.filter { composablePreview -> composablePreview.methodName.endsWith("A11yPreview").not() }
3233
.withIndex()
3334
.toList()
3435
}
3536

3637
override fun provideValues(): List<IndexedValue<ComposablePreview<AndroidPreviewInfo>>> = values
3738
}
3839

40+
object ComposableA11yPreviewProvider : TestParameter.TestParameterValuesProvider {
41+
private val values: List<ComposablePreview<AndroidPreviewInfo>> by lazy {
42+
AndroidComposablePreviewScanner()
43+
.scanPackageTrees(*PACKAGE_TREES)
44+
.getPreviews()
45+
.filter { composablePreview -> composablePreview.methodName.endsWith("A11yPreview") }
46+
.toList()
47+
}
48+
49+
override fun provideValues(): List<ComposablePreview<AndroidPreviewInfo>> = values
50+
}
51+
3952
object Shard1ComposablePreviewProvider : TestParameter.TestParameterValuesProvider {
4053
override fun provideValues(): List<ComposablePreview<AndroidPreviewInfo>> =
4154
ComposablePreviewProvider.provideValues().filter { it.index % 4 == 0 }.map { it.value }

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import androidx.compose.ui.platform.LocalInspectionMode
1919
import androidx.compose.ui.unit.Density
2020
import app.cash.paparazzi.DeviceConfig
2121
import app.cash.paparazzi.Paparazzi
22+
import app.cash.paparazzi.RenderExtension
2223
import app.cash.paparazzi.TestName
2324
import com.android.resources.NightMode
2425
import io.element.android.compound.theme.ElementTheme
@@ -112,7 +113,12 @@ fun createScreenshotIdFor(preview: ComposablePreview<AndroidPreviewInfo>) = buil
112113
}.joinToString("_")
113114

114115
object PaparazziPreviewRule {
115-
fun createFor(preview: ComposablePreview<AndroidPreviewInfo>, locale: String, deviceConfig: DeviceConfig = ScreenshotTest.defaultDeviceConfig): Paparazzi {
116+
fun createFor(
117+
preview: ComposablePreview<AndroidPreviewInfo>,
118+
locale: String,
119+
deviceConfig: DeviceConfig = ScreenshotTest.defaultDeviceConfig,
120+
renderExtensions: Set<RenderExtension> = setOf(),
121+
): Paparazzi {
116122
val densityScale = deviceConfig.density.dpiValue / 160f
117123
val customScreenHeight = preview.previewInfo.heightDp.takeIf { it >= 0 }?.let { it * densityScale }?.toInt()
118124
return Paparazzi(
@@ -125,7 +131,8 @@ object PaparazziPreviewRule {
125131
softButtons = false,
126132
screenHeight = customScreenHeight ?: deviceConfig.screenHeight,
127133
),
128-
maxPercentDifference = 0.01
134+
maxPercentDifference = 0.01,
135+
renderExtensions = renderExtensions,
129136
)
130137
}
131138
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package ui
9+
10+
import app.cash.paparazzi.accessibility.AccessibilityRenderExtension
11+
import base.ComposableA11yPreviewProvider
12+
import base.PaparazziPreviewRule
13+
import base.ScreenshotTest
14+
import com.google.testing.junit.testparameterinjector.TestParameter
15+
import com.google.testing.junit.testparameterinjector.TestParameterInjector
16+
import org.junit.Rule
17+
import org.junit.Test
18+
import org.junit.runner.RunWith
19+
import sergio.sastre.composable.preview.scanner.android.AndroidPreviewInfo
20+
import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview
21+
22+
/**
23+
* Test that takes a preview and runs a screenshot test on it.
24+
* It uses [ComposableA11yPreviewProvider] to test only previews that ends with "A11yPreview".
25+
*/
26+
@RunWith(TestParameterInjector::class)
27+
class PreviewA11yTest(
28+
@TestParameter(valuesProvider = ComposableA11yPreviewProvider::class)
29+
val preview: ComposablePreview<AndroidPreviewInfo>,
30+
) {
31+
@get:Rule
32+
val paparazziRule = PaparazziPreviewRule.createFor(
33+
preview = preview,
34+
locale = "en",
35+
renderExtensions = setOf(AccessibilityRenderExtension()),
36+
)
37+
38+
@Test
39+
fun snapshot() {
40+
ScreenshotTest.runTest(paparazzi = paparazziRule, preview = preview, localeStr = "en")
41+
}
42+
}
Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)