From 77fa9dc1dc9e68a797cc942405a9134dd52a27df Mon Sep 17 00:00:00 2001 From: takahirom Date: Wed, 20 Nov 2024 11:18:50 +0900 Subject: [PATCH] Refactor to make AccessibilityChecker configurable for each checkRoboAccessibility. --- .../roborazzi/AccessibilityChecker.kt | 3 ++ .../takahirom/roborazzi/RoborazziContext.kt | 16 ++++++++ .../RoborazziATFAccessibilityChecker.kt | 41 +++++++++++-------- .../takahirom/roborazzi/RoborazziRule.kt | 13 +++--- .../roborazzi/sample/ComposeA11yTest.kt | 10 ++--- .../sample/ComposeA11yWithCustomCheckTest.kt | 14 +++---- .../roborazzi/sample/ViewA11yTest.kt | 12 +++--- 7 files changed, 68 insertions(+), 41 deletions(-) create mode 100644 include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/AccessibilityChecker.kt diff --git a/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/AccessibilityChecker.kt b/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/AccessibilityChecker.kt new file mode 100644 index 000000000..f8712f784 --- /dev/null +++ b/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/AccessibilityChecker.kt @@ -0,0 +1,3 @@ +package com.github.takahirom.roborazzi + +interface AccessibilityChecker \ No newline at end of file diff --git a/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziContext.kt b/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziContext.kt index 15ce447e7..9aadf778f 100644 --- a/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziContext.kt +++ b/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziContext.kt @@ -63,6 +63,22 @@ class RoborazziContextImpl { ruleOverrideImageExtension = null } + private var ruleOverrideAccessibilityChecker: AccessibilityChecker? = null + + @InternalRoborazziApi + fun setRuleOverrideAccessibilityChecker(checker: AccessibilityChecker?) { + ruleOverrideAccessibilityChecker = checker + } + + @InternalRoborazziApi + fun clearRuleOverrideAccessibilityChecker() { + ruleOverrideAccessibilityChecker = null + } + + @InternalRoborazziApi + val accessibilityChecker: AccessibilityChecker? + get() = ruleOverrideAccessibilityChecker + @InternalRoborazziApi val imageExtension: String get() = ruleOverrideImageExtension ?: roborazziSystemPropertyImageExtension() diff --git a/roborazzi-accessibility-check/src/main/java/com/github/takahirom/roborazzi/RoborazziATFAccessibilityChecker.kt b/roborazzi-accessibility-check/src/main/java/com/github/takahirom/roborazzi/RoborazziATFAccessibilityChecker.kt index 1647d9d66..b79d7b463 100644 --- a/roborazzi-accessibility-check/src/main/java/com/github/takahirom/roborazzi/RoborazziATFAccessibilityChecker.kt +++ b/roborazzi-accessibility-check/src/main/java/com/github/takahirom/roborazzi/RoborazziATFAccessibilityChecker.kt @@ -25,7 +25,7 @@ import org.hamcrest.Matchers import org.robolectric.shadows.ShadowBuild fun SemanticsNodeInteraction.checkRoboAccessibility( - checker: RoborazziATFAccessibilityChecker = RoborazziATFAccessibilityChecker(), + checker: RoborazziATFAccessibilityChecker = provideATFAccessibilityCheckerOrCreateDefault(), failureLevel: RoborazziATFAccessibilityChecker.CheckLevel = RoborazziATFAccessibilityChecker.CheckLevel.Error, roborazziOptions: RoborazziOptions = provideRoborazziContext().options, ) { @@ -37,7 +37,7 @@ fun SemanticsNodeInteraction.checkRoboAccessibility( } fun ViewInteraction.checkRoboAccessibility( - checker: RoborazziATFAccessibilityChecker = RoborazziATFAccessibilityChecker(), + checker: RoborazziATFAccessibilityChecker = provideATFAccessibilityCheckerOrCreateDefault(), failureLevel: RoborazziATFAccessibilityChecker.CheckLevel = RoborazziATFAccessibilityChecker.CheckLevel.Error, roborazziOptions: RoborazziOptions = provideRoborazziContext().options, ) { @@ -48,13 +48,18 @@ fun ViewInteraction.checkRoboAccessibility( ) } +private fun provideATFAccessibilityCheckerOrCreateDefault() = + ((provideRoborazziContext().accessibilityChecker as? RoborazziATFAccessibilityChecker) + ?: RoborazziATFAccessibilityChecker()) + + @ExperimentalRoborazziApi data class RoborazziATFAccessibilityChecker( val checks: Set = AccessibilityCheckPreset.getAccessibilityHierarchyChecksForPreset( AccessibilityCheckPreset.LATEST ), val suppressions: Matcher = Matchers.not(Matchers.anything()), -) { +) : AccessibilityChecker { constructor( preset: AccessibilityCheckPreset, suppressions: Matcher = Matchers.not(Matchers.anything()), @@ -201,25 +206,25 @@ data class RoborazziATFAccessibilityChecker( @ExperimentalRoborazziApi data class AccessibilityCheckAfterTest( - val checker: RoborazziATFAccessibilityChecker = RoborazziATFAccessibilityChecker(), val failureLevel: RoborazziATFAccessibilityChecker.CheckLevel = RoborazziATFAccessibilityChecker.CheckLevel.Error, -) : RoborazziRule.AccessibilityChecks { +) : RoborazziRule.AccessibilityCheckStrategy { override fun runAccessibilityChecks( captureRoot: CaptureRoot, roborazziOptions: RoborazziOptions ) { - checker.runAccessibilityChecks( - checkNode = when (captureRoot) { - is CaptureRoot.Compose -> RoborazziATFAccessibilityChecker.CheckNode.Compose( - semanticsNodeInteraction = captureRoot.semanticsNodeInteraction - ) + provideATFAccessibilityCheckerOrCreateDefault() + .runAccessibilityChecks( + checkNode = when (captureRoot) { + is CaptureRoot.Compose -> RoborazziATFAccessibilityChecker.CheckNode.Compose( + semanticsNodeInteraction = captureRoot.semanticsNodeInteraction + ) - CaptureRoot.None -> return - is CaptureRoot.View -> RoborazziATFAccessibilityChecker.CheckNode.View( - viewInteraction = captureRoot.viewInteraction - ) - }, - roborazziOptions = roborazziOptions, - failureLevel = failureLevel, - ) + CaptureRoot.None -> return + is CaptureRoot.View -> RoborazziATFAccessibilityChecker.CheckNode.View( + viewInteraction = captureRoot.viewInteraction + ) + }, + roborazziOptions = roborazziOptions, + failureLevel = failureLevel, + ) } } diff --git a/roborazzi-junit-rule/src/main/java/com/github/takahirom/roborazzi/RoborazziRule.kt b/roborazzi-junit-rule/src/main/java/com/github/takahirom/roborazzi/RoborazziRule.kt index bf1d456b5..674d1afcb 100644 --- a/roborazzi-junit-rule/src/main/java/com/github/takahirom/roborazzi/RoborazziRule.kt +++ b/roborazzi-junit-rule/src/main/java/com/github/takahirom/roborazzi/RoborazziRule.kt @@ -56,18 +56,19 @@ class RoborazziRule private constructor( val outputFileProvider: FileProvider = provideRoborazziContext().fileProvider ?: defaultFileProvider, val roborazziOptions: RoborazziOptions = provideRoborazziContext().options, - val accessibilityChecks: AccessibilityChecks = AccessibilityChecks.Disabled, + val accessibilityChecker: AccessibilityChecker? = null, + val accessibilityCheckStrategy: AccessibilityCheckStrategy = AccessibilityCheckStrategy.None, ) @ExperimentalRoborazziApi - interface AccessibilityChecks { + interface AccessibilityCheckStrategy { @InternalRoborazziApi fun runAccessibilityChecks( captureRoot: CaptureRoot, roborazziOptions: RoborazziOptions, ) - // Use `roborazzi-accessibility-check`'s ValidateAfterTest - data object Disabled : AccessibilityChecks { + // Use `roborazzi-accessibility-check`'s AccessibilityCheckAfterTest + data object None : AccessibilityCheckStrategy { override fun runAccessibilityChecks( captureRoot: CaptureRoot, roborazziOptions: RoborazziOptions @@ -161,12 +162,14 @@ class RoborazziRule private constructor( provideRoborazziContext().setRuleOverrideRoborazziOptions(options.roborazziOptions) provideRoborazziContext().setRuleOverrideFileProvider(options.outputFileProvider) provideRoborazziContext().setRuleOverrideDescription(description) + provideRoborazziContext().setRuleOverrideAccessibilityChecker(options.accessibilityChecker) runTest(base, description, captureRoot) } finally { provideRoborazziContext().clearRuleOverrideOutputDirectory() provideRoborazziContext().clearRuleOverrideRoborazziOptions() provideRoborazziContext().clearRuleOverrideFileProvider() provideRoborazziContext().clearRuleOverrideDescription() + provideRoborazziContext().clearRuleOverrideAccessibilityChecker() } } } @@ -179,7 +182,7 @@ class RoborazziRule private constructor( ) { val evaluate: () -> Unit = { try { - val accessibilityChecks = options.accessibilityChecks + val accessibilityChecks = options.accessibilityCheckStrategy // TODO enable a11y before showing content base.evaluate() diff --git a/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/ComposeA11yTest.kt b/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/ComposeA11yTest.kt index dfdf13e3a..4cbcf888b 100644 --- a/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/ComposeA11yTest.kt +++ b/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/ComposeA11yTest.kt @@ -54,11 +54,11 @@ class ComposeA11yTest { composeRule = composeTestRule, captureRoot = composeTestRule.onRoot(), options = Options( - accessibilityChecks = AccessibilityCheckAfterTest( - checker = RoborazziATFAccessibilityChecker( - preset = AccessibilityCheckPreset.LATEST, - suppressions = matchesElements(withTestTag("suppress")) - ), + accessibilityChecker = RoborazziATFAccessibilityChecker( + preset = AccessibilityCheckPreset.LATEST, + suppressions = matchesElements(withTestTag("suppress")) + ), + accessibilityCheckStrategy = AccessibilityCheckAfterTest( failureLevel = RoborazziATFAccessibilityChecker.CheckLevel.Warning, ) ) diff --git a/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/ComposeA11yWithCustomCheckTest.kt b/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/ComposeA11yWithCustomCheckTest.kt index c252ca9ca..b421e540b 100644 --- a/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/ComposeA11yWithCustomCheckTest.kt +++ b/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/ComposeA11yWithCustomCheckTest.kt @@ -14,11 +14,11 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onRoot import androidx.compose.ui.unit.dp import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.github.takahirom.roborazzi.RoborazziATFAccessibilityChecker +import com.github.takahirom.roborazzi.AccessibilityCheckAfterTest import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers +import com.github.takahirom.roborazzi.RoborazziATFAccessibilityChecker import com.github.takahirom.roborazzi.RoborazziRule import com.github.takahirom.roborazzi.RoborazziRule.Options -import com.github.takahirom.roborazzi.AccessibilityCheckAfterTest import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult.AccessibilityCheckResultType import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult.AccessibilityCheckResultType.ERROR import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult.AccessibilityCheckResultType.INFO @@ -59,11 +59,11 @@ class ComposeA11yWithCustomCheckTest { composeRule = composeTestRule, captureRoot = composeTestRule.onRoot(), options = Options( - accessibilityChecks = AccessibilityCheckAfterTest( - checker = RoborazziATFAccessibilityChecker( - checks = setOf(NoRedTextCheck()), - suppressions = matchesElements(withTestTag("suppress")) - ), + accessibilityChecker = RoborazziATFAccessibilityChecker( + checks = setOf(NoRedTextCheck()), + suppressions = matchesElements(withTestTag("suppress")) + ), + accessibilityCheckStrategy = AccessibilityCheckAfterTest( failureLevel = RoborazziATFAccessibilityChecker.CheckLevel.Warning, ) ) diff --git a/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/ViewA11yTest.kt b/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/ViewA11yTest.kt index 7cdc19830..1c70f17e4 100644 --- a/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/ViewA11yTest.kt +++ b/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/ViewA11yTest.kt @@ -11,9 +11,9 @@ import androidx.test.espresso.assertion.ViewAssertions import androidx.test.espresso.matcher.ViewMatchers import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.github.takahirom.roborazzi.RoborazziATFAccessibilityChecker import com.github.takahirom.roborazzi.AccessibilityCheckAfterTest import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers +import com.github.takahirom.roborazzi.RoborazziATFAccessibilityChecker import com.github.takahirom.roborazzi.RoborazziRule import com.github.takahirom.roborazzi.RoborazziRule.Options import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckPreset @@ -41,11 +41,11 @@ class ViewA11yTest { val roborazziRule = RoborazziRule( captureRoot = Espresso.onView(ViewMatchers.isRoot()), options = Options( - accessibilityChecks = AccessibilityCheckAfterTest( - checker = RoborazziATFAccessibilityChecker( - preset = AccessibilityCheckPreset.LATEST, - suppressions = matchesElements(withTestTag("suppress")) - ), + accessibilityChecker = RoborazziATFAccessibilityChecker( + preset = AccessibilityCheckPreset.LATEST, + suppressions = matchesElements(withTestTag("suppress")) + ), + accessibilityCheckStrategy = AccessibilityCheckAfterTest( failureLevel = RoborazziATFAccessibilityChecker.CheckLevel.Warning, ) )