From 03668b942cb9d0e6ecf51aed3ed440a0bc756b84 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Mon, 4 Nov 2024 14:22:30 +0000 Subject: [PATCH 01/12] Add A11y Tests for Box This commit adds accessibility tests for the Box composable. It also adds a shadow for AccessibilityManager and a non-shadow shadow for AccessibilityNodeInfo. The dependencies are updated to include the Accessibility Test Framework. --- gradle/libs.versions.toml | 6 +- media/audio-ui/build.gradle.kts | 1 + .../google/android/horologist/BoxA11yTest.kt | 80 +++++++++++++++++++ .../ExtraShadowAccessibilityManager.kt | 39 +++++++++ .../NonShadowShadowAccessibilityNodeInfo.kt | 24 ++++++ 5 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 media/audio-ui/src/test/kotlin/com/google/android/horologist/BoxA11yTest.kt create mode 100644 media/audio-ui/src/test/kotlin/com/google/android/horologist/ExtraShadowAccessibilityManager.kt create mode 100644 media/audio-ui/src/test/kotlin/com/google/android/horologist/NonShadowShadowAccessibilityNodeInfo.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7a57c8c001..7e901390d1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,7 @@ [versions] accompanist = "0.36.0" androidx-benchmark = "1.3.3" +accessibilityTestFramework = "4.1.1" androidx-complications-data = "1.2.1" androidx-concurrent = "1.2.0" androidx-constraintlayout-compose = "1.0.1" @@ -13,7 +14,7 @@ androidx-test-ext = "1.2.1" androidx-test-runner = "1.6.2" androidx-wear-watchface = "1.2.1" androidxActivity = "1.9.3" -androidxComposeBom = "2024.09.00-alpha" +androidxComposeBom = "2024.10.00" androidxCore = "1.13.1" androidxLifecycle = "2.8.6" androidxNavigation = "2.8.3" @@ -65,6 +66,7 @@ wearToolingPreview = "1.0.0" wearcompose = "1.5.0-alpha04" [libraries] +accessibility-test-framework = { module = "com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework", version.ref = "accessibilityTestFramework" } accompanist-testharness = { module = "com.google.accompanist:accompanist-testharness", version.ref = "accompanist" } android-tools-build-gradle = { module = "com.android.tools.build:gradle", version.ref = "gradlePlugin" } androidx-activity = { module = "androidx.activity:activity", version.ref = "androidxActivity" } @@ -143,7 +145,7 @@ com-squareup-okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logg com-squareup-okhttp3-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "com-squareup-okhttp3" } com-squareup-okhttp3-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "com-squareup-okhttp3" } compose-animation-animationgraphics = { group = "androidx.compose.animation", name = "animation-graphics" } -compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" } +compose-bom = { group = "androidx.compose", name = "compose-bom-alpha", version.ref = "androidxComposeBom" } compose-foundation-foundation = { group = "androidx.compose.foundation", name = "foundation" } compose-foundation-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" } compose-material-iconscore = { group = "androidx.compose.material", name = "material-icons-core" } diff --git a/media/audio-ui/build.gradle.kts b/media/audio-ui/build.gradle.kts index 8eb78d8302..7e6c4f3eac 100644 --- a/media/audio-ui/build.gradle.kts +++ b/media/audio-ui/build.gradle.kts @@ -107,6 +107,7 @@ dependencies { implementation(libs.compose.material.iconscore) implementation(libs.compose.material.iconsext) implementation(libs.compose.material.ripple) + implementation(libs.accessibility.test.framework) implementation(libs.androidx.wear) implementation(libs.androidx.lifecycle.viewmodel.compose) diff --git a/media/audio-ui/src/test/kotlin/com/google/android/horologist/BoxA11yTest.kt b/media/audio-ui/src/test/kotlin/com/google/android/horologist/BoxA11yTest.kt new file mode 100644 index 0000000000..dc955308d0 --- /dev/null +++ b/media/audio-ui/src/test/kotlin/com/google/android/horologist/BoxA11yTest.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2024 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 + * + * https://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. + */ + +@file:OptIn(ExperimentalTestApi::class) + +package com.google.android.horologist + +import androidx.compose.foundation.layout.Box +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.ExperimentalTestApi +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.runComposeUiTest +import androidx.compose.ui.test.tryPerformAccessibilityChecks +import androidx.compose.ui.unit.dp +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.shadows.ShadowBuild + +@Config( + sdk = [34], + qualifiers = RobolectricDeviceQualifiers.Pixel4, + shadows = [ExtraShadowAccessibilityManager::class, NonShadowShadowAccessibilityNodeInfo::class], +) +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class BoxA11yTest { + @Test + fun badBox() = runComposeUiTest { + setContent { + Box( + Modifier.size(48.dp).semantics { + // The SemanticsModifier will make this node importantForAccessibility + // Having no content description is now a violation + contentDescription = "" + } + ) + } + + // TODO change this check in ATF + ShadowBuild.setFingerprint("test_fingerprint") + + enableAccessibilityChecks() + + onRoot().tryPerformAccessibilityChecks() + } + + @Test + fun goodBox() = runComposeUiTest { + setContent { + Box(Modifier.size(20.dp)) + } + + // TODO change this check in ATF + ShadowBuild.setFingerprint("test_fingerprint") + + enableAccessibilityChecks() + + onRoot().tryPerformAccessibilityChecks() + } +} diff --git a/media/audio-ui/src/test/kotlin/com/google/android/horologist/ExtraShadowAccessibilityManager.kt b/media/audio-ui/src/test/kotlin/com/google/android/horologist/ExtraShadowAccessibilityManager.kt new file mode 100644 index 0000000000..f69dfa97ff --- /dev/null +++ b/media/audio-ui/src/test/kotlin/com/google/android/horologist/ExtraShadowAccessibilityManager.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2024 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 + * + * https://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. + */ + +package com.google.android.horologist + +import android.view.accessibility.AccessibilityManager +import org.robolectric.annotation.Implementation +import org.robolectric.annotation.Implements +import org.robolectric.shadows.ShadowAccessibilityManager +import org.robolectric.versioning.AndroidVersions.U + +@Implements(AccessibilityManager::class) +class ExtraShadowAccessibilityManager: ShadowAccessibilityManager() { + + /** + * This shadow method is required because {@link + * android.view.accessibility.DirectAccessibilityConnection} calls it to determine if any + * transformations have occurred on this window. + */ + @Implementation(minSdk = U.SDK_INT) + fun getWindowTransformationSpec(windowId: Int): Any { + val instance = Class.forName("android.view.accessibility.IAccessibilityManager\$WindowTransformationSpec").newInstance() + + return instance + } +} \ No newline at end of file diff --git a/media/audio-ui/src/test/kotlin/com/google/android/horologist/NonShadowShadowAccessibilityNodeInfo.kt b/media/audio-ui/src/test/kotlin/com/google/android/horologist/NonShadowShadowAccessibilityNodeInfo.kt new file mode 100644 index 0000000000..258e7574db --- /dev/null +++ b/media/audio-ui/src/test/kotlin/com/google/android/horologist/NonShadowShadowAccessibilityNodeInfo.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2024 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 + * + * https://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. + */ + +package com.google.android.horologist + +import android.view.accessibility.AccessibilityNodeInfo +import org.robolectric.annotation.Implements + +@Implements(AccessibilityNodeInfo::class) +class NonShadowShadowAccessibilityNodeInfo { +} From 5ce1abdcaa83d7d8fcb7723bcb81cb8baddd16a3 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Tue, 5 Nov 2024 09:20:53 +0000 Subject: [PATCH 02/12] Use snapshot to support API 35 --- build.gradle.kts | 1 + gradle/libs.versions.toml | 2 +- .../test/kotlin/com/google/android/horologist/BoxA11yTest.kt | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6a76e89023..d269b8a0cc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -99,6 +99,7 @@ allprojects { if (composeSnapshot.length > 1) { maven(url = uri("https://androidx.dev/snapshots/builds/$composeSnapshot/artifacts/repository/")) } + maven(url = "https://oss.sonatype.org/content/repositories/snapshots") } plugins.withId("com.vanniktech.maven.publish") { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7e901390d1..1120a5c0bf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,7 +49,7 @@ material = "1.12.0" metalava = "0.3.5" moshi = "1.15.1" okio = "3.9.1" -org-robolectric = "4.13" +org-robolectric = "4.15-SNAPSHOT" playServicesAuth = "21.2.0" protobuf = "4.28.3" protobuf-gen-grpc-java = "1.68.1" diff --git a/media/audio-ui/src/test/kotlin/com/google/android/horologist/BoxA11yTest.kt b/media/audio-ui/src/test/kotlin/com/google/android/horologist/BoxA11yTest.kt index dc955308d0..4c25edd0b4 100644 --- a/media/audio-ui/src/test/kotlin/com/google/android/horologist/BoxA11yTest.kt +++ b/media/audio-ui/src/test/kotlin/com/google/android/horologist/BoxA11yTest.kt @@ -37,8 +37,8 @@ import org.robolectric.annotation.GraphicsMode import org.robolectric.shadows.ShadowBuild @Config( - sdk = [34], - qualifiers = RobolectricDeviceQualifiers.Pixel4, + sdk = [35], + qualifiers = RobolectricDeviceQualifiers.WearOSLargeRound, shadows = [ExtraShadowAccessibilityManager::class, NonShadowShadowAccessibilityNodeInfo::class], ) @RunWith(AndroidJUnit4::class) From 8b3782a3ebd149232e662d2bc462078e5b56ea3b Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Wed, 6 Nov 2024 11:11:03 +0000 Subject: [PATCH 03/12] Adds Accessibility utilities to roboscreenshots Adds Accessibility testing utilities to roboscreenshots to allow for accessibility tests in Robolectric screenshots. --- gradle/libs.versions.toml | 2 +- media/audio-ui/build.gradle.kts | 1 - .../google/android/horologist/BoxA11yTest.kt | 80 ------------------- .../media/ui/MediaPlayerA11yScreenshotTest.kt | 3 + roboscreenshots/build.gradle.kts | 1 + .../a11y}/ExtraShadowAccessibilityManager.kt | 12 ++- .../NonShadowShadowAccessibilityNodeInfo.kt | 2 +- .../screenshots/rng/WearLegacyA11yTest.kt | 17 +++- 8 files changed, 30 insertions(+), 88 deletions(-) delete mode 100644 media/audio-ui/src/test/kotlin/com/google/android/horologist/BoxA11yTest.kt rename {media/audio-ui/src/test/kotlin/com/google/android/horologist => roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y}/ExtraShadowAccessibilityManager.kt (73%) rename {media/audio-ui/src/test/kotlin/com/google/android/horologist => roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y}/NonShadowShadowAccessibilityNodeInfo.kt (93%) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1120a5c0bf..7e901390d1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,7 +49,7 @@ material = "1.12.0" metalava = "0.3.5" moshi = "1.15.1" okio = "3.9.1" -org-robolectric = "4.15-SNAPSHOT" +org-robolectric = "4.13" playServicesAuth = "21.2.0" protobuf = "4.28.3" protobuf-gen-grpc-java = "1.68.1" diff --git a/media/audio-ui/build.gradle.kts b/media/audio-ui/build.gradle.kts index 7e6c4f3eac..8eb78d8302 100644 --- a/media/audio-ui/build.gradle.kts +++ b/media/audio-ui/build.gradle.kts @@ -107,7 +107,6 @@ dependencies { implementation(libs.compose.material.iconscore) implementation(libs.compose.material.iconsext) implementation(libs.compose.material.ripple) - implementation(libs.accessibility.test.framework) implementation(libs.androidx.wear) implementation(libs.androidx.lifecycle.viewmodel.compose) diff --git a/media/audio-ui/src/test/kotlin/com/google/android/horologist/BoxA11yTest.kt b/media/audio-ui/src/test/kotlin/com/google/android/horologist/BoxA11yTest.kt deleted file mode 100644 index 4c25edd0b4..0000000000 --- a/media/audio-ui/src/test/kotlin/com/google/android/horologist/BoxA11yTest.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2024 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 - * - * https://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. - */ - -@file:OptIn(ExperimentalTestApi::class) - -package com.google.android.horologist - -import androidx.compose.foundation.layout.Box -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.ExperimentalTestApi -import androidx.compose.ui.test.onRoot -import androidx.compose.ui.test.runComposeUiTest -import androidx.compose.ui.test.tryPerformAccessibilityChecks -import androidx.compose.ui.unit.dp -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.annotation.Config -import org.robolectric.annotation.GraphicsMode -import org.robolectric.shadows.ShadowBuild - -@Config( - sdk = [35], - qualifiers = RobolectricDeviceQualifiers.WearOSLargeRound, - shadows = [ExtraShadowAccessibilityManager::class, NonShadowShadowAccessibilityNodeInfo::class], -) -@RunWith(AndroidJUnit4::class) -@GraphicsMode(GraphicsMode.Mode.NATIVE) -class BoxA11yTest { - @Test - fun badBox() = runComposeUiTest { - setContent { - Box( - Modifier.size(48.dp).semantics { - // The SemanticsModifier will make this node importantForAccessibility - // Having no content description is now a violation - contentDescription = "" - } - ) - } - - // TODO change this check in ATF - ShadowBuild.setFingerprint("test_fingerprint") - - enableAccessibilityChecks() - - onRoot().tryPerformAccessibilityChecks() - } - - @Test - fun goodBox() = runComposeUiTest { - setContent { - Box(Modifier.size(20.dp)) - } - - // TODO change this check in ATF - ShadowBuild.setFingerprint("test_fingerprint") - - enableAccessibilityChecks() - - onRoot().tryPerformAccessibilityChecks() - } -} diff --git a/media/ui/src/test/java/com/google/android/horologist/media/ui/MediaPlayerA11yScreenshotTest.kt b/media/ui/src/test/java/com/google/android/horologist/media/ui/MediaPlayerA11yScreenshotTest.kt index 82f2b20d54..5ecbd61a81 100644 --- a/media/ui/src/test/java/com/google/android/horologist/media/ui/MediaPlayerA11yScreenshotTest.kt +++ b/media/ui/src/test/java/com/google/android/horologist/media/ui/MediaPlayerA11yScreenshotTest.kt @@ -50,6 +50,9 @@ class MediaPlayerA11yScreenshotTest : WearLegacyA11yTest() { mediaPlayerScreen() } + override val runAtf: Boolean + get() = false + private fun mediaPlayerScreen() { val playerUiState = PlayerUiState( playEnabled = true, diff --git a/roboscreenshots/build.gradle.kts b/roboscreenshots/build.gradle.kts index e8f0890235..3f708c6b7d 100644 --- a/roboscreenshots/build.gradle.kts +++ b/roboscreenshots/build.gradle.kts @@ -90,6 +90,7 @@ dependencies { api(projects.composeLayout) api(projects.images.coil) api(projects.tiles) + api(libs.accessibility.test.framework) api(libs.kotlin.stdlib) api(libs.okio) diff --git a/media/audio-ui/src/test/kotlin/com/google/android/horologist/ExtraShadowAccessibilityManager.kt b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/ExtraShadowAccessibilityManager.kt similarity index 73% rename from media/audio-ui/src/test/kotlin/com/google/android/horologist/ExtraShadowAccessibilityManager.kt rename to roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/ExtraShadowAccessibilityManager.kt index f69dfa97ff..90c9494963 100644 --- a/media/audio-ui/src/test/kotlin/com/google/android/horologist/ExtraShadowAccessibilityManager.kt +++ b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/ExtraShadowAccessibilityManager.kt @@ -14,8 +14,9 @@ * limitations under the License. */ -package com.google.android.horologist +package com.google.android.horologist.screenshots.a11y +import android.annotation.SuppressLint import android.view.accessibility.AccessibilityManager import org.robolectric.annotation.Implementation import org.robolectric.annotation.Implements @@ -23,16 +24,19 @@ import org.robolectric.shadows.ShadowAccessibilityManager import org.robolectric.versioning.AndroidVersions.U @Implements(AccessibilityManager::class) -class ExtraShadowAccessibilityManager: ShadowAccessibilityManager() { +class ExtraShadowAccessibilityManager : ShadowAccessibilityManager() { /** * This shadow method is required because {@link * android.view.accessibility.DirectAccessibilityConnection} calls it to determine if any * transformations have occurred on this window. */ + @SuppressLint("PrivateApi") @Implementation(minSdk = U.SDK_INT) - fun getWindowTransformationSpec(windowId: Int): Any { - val instance = Class.forName("android.view.accessibility.IAccessibilityManager\$WindowTransformationSpec").newInstance() + fun getWindowTransformationSpec(windowId: Int): Any { + val instance = + Class.forName("android.view.accessibility.IAccessibilityManager\$WindowTransformationSpec") + .getDeclaredConstructor().newInstance() return instance } diff --git a/media/audio-ui/src/test/kotlin/com/google/android/horologist/NonShadowShadowAccessibilityNodeInfo.kt b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/NonShadowShadowAccessibilityNodeInfo.kt similarity index 93% rename from media/audio-ui/src/test/kotlin/com/google/android/horologist/NonShadowShadowAccessibilityNodeInfo.kt rename to roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/NonShadowShadowAccessibilityNodeInfo.kt index 258e7574db..1d93bc5302 100644 --- a/media/audio-ui/src/test/kotlin/com/google/android/horologist/NonShadowShadowAccessibilityNodeInfo.kt +++ b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/NonShadowShadowAccessibilityNodeInfo.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.android.horologist +package com.google.android.horologist.screenshots.a11y import android.view.accessibility.AccessibilityNodeInfo import org.robolectric.annotation.Implements diff --git a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/rng/WearLegacyA11yTest.kt b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/rng/WearLegacyA11yTest.kt index 9e58ebb098..d937e174a3 100644 --- a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/rng/WearLegacyA11yTest.kt +++ b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/rng/WearLegacyA11yTest.kt @@ -20,6 +20,7 @@ package com.google.android.horologist.screenshots.rng import android.app.Application import android.graphics.Bitmap +import android.os.Build import android.os.Looper import android.view.accessibility.AccessibilityManager import androidx.compose.foundation.background @@ -32,6 +33,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.tryPerformAccessibilityChecks import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso import androidx.test.espresso.Root @@ -59,9 +61,10 @@ import org.junit.runner.RunWith import org.robolectric.Shadows import org.robolectric.annotation.Config import org.robolectric.annotation.GraphicsMode +import org.robolectric.shadows.ShadowBuild @Config( - sdk = [33], + sdk = [34], qualifiers = RobolectricDeviceQualifiers.WearOSLargeRound, ) @RunWith(AndroidJUnit4::class) @@ -78,6 +81,9 @@ public abstract class WearLegacyA11yTest { public open val imageLoader: FakeImageLoaderEngine? = null + public open val runAtf: Boolean + get() = true + public fun runScreenTest( content: @Composable () -> Unit, ) { @@ -87,6 +93,15 @@ public abstract class WearLegacyA11yTest { } } + if (runAtf && Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + // TODO change this check in ATF + ShadowBuild.setFingerprint("test_fingerprint") + + composeRule.enableAccessibilityChecks() + + composeRule.onRoot().tryPerformAccessibilityChecks() + } + captureScreenshot() } From 293009f54826528754ee7f8206db861f39bb28ad Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Wed, 6 Nov 2024 11:14:32 +0000 Subject: [PATCH 04/12] Fixes --- build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index d269b8a0cc..6a76e89023 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -99,7 +99,6 @@ allprojects { if (composeSnapshot.length > 1) { maven(url = uri("https://androidx.dev/snapshots/builds/$composeSnapshot/artifacts/repository/")) } - maven(url = "https://oss.sonatype.org/content/repositories/snapshots") } plugins.withId("com.vanniktech.maven.publish") { From 61a0bf32ccaa02d6851fb7b6bde51fef1752fd8d Mon Sep 17 00:00:00 2001 From: yschimke Date: Wed, 6 Nov 2024 11:23:34 +0000 Subject: [PATCH 05/12] =?UTF-8?q?=F0=9F=A4=96=20reformat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screenshots/a11y/ExtraShadowAccessibilityManager.kt | 2 +- .../screenshots/a11y/NonShadowShadowAccessibilityNodeInfo.kt | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/ExtraShadowAccessibilityManager.kt b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/ExtraShadowAccessibilityManager.kt index 90c9494963..2a49feb3b2 100644 --- a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/ExtraShadowAccessibilityManager.kt +++ b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/ExtraShadowAccessibilityManager.kt @@ -40,4 +40,4 @@ class ExtraShadowAccessibilityManager : ShadowAccessibilityManager() { return instance } -} \ No newline at end of file +} diff --git a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/NonShadowShadowAccessibilityNodeInfo.kt b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/NonShadowShadowAccessibilityNodeInfo.kt index 1d93bc5302..9d8652aa53 100644 --- a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/NonShadowShadowAccessibilityNodeInfo.kt +++ b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/NonShadowShadowAccessibilityNodeInfo.kt @@ -20,5 +20,4 @@ import android.view.accessibility.AccessibilityNodeInfo import org.robolectric.annotation.Implements @Implements(AccessibilityNodeInfo::class) -class NonShadowShadowAccessibilityNodeInfo { -} +class NonShadowShadowAccessibilityNodeInfo From 26178edbe7b0e95efd1d34e836f8e25b09ce49da Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Wed, 6 Nov 2024 12:42:52 +0000 Subject: [PATCH 06/12] Update --- datalayer/phone-ui/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/datalayer/phone-ui/build.gradle.kts b/datalayer/phone-ui/build.gradle.kts index 3161c65ae4..133747adde 100644 --- a/datalayer/phone-ui/build.gradle.kts +++ b/datalayer/phone-ui/build.gradle.kts @@ -106,6 +106,8 @@ dependencies { implementation(libs.compose.material3) implementation(libs.material) implementation(platform(libs.compose.bom)) + implementation(libs.compose.material.iconscore) + implementation(libs.compose.material.iconsext) testImplementation(libs.junit) From ee2db17b84131884c19d6a4a84c04f93544ba718 Mon Sep 17 00:00:00 2001 From: yschimke Date: Wed, 6 Nov 2024 13:14:23 +0000 Subject: [PATCH 07/12] =?UTF-8?q?=F0=9F=A4=96=20metalava?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- datalayer/watch/api/current.api | 2 +- roboscreenshots/api/current.api | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/datalayer/watch/api/current.api b/datalayer/watch/api/current.api index a83cf5ee87..56e7154b2e 100644 --- a/datalayer/watch/api/current.api +++ b/datalayer/watch/api/current.api @@ -13,7 +13,7 @@ package com.google.android.horologist.datalayer.watch { method public suspend Object? markSetupNoLongerComplete(kotlin.coroutines.Continuation); method @Deprecated public suspend Object? markTileAsInstalled(String tileName, kotlin.coroutines.Continuation); method @Deprecated public suspend Object? markTileAsRemoved(String tileName, kotlin.coroutines.Continuation); - method @CheckResult public suspend Object? startCompanion(String nodeId, kotlin.coroutines.Continuation); + method @CheckResult public suspend Object? startCompanion(String nodeId, kotlin.coroutines.Continuation); method public suspend Object? updateInstalledTiles(kotlin.coroutines.Continuation); property public kotlinx.coroutines.flow.Flow> connectedAndInstalledNodes; property public final kotlinx.coroutines.flow.Flow surfacesInfo; diff --git a/roboscreenshots/api/current.api b/roboscreenshots/api/current.api index 9fd6e867d4..d7b3aa54c0 100644 --- a/roboscreenshots/api/current.api +++ b/roboscreenshots/api/current.api @@ -9,6 +9,19 @@ package com.google.android.horologist.screenshots { } +package com.google.android.horologist.screenshots.a11y { + + @org.robolectric.annotation.Implements(AccessibilityManager::class) public final class ExtraShadowAccessibilityManager extends org.robolectric.shadows.ShadowAccessibilityManager { + ctor public ExtraShadowAccessibilityManager(); + method @org.robolectric.annotation.Implementation(minSdk=org.robolectric.versioning.AndroidVersions.U.SDK_INT) public Object getWindowTransformationSpec(int windowId); + } + + @org.robolectric.annotation.Implements(AccessibilityNodeInfo::class) public final class NonShadowShadowAccessibilityNodeInfo { + ctor public NonShadowShadowAccessibilityNodeInfo(); + } + +} + package com.google.android.horologist.screenshots.rng { public interface ScreenshotTest { @@ -74,7 +87,7 @@ package com.google.android.horologist.screenshots.rng { method @org.robolectric.ParameterizedRobolectricTestRunner.Parameters public java.util.List devices(); } - @org.junit.runner.RunWith(AndroidJUnit4::class) @org.robolectric.annotation.Config(sdk={33}, qualifiers=com.github.takahirom.roborazzi.RobolectricDeviceQualifiers.WearOSLargeRound) @org.robolectric.annotation.GraphicsMode(org.robolectric.annotation.GraphicsMode.Mode.NATIVE) public abstract class WearLegacyA11yTest { + @org.junit.runner.RunWith(AndroidJUnit4::class) @org.robolectric.annotation.Config(sdk={34}, qualifiers=com.github.takahirom.roborazzi.RobolectricDeviceQualifiers.WearOSLargeRound) @org.robolectric.annotation.GraphicsMode(org.robolectric.annotation.GraphicsMode.Mode.NATIVE) public abstract class WearLegacyA11yTest { ctor public WearLegacyA11yTest(); method @androidx.compose.runtime.Composable public void ComponentScaffold(kotlin.jvm.functions.Function0 content); method @androidx.compose.runtime.Composable public void TestScaffold(kotlin.jvm.functions.Function0 content); @@ -82,6 +95,7 @@ package com.google.android.horologist.screenshots.rng { method public final void captureScreenshot(optional String suffix); method @org.junit.Rule public final androidx.compose.ui.test.junit4.ComposeContentTestRule getComposeRule(); method public coil.test.FakeImageLoaderEngine? getImageLoader(); + method public boolean getRunAtf(); method @org.junit.Rule public final org.junit.rules.TestName getTestInfo(); method public float getTolerance(); method public final void runComponentTest(optional androidx.compose.ui.graphics.Color? background, kotlin.jvm.functions.Function0 content); @@ -89,6 +103,7 @@ package com.google.android.horologist.screenshots.rng { method public String testName(String suffix); property @org.junit.Rule public final androidx.compose.ui.test.junit4.ComposeContentTestRule composeRule; property public coil.test.FakeImageLoaderEngine? imageLoader; + property public boolean runAtf; property @org.junit.Rule public final org.junit.rules.TestName testInfo; property public float tolerance; field public static final com.google.android.horologist.screenshots.rng.WearLegacyA11yTest.Companion Companion; From e69fc36c076fd7c3df7e1c5e9f03fc682c800e54 Mon Sep 17 00:00:00 2001 From: yschimke Date: Wed, 6 Nov 2024 13:24:09 +0000 Subject: [PATCH 08/12] =?UTF-8?q?=F0=9F=A4=96=20Updates=20screenshots?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...roid.horologist.compose.material_StepperA11yTest_float.png | 4 ++-- ...ndroid.horologist.compose.material_StepperA11yTest_int.png | 4 ++-- ....screens_MetricsScreenA11yTest_metricsScreenTwoMetrics.png | 4 ++-- ...eens_MetricsScreenA11yTest_metricsScreenTwoMetrics_rtl.png | 4 ++-- ...ui_MediaPlayerA11yScreenshotTest_mediaPlayerLargeRound.png | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/compose-material/src/test/snapshots/images/com.google.android.horologist.compose.material_StepperA11yTest_float.png b/compose-material/src/test/snapshots/images/com.google.android.horologist.compose.material_StepperA11yTest_float.png index 1863cd2a29..b84c408333 100644 --- a/compose-material/src/test/snapshots/images/com.google.android.horologist.compose.material_StepperA11yTest_float.png +++ b/compose-material/src/test/snapshots/images/com.google.android.horologist.compose.material_StepperA11yTest_float.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3fc7c41bca5cc749f5327476182395c7549775b41c174288b8a276fe06150b7f -size 31345 +oid sha256:7602972205024ea3b3a56cde38949ec7679565a5ea2a77e14b1ec4fb23268c42 +size 31327 diff --git a/compose-material/src/test/snapshots/images/com.google.android.horologist.compose.material_StepperA11yTest_int.png b/compose-material/src/test/snapshots/images/com.google.android.horologist.compose.material_StepperA11yTest_int.png index 103aa986dd..5e4793775d 100644 --- a/compose-material/src/test/snapshots/images/com.google.android.horologist.compose.material_StepperA11yTest_int.png +++ b/compose-material/src/test/snapshots/images/com.google.android.horologist.compose.material_StepperA11yTest_int.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5179bd6215f87a0ad04bc093ee759e92a104d03639bf4a743fabd11af6c0156 -size 31207 +oid sha256:9425db9cf8c2e6b388bc9c2f19d9ca3e6f596b7eccf94180f8fa580e26f914e7 +size 31192 diff --git a/health/composables/src/test/snapshots/images/com.google.android.horologist.health.composables.screens_MetricsScreenA11yTest_metricsScreenTwoMetrics.png b/health/composables/src/test/snapshots/images/com.google.android.horologist.health.composables.screens_MetricsScreenA11yTest_metricsScreenTwoMetrics.png index 0868e0dbdb..6138797b8d 100644 --- a/health/composables/src/test/snapshots/images/com.google.android.horologist.health.composables.screens_MetricsScreenA11yTest_metricsScreenTwoMetrics.png +++ b/health/composables/src/test/snapshots/images/com.google.android.horologist.health.composables.screens_MetricsScreenA11yTest_metricsScreenTwoMetrics.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae78b0fe1000514d126748fed3082181646642e5351f74ba5d7a7cc002c2d6b8 -size 28636 +oid sha256:11402e3914bfcd003f229ef3703db0bf19ba49f41741c103f9c45f35610dbb52 +size 28613 diff --git a/health/composables/src/test/snapshots/images/com.google.android.horologist.health.composables.screens_MetricsScreenA11yTest_metricsScreenTwoMetrics_rtl.png b/health/composables/src/test/snapshots/images/com.google.android.horologist.health.composables.screens_MetricsScreenA11yTest_metricsScreenTwoMetrics_rtl.png index 1cc6aa1759..6ec737dd45 100644 --- a/health/composables/src/test/snapshots/images/com.google.android.horologist.health.composables.screens_MetricsScreenA11yTest_metricsScreenTwoMetrics_rtl.png +++ b/health/composables/src/test/snapshots/images/com.google.android.horologist.health.composables.screens_MetricsScreenA11yTest_metricsScreenTwoMetrics_rtl.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc679357f057b70b81206284f7aa7ae96e82eccf6516db92dc12bdbe823c7ad7 -size 28532 +oid sha256:393a64ab93d67d43100770bfccbeffdf09d243b0a201a00a29642e6856fab4f0 +size 28517 diff --git a/media/ui/src/test/snapshots/images/com.google.android.horologist.media.ui_MediaPlayerA11yScreenshotTest_mediaPlayerLargeRound.png b/media/ui/src/test/snapshots/images/com.google.android.horologist.media.ui_MediaPlayerA11yScreenshotTest_mediaPlayerLargeRound.png index b6381cf3ee..308a070033 100644 --- a/media/ui/src/test/snapshots/images/com.google.android.horologist.media.ui_MediaPlayerA11yScreenshotTest_mediaPlayerLargeRound.png +++ b/media/ui/src/test/snapshots/images/com.google.android.horologist.media.ui_MediaPlayerA11yScreenshotTest_mediaPlayerLargeRound.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:70c2071e1143891451d9d3abf7b59f17b4471d2def4a7c096e05052caad84da9 -size 118004 +oid sha256:0ae9899f7af174e67ba715657e6d0199e2152100cfc5709894593a8a0cf65af7 +size 117995 From e894b416f6d4d2c724757d67a4037b4e0e45c148 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Wed, 6 Nov 2024 13:59:51 +0000 Subject: [PATCH 09/12] allow configuration --- .../horologist/screenshots/rng/WearLegacyA11yTest.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/rng/WearLegacyA11yTest.kt b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/rng/WearLegacyA11yTest.kt index d937e174a3..110425099a 100644 --- a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/rng/WearLegacyA11yTest.kt +++ b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/rng/WearLegacyA11yTest.kt @@ -30,6 +30,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onRoot @@ -48,6 +49,7 @@ import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers import com.github.takahirom.roborazzi.RoborazziOptions import com.github.takahirom.roborazzi.ThresholdValidator import com.github.takahirom.roborazzi.captureRoboImage +import com.google.android.apps.common.testing.accessibility.framework.integrations.espresso.AccessibilityValidator import com.google.android.horologist.compose.layout.AppScaffold import com.google.android.horologist.compose.layout.ResponsiveTimeText import com.google.android.horologist.screenshots.FixedTimeSource @@ -84,6 +86,9 @@ public abstract class WearLegacyA11yTest { public open val runAtf: Boolean get() = true + open fun configureAccessibilityValidator(validator: AccessibilityValidator) { + } + public fun runScreenTest( content: @Composable () -> Unit, ) { @@ -99,6 +104,10 @@ public abstract class WearLegacyA11yTest { composeRule.enableAccessibilityChecks() + val accessibilityValidator = + (composeRule as AndroidComposeTestRule<*, *>).accessibilityValidator!! + configureAccessibilityValidator(accessibilityValidator) + composeRule.onRoot().tryPerformAccessibilityChecks() } From 8a4b5d0ad1d57f1ce3ab135296046d4005c6a4d5 Mon Sep 17 00:00:00 2001 From: yschimke Date: Wed, 6 Nov 2024 14:10:54 +0000 Subject: [PATCH 10/12] =?UTF-8?q?=F0=9F=A4=96=20metalava?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- roboscreenshots/api/current.api | 1 + 1 file changed, 1 insertion(+) diff --git a/roboscreenshots/api/current.api b/roboscreenshots/api/current.api index d7b3aa54c0..8d699f17fd 100644 --- a/roboscreenshots/api/current.api +++ b/roboscreenshots/api/current.api @@ -93,6 +93,7 @@ package com.google.android.horologist.screenshots.rng { method @androidx.compose.runtime.Composable public void TestScaffold(kotlin.jvm.functions.Function0 content); method public final void captureScreenA11yRoboImage(String filePath, com.github.takahirom.roborazzi.RoborazziOptions roborazziOptions); method public final void captureScreenshot(optional String suffix); + method public void configureAccessibilityValidator(com.google.android.apps.common.testing.accessibility.framework.integrations.espresso.AccessibilityValidator validator); method @org.junit.Rule public final androidx.compose.ui.test.junit4.ComposeContentTestRule getComposeRule(); method public coil.test.FakeImageLoaderEngine? getImageLoader(); method public boolean getRunAtf(); From 7e0beee2c465805013cba6c17c238999665830ad Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Wed, 6 Nov 2024 16:06:06 +0000 Subject: [PATCH 11/12] Fixes --- roboscreenshots/api/current.api | 13 ------------- .../a11y/ExtraShadowAccessibilityManager.kt | 2 +- .../a11y/NonShadowShadowAccessibilityNodeInfo.kt | 2 +- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/roboscreenshots/api/current.api b/roboscreenshots/api/current.api index 8d699f17fd..78242428ee 100644 --- a/roboscreenshots/api/current.api +++ b/roboscreenshots/api/current.api @@ -9,19 +9,6 @@ package com.google.android.horologist.screenshots { } -package com.google.android.horologist.screenshots.a11y { - - @org.robolectric.annotation.Implements(AccessibilityManager::class) public final class ExtraShadowAccessibilityManager extends org.robolectric.shadows.ShadowAccessibilityManager { - ctor public ExtraShadowAccessibilityManager(); - method @org.robolectric.annotation.Implementation(minSdk=org.robolectric.versioning.AndroidVersions.U.SDK_INT) public Object getWindowTransformationSpec(int windowId); - } - - @org.robolectric.annotation.Implements(AccessibilityNodeInfo::class) public final class NonShadowShadowAccessibilityNodeInfo { - ctor public NonShadowShadowAccessibilityNodeInfo(); - } - -} - package com.google.android.horologist.screenshots.rng { public interface ScreenshotTest { diff --git a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/ExtraShadowAccessibilityManager.kt b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/ExtraShadowAccessibilityManager.kt index 2a49feb3b2..c17652e093 100644 --- a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/ExtraShadowAccessibilityManager.kt +++ b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/ExtraShadowAccessibilityManager.kt @@ -24,7 +24,7 @@ import org.robolectric.shadows.ShadowAccessibilityManager import org.robolectric.versioning.AndroidVersions.U @Implements(AccessibilityManager::class) -class ExtraShadowAccessibilityManager : ShadowAccessibilityManager() { +internal class ExtraShadowAccessibilityManager : ShadowAccessibilityManager() { /** * This shadow method is required because {@link diff --git a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/NonShadowShadowAccessibilityNodeInfo.kt b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/NonShadowShadowAccessibilityNodeInfo.kt index 9d8652aa53..a5d740ce67 100644 --- a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/NonShadowShadowAccessibilityNodeInfo.kt +++ b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/a11y/NonShadowShadowAccessibilityNodeInfo.kt @@ -20,4 +20,4 @@ import android.view.accessibility.AccessibilityNodeInfo import org.robolectric.annotation.Implements @Implements(AccessibilityNodeInfo::class) -class NonShadowShadowAccessibilityNodeInfo +internal class NonShadowShadowAccessibilityNodeInfo From e0f1032afe22424186e3adaaabf6eb054195cc87 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Wed, 6 Nov 2024 16:10:56 +0000 Subject: [PATCH 12/12] Fixes --- .../android/horologist/screenshots/rng/WearLegacyA11yTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/rng/WearLegacyA11yTest.kt b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/rng/WearLegacyA11yTest.kt index 110425099a..9aab907fec 100644 --- a/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/rng/WearLegacyA11yTest.kt +++ b/roboscreenshots/src/main/java/com/google/android/horologist/screenshots/rng/WearLegacyA11yTest.kt @@ -86,7 +86,7 @@ public abstract class WearLegacyA11yTest { public open val runAtf: Boolean get() = true - open fun configureAccessibilityValidator(validator: AccessibilityValidator) { + public open fun configureAccessibilityValidator(validator: AccessibilityValidator) { } public fun runScreenTest(