test: add accessibility tests for RecordsScreen and PlayerScreen
This commit is contained in:
1 parent
abeb484258
commit
2c1de5cf37
4 files changed
+195
No files matched your search
@@ -6,6 +6,16 @@ plugins {
|
|||||||
kotlin {
|
kotlin {
|
||||||
androidLibrary {
|
androidLibrary {
|
||||||
namespace = "bzh.ajaury.chombev.records"
|
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 {
|
sourceSets {
|
||||||
@@ -45,6 +55,16 @@ kotlin {
|
|||||||
implementation(libs.koin.core.viewmodel)
|
implementation(libs.koin.core.viewmodel)
|
||||||
implementation(libs.koin.compose.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+100
@@ -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,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+67
@@ -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 = "",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,10 @@ androidCompileSdk = "37" # https://developer.android.com/tools/releas
|
|||||||
androidMinSdk = "24" # https://developer.android.com/tools/releases/platforms
|
androidMinSdk = "24" # https://developer.android.com/tools/releases/platforms
|
||||||
androidTargetSdk = "37" # https://developer.android.com/tools/releases/platforms
|
androidTargetSdk = "37" # https://developer.android.com/tools/releases/platforms
|
||||||
androidxActivity = "1.13.0" # https://developer.android.com/jetpack/androidx/releases/activity
|
androidxActivity = "1.13.0" # https://developer.android.com/jetpack/androidx/releases/activity
|
||||||
|
androidxComposeUi = "1.12.1" # https://developer.android.com/jetpack/androidx/releases/compose-ui
|
||||||
|
androidxEspresso = "3.7.0" # https://developer.android.com/jetpack/androidx/releases/test
|
||||||
androidxLifecycle = "2.11.0" # https://developer.android.com/jetpack/androidx/releases/lifecycle
|
androidxLifecycle = "2.11.0" # https://developer.android.com/jetpack/androidx/releases/lifecycle
|
||||||
|
androidxTestRunner = "1.7.0" # https://developer.android.com/jetpack/androidx/releases/test
|
||||||
androidxWebkit = "1.17.1" # https://developer.android.com/jetpack/androidx/releases/webkit
|
androidxWebkit = "1.17.1" # https://developer.android.com/jetpack/androidx/releases/webkit
|
||||||
composeIconsExtended = "1.7.3" # https://github.com/JetBrains/compose-multiplatform/releases
|
composeIconsExtended = "1.7.3" # https://github.com/JetBrains/compose-multiplatform/releases
|
||||||
composeMultiplatform = "1.12.1" # https://github.com/JetBrains/compose-multiplatform/releases
|
composeMultiplatform = "1.12.1" # https://github.com/JetBrains/compose-multiplatform/releases
|
||||||
@@ -34,6 +37,8 @@ composeCompiler-gradlePlugin = { module = "org.jetbrains.kotlin:compose-compiler
|
|||||||
|
|
||||||
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivity" }
|
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivity" }
|
||||||
androidx-datastore-preferences-core = { module = "androidx.datastore:datastore-preferences-core", version.ref = "datastore" }
|
androidx-datastore-preferences-core = { module = "androidx.datastore:datastore-preferences-core", version.ref = "datastore" }
|
||||||
|
androidx-espressoCore = { module = "androidx.test.espresso:espresso-core", version.ref = "androidxEspresso" }
|
||||||
|
androidx-testRunner = { module = "androidx.test:runner", version.ref = "androidxTestRunner" }
|
||||||
androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "androidxWebkit" }
|
androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "androidxWebkit" }
|
||||||
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
|
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
|
||||||
compose-components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "composeMultiplatform" }
|
compose-components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "composeMultiplatform" }
|
||||||
@@ -42,6 +47,9 @@ compose-material3 = { module = "org.jetbrains.compose.material3:material3", vers
|
|||||||
compose-material-icons-extended = { module = "org.jetbrains.compose.material:material-icons-extended", version.ref = "composeIconsExtended" }
|
compose-material-icons-extended = { module = "org.jetbrains.compose.material:material-icons-extended", version.ref = "composeIconsExtended" }
|
||||||
compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "composeMultiplatform" }
|
compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "composeMultiplatform" }
|
||||||
compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "composeMultiplatform" }
|
compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "composeMultiplatform" }
|
||||||
|
compose-uiTest = { module = "org.jetbrains.compose.ui:ui-test", version.ref = "composeMultiplatform" }
|
||||||
|
compose-uiTestAccessibility = { module = "androidx.compose.ui:ui-test-accessibility", version.ref = "androidxComposeUi" }
|
||||||
|
compose-uiTestManifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "androidxComposeUi" }
|
||||||
compose-uiTooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "composeMultiplatform" }
|
compose-uiTooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "composeMultiplatform" }
|
||||||
compose-uiToolingPreview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "composeMultiplatform" }
|
compose-uiToolingPreview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "composeMultiplatform" }
|
||||||
jaad = { module = "com.tianscar.javasound:jaad", version.ref = "jaad" }
|
jaad = { module = "com.tianscar.javasound:jaad", version.ref = "jaad" }
|
||||||
|
|||||||
Reference in new issue
Block a user