Skip to content

Commit

Permalink
Merge pull request #557 from yschimke/add_a11y_checks
Browse files Browse the repository at this point in the history
Add basic ATF A11y checks via AccessibilityCheckAfterTestStrategy and checkRoboAccessibility
  • Loading branch information
takahirom authored Nov 21, 2024
2 parents cff3c92 + 927424b commit 0cf989b
Show file tree
Hide file tree
Showing 28 changed files with 1,142 additions and 22 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,11 @@ If you are having trouble debugging your test, try Dump mode as follows.
![image](https://user-images.githubusercontent.com/1386930/226364158-a07a0fb0-d8e7-46b7-a495-8dd217faaadb.png)
### Accessibility Check
Roborazzi Accessibility Checks is a library that integrates accessibility checks into Roborazzi.
Please refer to [Accessibility Check](https://github.com/takahirom/roborazzi/blob/main/roborazzi-accessibility-check/README.md)
### Roborazzi options
Please check out [RoborazziOptions](https://github.com/takahirom/roborazzi/blob/main/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziOptions.kt) for available Roborazzi options.
Expand Down
5 changes: 5 additions & 0 deletions docs/topics/how_to_use.md
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,11 @@ If you are having trouble debugging your test, try Dump mode as follows.
![image](https://user-images.githubusercontent.com/1386930/226364158-a07a0fb0-d8e7-46b7-a495-8dd217faaadb.png)
### Accessibility Check
Roborazzi Accessibility Checks is a library that integrates accessibility checks into Roborazzi.
Please refer to [Accessibility Check](https://github.com/takahirom/roborazzi/blob/main/roborazzi-accessibility-check/README.md)
### Roborazzi options
Please check out [RoborazziOptions](https://github.com/takahirom/roborazzi/blob/main/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziOptions.kt) for available Roborazzi options.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[versions]
accessibilityTestFramework = "4.1.1"
javaToolchain = "17"
javaTarget = "11"
agp = "8.6.1"
Expand Down Expand Up @@ -49,6 +50,7 @@ webpImageio = "0.3.3"
composable-preview-scanner = "0.4.0"

[libraries]
accessibility-test-framework = { module = "com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework", version.ref = "accessibilityTestFramework" }
roborazzi = { module = "io.github.takahirom.roborazzi:roborazzi", version.ref = "roborazzi-for-replacing-by-include-build" }
roborazzi-junit-rule = { module = "io.github.takahirom.roborazzi:roborazzi-junit-rule", version.ref = "roborazzi-for-replacing-by-include-build" }
roborazzi-rule = { module = "io.github.takahirom.roborazzi:roborazzi-rule", version.ref = "roborazzi-for-replacing-by-include-build" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private fun View.generateBitmap(
val destBitmap = Bitmap.createBitmap(width, height, pixelBitConfig.toBitmapConfig())
when {
Build.VERSION.SDK_INT < 26 -> {
println(
roborazziErrorLog(
"**Warning from Roborazzi**: Robolectric may not function properly under API 26, " +
"specifically it may fail to capture accurate screenshots. " +
"Please add @Config(sdk = [26]) or higher to your test class to ensure proper operation. " +
Expand Down Expand Up @@ -115,7 +115,7 @@ private fun View.generateBitmap(
val window = nullableWindow!!
if (Build.VERSION.SDK_INT < 28) {
// See: https://github.com/robolectric/robolectric/blob/robolectric-4.10.3/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPixelCopy.java#L32
println(
roborazziReportLog(
"PixelCopy is not supported for API levels below 28. Falling back to View#draw instead of PixelCopy. " +
"Consider using API level 28 or higher, e.g., @Config(sdk = [28])."
)
Expand All @@ -124,7 +124,7 @@ private fun View.generateBitmap(
generateBitmapFromPixelCopy(window, destBitmap, bitmapFuture)
}
} else {
println(
roborazziReportLog(
"View.captureToImage Could not find window for view. Falling back to View#draw instead of PixelCopy" +
"(If you are using Material3 dialogs, this is expected).",
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.github.takahirom.roborazzi

@ExperimentalRoborazziApi
interface RoborazziAccessibilityOptions {
// See `roborazzi-accessibility-check`'s [com.github.takahirom.roborazzi.RoborazziATFAccessibilityCheckOptions]
object None: RoborazziAccessibilityOptions
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ class RoborazziContextImpl {
ruleOverrideImageExtension = null
}

private var ruleOverrideRoborazziAccessibilityOptions: RoborazziAccessibilityOptions? = null

@InternalRoborazziApi
fun setRuleOverrideAccessibilityOptions(checker: RoborazziAccessibilityOptions?) {
ruleOverrideRoborazziAccessibilityOptions = checker
}

@InternalRoborazziApi
fun clearRuleOverrideAccessibilityOptions() {
ruleOverrideRoborazziAccessibilityOptions = null
}

@InternalRoborazziApi
val roborazziAccessibilityOptions: RoborazziAccessibilityOptions
get() = ruleOverrideRoborazziAccessibilityOptions ?: RoborazziAccessibilityOptions.None

@InternalRoborazziApi
val imageExtension: String
get() = ruleOverrideImageExtension ?: roborazziSystemPropertyImageExtension()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fun processOutputImageAndReport(
"\ngoldenFile:${goldenFile.absolutePath}"
}
if (taskType.isEnabled() && !roborazziSystemPropertyTaskType().isEnabled()) {
println(
roborazziReportLog(
"Roborazzi Warning:\n" +
"You have specified '$taskType' without the necessary plugin configuration like roborazzi.test.record=true or ./gradlew recordRoborazziDebug.\n" +
"This may complicate your screenshot testing process because the behavior is not changeable. And it doesn't allow Roborazzi plugin to generate test report.\n" +
Expand Down Expand Up @@ -98,10 +98,10 @@ fun processOutputImageAndReport(
)
diffPercentage = comparisonResult.pixelDifferences.toFloat() / comparisonResult.pixelCount
val changed = !compareOptions.resultValidator(comparisonResult)
reportLog("${goldenFile.name} The differ result :$comparisonResult changed:$changed")
roborazziReportLog("${goldenFile.name} The differ result :$comparisonResult changed:$changed")
changed
} else {
reportLog("${goldenFile.name} The image size is changed. actual = (${goldenRoboCanvas.width}, ${goldenRoboCanvas.height}), golden = (${newRoboCanvas.croppedWidth}, ${newRoboCanvas.croppedHeight})")
roborazziReportLog("${goldenFile.name} The image size is changed. actual = (${goldenRoboCanvas.width}, ${goldenRoboCanvas.height}), golden = (${newRoboCanvas.croppedWidth}, ${newRoboCanvas.croppedHeight})")
true
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.github.takahirom.roborazzi

actual fun roborazziErrorLog(message: String) {
System.err.println("Roborazzi: $message")
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.github.takahirom.roborazzi

fun reportLog(message: String) {
@InternalRoborazziApi
fun roborazziReportLog(message: String) {
println("Roborazzi: $message")
}

@InternalRoborazziApi
expect fun roborazziErrorLog(message: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.github.takahirom.roborazzi

import kotlinx.cinterop.ExperimentalForeignApi
import platform.posix.fprintf
import platform.posix.stderr

@OptIn(ExperimentalForeignApi::class)
actual fun roborazziErrorLog(message: String) {
fprintf(stderr, "Roborazzi: %s\n", message)
}
1 change: 1 addition & 0 deletions roborazzi-accessibility-check/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
76 changes: 76 additions & 0 deletions roborazzi-accessibility-check/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Roborazzi Acessibility Checks

Roborazzi Accessibility Checks is a library that integrates accessibility checks into Roborazzi.
It uses [ Accessibility Test Framework](https://github.com/google/Accessibility-Test-Framework-for-Android) to check accessibility.

## How to use

### Add dependencies

| Description | Dependencies |
|---------------------|-----------------------------------------------------------------------------------------------|
| Accessibility Check | `testImplementation("io.github.takahirom.roborazzi:roborazzi-accessibility-check:[version]")` |

### Configure in Junit Rule

```kotlin
@get:Rule
val roborazziRule = RoborazziRule(
composeRule = composeTestRule,
captureRoot = composeTestRule.onRoot(),
options = Options(
roborazziAccessibilityOptions = RoborazziATFAccessibilityCheckOptions(
checker = RoborazziATFAccessibilityChecker(
checks = setOf(NoRedTextCheck()),
suppressions = matchesElements(withTestTag("suppress"))
),
failureLevel = RoborazziATFAccessibilityChecker.CheckLevel.Warning
),
// If you want to automatically check accessibility after a test, use AccessibilityCheckAfterTestStrategy.
accessibilityCheckStrategy = AccessibilityCheckAfterTestStrategy(),
)
)
```

### Add accessibility checks

```kotlin
composeTestRule.onNodeWithTag("nothard").checkRoboAccessibility(
// If you don't specify options, the options in RoborazziRule will be used.
roborazziATFAccessibilityCheckOptions = RoborazziATFAccessibilityCheckOptions(
checker = RoborazziATFAccessibilityChecker(
preset = AccessibilityCheckPreset.LATEST,
),
failureLevel = RoborazziATFAccessibilityChecker.CheckLevel.Warning
)
)
```

### Checking Log output

Particularly with `failureLevel = CheckLevel.LogOnly` the output log of each test will including a11y checks.

```text
Error: [AccessibilityViewCheckResult check=AccessibilityHierarchyCheckResult ERROR SpeakableTextPresentCheck 4 [ViewHierarchyElement class=android.view.View bounds=Rect(474, 1074 - 606, 1206)] null num_answers:0 view=null]
Warning: [AccessibilityViewCheckResult check=AccessibilityHierarchyCheckResult WARNING TextContrastCheck 11 [ViewHierarchyElement class=android.widget.TextView text=Something hard to read bounds=Rect(403, 1002 - 678, 1092)] {KEY_BACKGROUND_COLOR=-12303292, KEY_CONTRAST_RATIO=1.02, KEY_FOREGROUND_COLOR=-12369085, KEY_REQUIRED_CONTRAST_RATIO=3.0} num_answers:0 view=null]
```

### LICENSE

```
Copyright 2023 takahirom
Copyright 2019 Square, Inc.
Copyright The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
42 changes: 42 additions & 0 deletions roborazzi-accessibility-check/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
if (System.getenv("INTEGRATION_TEST") != "true") {
pluginManager.apply("com.vanniktech.maven.publish")
}


android {
namespace 'com.github.takahirom.roborazzi.accessibility.check'
compileSdk 34

defaultConfig {
minSdk 21
targetSdk 32

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
buildFeatures {
}
testOptions {
unitTests {
includeAndroidResources = true
}
}
}

dependencies {
implementation project(':roborazzi-junit-rule')
implementation project(':roborazzi')
compileOnly libs.robolectric
compileOnly libs.androidx.compose.ui.test
api libs.accessibility.test.framework
}
Empty file.
21 changes: 21 additions & 0 deletions roborazzi-accessibility-check/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.github.takahirom.roborazzi

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.github.takahirom.roborazzi.test", appContext.packageName)
}
}
4 changes: 4 additions & 0 deletions roborazzi-accessibility-check/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest>

</manifest>
Loading

0 comments on commit 0cf989b

Please sign in to comment.