Skip to content

Instantly share code, notes, and snippets.

@Marchuck
Created April 25, 2026 12:04
Show Gist options
  • Select an option

  • Save Marchuck/f7bbd016c679807381a10b4bfbf28446 to your computer and use it in GitHub Desktop.

Select an option

Save Marchuck/f7bbd016c679807381a10b4bfbf28446 to your computer and use it in GitHub Desktop.
automate verification my composables have issues with a11y
package com.marchuck.accessibilitychecks
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.size
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.test.junit4.accessibility.enableAccessibilityChecks
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.printToLog
import androidx.compose.ui.test.tryPerformAccessibilityChecks
import androidx.compose.ui.unit.dp
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheck
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesCheck
import com.google.android.apps.common.testing.accessibility.framework.checks.TouchTargetSizeCheck
import com.google.android.apps.common.testing.accessibility.framework.integrations.espresso.AccessibilityValidator
import com.google.android.apps.common.testing.accessibility.framework.integrations.espresso.AccessibilityViewCheckException
import com.marchuck.accessibilitychecks.ui.theme.SomeTheme
import org.hamcrest.Matchers.not
import org.junit.Before
import org.junit.Rule
import org.junit.Test
class AccessibilityChecksTest {
@get:Rule
val composeTestRule = createComposeRule()
@Before
fun setUp() {
val accessibilityValidator = AccessibilityValidator().setRunChecksFromRootView(true)
composeTestRule.enableAccessibilityChecks(accessibilityValidator)
}
private fun AccessibilityValidator.only(checkClass: Class<out AccessibilityCheck> = TouchTargetSizeCheck::class.java): AccessibilityValidator {
return setSuppressingResultMatcher(
not(matchesCheck(checkClass))
)
}
@Test
fun validateAccessibility_allIssuesDetected() {
composeTestRule.setContent {
SomeTheme {
Column {
Box(
modifier = Modifier
.size(47.dp)
.clickable {}
.semantics {
contentDescription = "box"
},
content = {}
)
}
}
}
composeTestRule.onRoot().printToLog("AccessibilitySemanticsTree")
val exceptionThrown = assertThrows<AccessibilityViewCheckException> {
composeTestRule.onRoot().tryPerformAccessibilityChecks()
}
require("TouchTargetSizeCheck" in exceptionThrown.results.first().toString())
}
private inline fun <reified T : Throwable> assertThrows(block: () -> Unit): T {
try {
block()
} catch (e: Throwable) {
if (e is T) {
return e
} else {
throw AssertionError("expected ${T::class.java} to be thrown, got ${e::class.java}")
}
}
throw AssertionError("expected ${T::class.java} to be thrown, but nothing thrown")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment