Merge pull request #5214 from element-hq/feature/bma/a11yScreenshotTest
Introduce a11y screenshot test
This commit is contained in:
commit
61e2ae2606
6 changed files with 108 additions and 2 deletions
|
|
@ -31,6 +31,7 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import dev.chrisbanes.haze.hazeEffect
|
import dev.chrisbanes.haze.hazeEffect
|
||||||
|
|
@ -313,3 +314,22 @@ internal fun HomeViewPreview(@PreviewParameter(HomeStateProvider::class) state:
|
||||||
leaveRoomView = {}
|
leaveRoomView = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
internal fun HomeViewA11yPreview() = ElementPreview {
|
||||||
|
HomeView(
|
||||||
|
homeState = aHomeState(),
|
||||||
|
onRoomClick = {},
|
||||||
|
onSettingsClick = {},
|
||||||
|
onSetUpRecoveryClick = {},
|
||||||
|
onConfirmRecoveryKeyClick = {},
|
||||||
|
onStartChatClick = {},
|
||||||
|
onRoomSettingsClick = {},
|
||||||
|
onReportRoomClick = {},
|
||||||
|
onMenuActionClick = {},
|
||||||
|
onDeclineInviteAndBlockUser = {},
|
||||||
|
acceptDeclineInviteView = {},
|
||||||
|
leaveRoomView = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import com.google.common.truth.Truth.assertThat
|
||||||
import com.lemonappdev.konsist.api.Konsist
|
import com.lemonappdev.konsist.api.Konsist
|
||||||
import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf
|
import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf
|
||||||
import com.lemonappdev.konsist.api.ext.list.withName
|
import com.lemonappdev.konsist.api.ext.list.withName
|
||||||
|
import com.lemonappdev.konsist.api.ext.list.withNameEndingWith
|
||||||
import com.lemonappdev.konsist.api.ext.list.withoutName
|
import com.lemonappdev.konsist.api.ext.list.withoutName
|
||||||
import com.lemonappdev.konsist.api.verify.assertEmpty
|
import com.lemonappdev.konsist.api.verify.assertEmpty
|
||||||
import com.lemonappdev.konsist.api.verify.assertTrue
|
import com.lemonappdev.konsist.api.verify.assertTrue
|
||||||
|
|
@ -32,6 +33,26 @@ class KonsistPreviewTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Check functions with 'A11yPreview'`() {
|
||||||
|
Konsist
|
||||||
|
.scopeFromProject()
|
||||||
|
.functions()
|
||||||
|
.withNameEndingWith("A11yPreview")
|
||||||
|
.assertTrue(
|
||||||
|
additionalMessage = "Functions with 'A11yPreview' suffix should have '@Preview' annotation and not '@PreviewsDayNight'," +
|
||||||
|
" should contain 'ElementPreview' composable," +
|
||||||
|
" should contain the tested view" +
|
||||||
|
" and should be internal."
|
||||||
|
) {
|
||||||
|
val testedView = it.name.removeSuffix("A11yPreview")
|
||||||
|
it.text.contains("$testedView(") &&
|
||||||
|
it.hasAllAnnotationsOf(PreviewsDayNight::class).not() &&
|
||||||
|
it.text.contains("ElementPreview") &&
|
||||||
|
it.hasInternalModifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Functions with '@PreviewsDayNight' annotation should contain 'ElementPreview' composable`() {
|
fun `Functions with '@PreviewsDayNight' annotation should contain 'ElementPreview' composable`() {
|
||||||
Konsist
|
Konsist
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ object ComposablePreviewProvider : TestParameter.TestParameterValuesProvider {
|
||||||
AndroidComposablePreviewScanner()
|
AndroidComposablePreviewScanner()
|
||||||
.scanPackageTrees(*PACKAGE_TREES)
|
.scanPackageTrees(*PACKAGE_TREES)
|
||||||
.getPreviews()
|
.getPreviews()
|
||||||
|
.filter { composablePreview -> composablePreview.methodName.endsWith("A11yPreview").not() }
|
||||||
.withIndex()
|
.withIndex()
|
||||||
.toList()
|
.toList()
|
||||||
}
|
}
|
||||||
|
|
@ -36,6 +37,18 @@ object ComposablePreviewProvider : TestParameter.TestParameterValuesProvider {
|
||||||
override fun provideValues(): List<IndexedValue<ComposablePreview<AndroidPreviewInfo>>> = values
|
override fun provideValues(): List<IndexedValue<ComposablePreview<AndroidPreviewInfo>>> = values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object ComposableA11yPreviewProvider : TestParameter.TestParameterValuesProvider {
|
||||||
|
private val values: List<ComposablePreview<AndroidPreviewInfo>> by lazy {
|
||||||
|
AndroidComposablePreviewScanner()
|
||||||
|
.scanPackageTrees(*PACKAGE_TREES)
|
||||||
|
.getPreviews()
|
||||||
|
.filter { composablePreview -> composablePreview.methodName.endsWith("A11yPreview") }
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun provideValues(): List<ComposablePreview<AndroidPreviewInfo>> = values
|
||||||
|
}
|
||||||
|
|
||||||
object Shard1ComposablePreviewProvider : TestParameter.TestParameterValuesProvider {
|
object Shard1ComposablePreviewProvider : TestParameter.TestParameterValuesProvider {
|
||||||
override fun provideValues(): List<ComposablePreview<AndroidPreviewInfo>> =
|
override fun provideValues(): List<ComposablePreview<AndroidPreviewInfo>> =
|
||||||
ComposablePreviewProvider.provideValues().filter { it.index % 4 == 0 }.map { it.value }
|
ComposablePreviewProvider.provideValues().filter { it.index % 4 == 0 }.map { it.value }
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import androidx.compose.ui.platform.LocalInspectionMode
|
||||||
import androidx.compose.ui.unit.Density
|
import androidx.compose.ui.unit.Density
|
||||||
import app.cash.paparazzi.DeviceConfig
|
import app.cash.paparazzi.DeviceConfig
|
||||||
import app.cash.paparazzi.Paparazzi
|
import app.cash.paparazzi.Paparazzi
|
||||||
|
import app.cash.paparazzi.RenderExtension
|
||||||
import app.cash.paparazzi.TestName
|
import app.cash.paparazzi.TestName
|
||||||
import com.android.resources.NightMode
|
import com.android.resources.NightMode
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
|
|
@ -112,7 +113,12 @@ fun createScreenshotIdFor(preview: ComposablePreview<AndroidPreviewInfo>) = buil
|
||||||
}.joinToString("_")
|
}.joinToString("_")
|
||||||
|
|
||||||
object PaparazziPreviewRule {
|
object PaparazziPreviewRule {
|
||||||
fun createFor(preview: ComposablePreview<AndroidPreviewInfo>, locale: String, deviceConfig: DeviceConfig = ScreenshotTest.defaultDeviceConfig): Paparazzi {
|
fun createFor(
|
||||||
|
preview: ComposablePreview<AndroidPreviewInfo>,
|
||||||
|
locale: String,
|
||||||
|
deviceConfig: DeviceConfig = ScreenshotTest.defaultDeviceConfig,
|
||||||
|
renderExtensions: Set<RenderExtension> = setOf(),
|
||||||
|
): Paparazzi {
|
||||||
val densityScale = deviceConfig.density.dpiValue / 160f
|
val densityScale = deviceConfig.density.dpiValue / 160f
|
||||||
val customScreenHeight = preview.previewInfo.heightDp.takeIf { it >= 0 }?.let { it * densityScale }?.toInt()
|
val customScreenHeight = preview.previewInfo.heightDp.takeIf { it >= 0 }?.let { it * densityScale }?.toInt()
|
||||||
return Paparazzi(
|
return Paparazzi(
|
||||||
|
|
@ -125,7 +131,8 @@ object PaparazziPreviewRule {
|
||||||
softButtons = false,
|
softButtons = false,
|
||||||
screenHeight = customScreenHeight ?: deviceConfig.screenHeight,
|
screenHeight = customScreenHeight ?: deviceConfig.screenHeight,
|
||||||
),
|
),
|
||||||
maxPercentDifference = 0.01
|
maxPercentDifference = 0.01,
|
||||||
|
renderExtensions = renderExtensions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
42
tests/uitests/src/test/kotlin/ui/PreviewA11yTest.kt
Normal file
42
tests/uitests/src/test/kotlin/ui/PreviewA11yTest.kt
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import app.cash.paparazzi.accessibility.AccessibilityRenderExtension
|
||||||
|
import base.ComposableA11yPreviewProvider
|
||||||
|
import base.PaparazziPreviewRule
|
||||||
|
import base.ScreenshotTest
|
||||||
|
import com.google.testing.junit.testparameterinjector.TestParameter
|
||||||
|
import com.google.testing.junit.testparameterinjector.TestParameterInjector
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import sergio.sastre.composable.preview.scanner.android.AndroidPreviewInfo
|
||||||
|
import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that takes a preview and runs a screenshot test on it.
|
||||||
|
* It uses [ComposableA11yPreviewProvider] to test only previews that ends with "A11yPreview".
|
||||||
|
*/
|
||||||
|
@RunWith(TestParameterInjector::class)
|
||||||
|
class PreviewA11yTest(
|
||||||
|
@TestParameter(valuesProvider = ComposableA11yPreviewProvider::class)
|
||||||
|
val preview: ComposablePreview<AndroidPreviewInfo>,
|
||||||
|
) {
|
||||||
|
@get:Rule
|
||||||
|
val paparazziRule = PaparazziPreviewRule.createFor(
|
||||||
|
preview = preview,
|
||||||
|
locale = "en",
|
||||||
|
renderExtensions = setOf(AccessibilityRenderExtension()),
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun snapshot() {
|
||||||
|
ScreenshotTest.runTest(paparazzi = paparazziRule, preview = preview, localeStr = "en")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:21c09cac237b2b4823dbb945161c6d6d574dc2c8ffb37088696d09736d8e782a
|
||||||
|
size 124636
|
||||||
Loading…
Add table
Add a link
Reference in a new issue