test: add accessibility tests for RecordsScreen and PlayerScreen

This commit is contained in:
Antoine Jaury committed 2026-10-23 15:04:57 +02:00
1 parent abeb484258
commit 2c1de5cf37
4 files changed
+195

No files matched your search

+20
View File
@@ -6,6 +6,16 @@ plugins {
kotlin {
androidLibrary {
namespace = "bzh.ajaury.chombev.records"
// Packages the Compose resources (strings of the screens) into the device test APK.
androidResources {
enable = true
}
// Accessibility tests: they run the Accessibility Test Framework checks, which need an
// Android device or emulator on API 34+ (Robolectric does not support them).
withDeviceTestBuilder {}.configure {
instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
}
sourceSets {
@@ -45,6 +55,16 @@ kotlin {
implementation(libs.koin.core.viewmodel)
implementation(libs.koin.compose.viewmodel)
}
getByName("androidDeviceTest").dependencies {
implementation(libs.kotlin.test)
implementation(libs.androidx.testRunner)
// Espresso 3.5, which Compose UI tests depend on, breaks on API 37 (InputManager.getInstance).
implementation(libs.androidx.espressoCore)
implementation(libs.compose.uiTest)
implementation(libs.compose.uiTestAccessibility)
// Declares the activity the UI tests host their content in.
implementation(libs.compose.uiTestManifest)
}
}
}
@@ -0,0 +1,100 @@
package bzh.ajaury.chombev.player.ui
import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.accessibility.enableAccessibilityChecks
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.tryPerformAccessibilityChecks
import androidx.compose.ui.test.v2.runComposeUiTest
import androidx.test.filters.SdkSuppress
import bzh.ajaury.chombev.core.model.Phrase
import bzh.ajaury.chombev.core.model.Record
import bzh.ajaury.chombev.player.model.PlaybackState
import bzh.ajaury.chombev.player.model.PlaybackTiming
import bzh.ajaury.chombev.player.model.PlayerState
import bzh.ajaury.chombev.player.ui.viewmodel.PlayerUiState
import bzh.ajaury.chombev.subtitle.model.SubtitleLine
import kotlin.test.Test
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
/**
* Runs the Accessibility Test Framework checks (labels, touch targets, contrast, traversal order...)
* on the player, in each state that changes its controls.
*/
@OptIn(ExperimentalTestApi::class)
@SdkSuppress(minSdkVersion = 34)
class PlayerScreenAccessibilityTest {
@Test
fun playingPlayerPassesAccessibilityChecks() =
assertPassesAccessibilityChecks(uiState(playbackState = PlaybackState.PLAYING))
@Test
fun pausedPlayerPassesAccessibilityChecks() =
assertPassesAccessibilityChecks(uiState(playbackState = PlaybackState.PAUSED))
@Test
fun endedPlayerPassesAccessibilityChecks() =
assertPassesAccessibilityChecks(uiState(playbackState = PlaybackState.ENDED))
@Test
fun playerOnFirstSentencePassesAccessibilityChecks() =
assertPassesAccessibilityChecks(uiState(position = 0.seconds, currentSubtitleIndex = 0))
@Test
fun playerOnLastSentencePassesAccessibilityChecks() = assertPassesAccessibilityChecks(
uiState(position = 8.seconds, currentSubtitleIndex = subtitleLines.lastIndex),
)
private fun assertPassesAccessibilityChecks(uiState: PlayerUiState) = runComposeUiTest {
enableAccessibilityChecks()
setContent {
MaterialTheme {
PlayerScreen(uiState = uiState)
}
}
onRoot().tryPerformAccessibilityChecks()
}
private companion object {
val record = Record(
id = 1,
index = 1,
emoji = "🙏",
title = Phrase(transcription = "An onestiz", translation = "La politesse"),
audioResourcePath = "",
subtitleResourcePath = "",
)
val subtitleLines = listOf(
SubtitleLine(
startTime = 0.seconds,
phrase = Phrase(transcription = "Demat deoc'h !", translation = "Bonjour !"),
),
SubtitleLine(
startTime = 4.seconds,
phrase = Phrase(transcription = "Kenavo !", translation = "Au revoir !"),
),
SubtitleLine(
startTime = 7.seconds,
phrase = Phrase(transcription = "Kenavo emberr !", translation = "À ce soir !"),
),
)
fun uiState(
playbackState: PlaybackState = PlaybackState.PLAYING,
position: Duration = 5.seconds,
currentSubtitleIndex: Int = 1,
) = PlayerUiState(
record = record,
playerState = PlayerState(
playbackState = playbackState,
playbackTiming = PlaybackTiming(position = position, duration = 10.seconds),
subtitleLines = subtitleLines,
currentSubtitleIndex = currentSubtitleIndex,
),
)
}
}
@@ -0,0 +1,67 @@
package bzh.ajaury.chombev.records.ui
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.accessibility.enableAccessibilityChecks
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.tryPerformAccessibilityChecks
import androidx.compose.ui.test.v2.runComposeUiTest
import androidx.test.filters.SdkSuppress
import bzh.ajaury.chombev.core.model.Phrase
import bzh.ajaury.chombev.core.model.Record
import kotlin.test.Test
/**
* Runs the Accessibility Test Framework checks (labels, touch targets, contrast, traversal order...)
* on the list of records.
*/
@OptIn(ExperimentalTestApi::class)
@SdkSuppress(minSdkVersion = 34)
class RecordsScreenAccessibilityTest {
@Test
fun recordsScreenPassesAccessibilityChecks() = runComposeUiTest {
enableAccessibilityChecks()
setContent {
MaterialTheme {
RecordsScreen(
records = records,
contentPadding = PaddingValues(),
onRecordClick = {},
)
}
}
onRoot().tryPerformAccessibilityChecks()
}
private companion object {
val records = listOf(
Record(
id = 1,
index = 1,
emoji = "🙏",
title = Phrase(transcription = "An onestiz", translation = "La politesse"),
audioResourcePath = "",
subtitleResourcePath = "",
),
Record(
id = 2,
index = 2,
emoji = "👋",
title = Phrase(transcription = "En em brezantiñ", translation = "Se présenter"),
audioResourcePath = "",
subtitleResourcePath = "",
),
Record(
id = 3,
index = 3,
emoji = "🌦️",
title = Phrase(transcription = "An amzer", translation = null),
audioResourcePath = "",
subtitleResourcePath = "",
),
)
}
}