mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Migrates android semanitcs integration to integration test (#127128)
I think the flake is due to setclipboard or semantics update race condition. I migrated the test to use integration test package which relies less on timing fixes https://github.com/flutter/flutter/issues/124636
This commit is contained in:
parent
8c086d0171
commit
c687dcd56f
79
dev/devicelab/lib/framework/talkback.dart
Normal file
79
dev/devicelab/lib/framework/talkback.dart
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:io' as io;
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:pub_semver/pub_semver.dart';
|
||||
|
||||
String adbPath() {
|
||||
final String? androidHome = io.Platform.environment['ANDROID_HOME'] ?? io.Platform.environment['ANDROID_SDK_ROOT'];
|
||||
if (androidHome == null) {
|
||||
return 'adb';
|
||||
} else {
|
||||
return path.join(androidHome, 'platform-tools', 'adb');
|
||||
}
|
||||
}
|
||||
|
||||
Future<Version> getTalkbackVersion() async {
|
||||
final io.ProcessResult result = await io.Process.run(adbPath(), const <String>[
|
||||
'shell',
|
||||
'dumpsys',
|
||||
'package',
|
||||
'com.google.android.marvin.talkback',
|
||||
]);
|
||||
if (result.exitCode != 0) {
|
||||
throw Exception('Failed to get TalkBack version: ${result.stdout as String}\n${result.stderr as String}');
|
||||
}
|
||||
final List<String> lines = (result.stdout as String).split('\n');
|
||||
String? version;
|
||||
for (final String line in lines) {
|
||||
if (line.contains('versionName')) {
|
||||
version = line.replaceAll(RegExp(r'\s*versionName='), '');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (version == null) {
|
||||
throw Exception('Unable to determine TalkBack version.');
|
||||
}
|
||||
|
||||
// Android doesn't quite use semver, so convert the version string to semver form.
|
||||
final RegExp startVersion = RegExp(r'(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(\.(?<build>\d+))?');
|
||||
final RegExpMatch? match = startVersion.firstMatch(version);
|
||||
if (match == null) {
|
||||
return Version(0, 0, 0);
|
||||
}
|
||||
return Version(
|
||||
int.parse(match.namedGroup('major')!),
|
||||
int.parse(match.namedGroup('minor')!),
|
||||
int.parse(match.namedGroup('patch')!),
|
||||
build: match.namedGroup('build'),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> enableTalkBack() async {
|
||||
final io.Process run = await io.Process.start(adbPath(), const <String>[
|
||||
'shell',
|
||||
'settings',
|
||||
'put',
|
||||
'secure',
|
||||
'enabled_accessibility_services',
|
||||
'com.google.android.marvin.talkback/com.google.android.marvin.talkback.TalkBackService',
|
||||
]);
|
||||
await run.exitCode;
|
||||
|
||||
print('TalkBack version is ${await getTalkbackVersion()}');
|
||||
}
|
||||
|
||||
Future<void> disableTalkBack() async {
|
||||
final io.Process run = await io.Process.start(adbPath(), const <String>[
|
||||
'shell',
|
||||
'settings',
|
||||
'put',
|
||||
'secure',
|
||||
'enabled_accessibility_services',
|
||||
'null',
|
||||
]);
|
||||
await run.exitCode;
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
|
||||
import '../framework/devices.dart';
|
||||
import '../framework/framework.dart';
|
||||
import '../framework/talkback.dart';
|
||||
import '../framework/task_result.dart';
|
||||
import '../framework/utils.dart';
|
||||
|
||||
@ -74,9 +75,10 @@ TaskFunction createHybridAndroidViewsIntegrationTest() {
|
||||
}
|
||||
|
||||
TaskFunction createAndroidSemanticsIntegrationTest() {
|
||||
return DriverTest(
|
||||
return IntegrationTest(
|
||||
'${flutterDirectory.path}/dev/integration_tests/android_semantics_testing',
|
||||
'lib/main.dart',
|
||||
'integration_test/main_test.dart',
|
||||
withTalkBack: true,
|
||||
).call;
|
||||
}
|
||||
|
||||
@ -213,6 +215,7 @@ class IntegrationTest {
|
||||
this.testTarget, {
|
||||
this.extraOptions = const <String>[],
|
||||
this.createPlatforms = const <String>[],
|
||||
this.withTalkBack = false,
|
||||
}
|
||||
);
|
||||
|
||||
@ -220,6 +223,7 @@ class IntegrationTest {
|
||||
final String testTarget;
|
||||
final List<String> extraOptions;
|
||||
final List<String> createPlatforms;
|
||||
final bool withTalkBack;
|
||||
|
||||
Future<TaskResult> call() {
|
||||
return inDirectory<TaskResult>(testDirectory, () async {
|
||||
@ -237,6 +241,13 @@ class IntegrationTest {
|
||||
]);
|
||||
}
|
||||
|
||||
if (withTalkBack) {
|
||||
if (device is! AndroidDevice) {
|
||||
return TaskResult.failure('A test that enables TalkBack can only be run on Android devices');
|
||||
}
|
||||
await enableTalkBack();
|
||||
}
|
||||
|
||||
final List<String> options = <String>[
|
||||
'-v',
|
||||
'-d',
|
||||
@ -246,6 +257,10 @@ class IntegrationTest {
|
||||
];
|
||||
await flutter('test', options: options);
|
||||
|
||||
if (withTalkBack) {
|
||||
await disableTalkBack();
|
||||
}
|
||||
|
||||
return TaskResult.success(null);
|
||||
});
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity;
|
||||
import io.flutter.embedding.engine.FlutterEngine;
|
||||
import io.flutter.embedding.android.FlutterActivity;
|
||||
import io.flutter.embedding.android.FlutterView;
|
||||
@ -123,11 +122,60 @@ public class MainActivity extends FlutterActivity {
|
||||
if (actionList.size() > 0) {
|
||||
ArrayList<Integer> actions = new ArrayList<>();
|
||||
for (AccessibilityNodeInfo.AccessibilityAction action : actionList) {
|
||||
actions.add(action.getId());
|
||||
if (kIdByAction.containsKey(action)) {
|
||||
actions.add(kIdByAction.get(action).intValue());
|
||||
}
|
||||
}
|
||||
result.put("actions", actions);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// These indices need to be in sync with android_semantics_testing/lib/src/constants.dart
|
||||
static final int kFocusIndex = 1 << 0;
|
||||
static final int kClearFocusIndex = 1 << 1;
|
||||
static final int kSelectIndex = 1 << 2;
|
||||
static final int kClearSelectionIndex = 1 << 3;
|
||||
static final int kClickIndex = 1 << 4;
|
||||
static final int kLongClickIndex = 1 << 5;
|
||||
static final int kAccessibilityFocusIndex = 1 << 6;
|
||||
static final int kClearAccessibilityFocusIndex = 1 << 7;
|
||||
static final int kNextAtMovementGranularityIndex = 1 << 8;
|
||||
static final int kPreviousAtMovementGranularityIndex = 1 << 9;
|
||||
static final int kNextHtmlElementIndex = 1 << 10;
|
||||
static final int kPreviousHtmlElementIndex = 1 << 11;
|
||||
static final int kScrollForwardIndex = 1 << 12;
|
||||
static final int kScrollBackwardIndex = 1 << 13;
|
||||
static final int kCutIndex = 1 << 14;
|
||||
static final int kCopyIndex = 1 << 15;
|
||||
static final int kPasteIndex = 1 << 16;
|
||||
static final int kSetSelectionIndex = 1 << 17;
|
||||
static final int kExpandIndex = 1 << 18;
|
||||
static final int kCollapseIndex = 1 << 19;
|
||||
static final int kSetText = 1 << 21;
|
||||
|
||||
static final Map<AccessibilityNodeInfo.AccessibilityAction, Integer> kIdByAction = new HashMap<AccessibilityNodeInfo.AccessibilityAction, Integer>() {{
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_FOCUS, kFocusIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_FOCUS, kClearFocusIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_SELECT, kSelectIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_SELECTION, kClearSelectionIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK, kClickIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK, kLongClickIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS, kAccessibilityFocusIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS, kClearAccessibilityFocusIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, kNextAtMovementGranularityIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, kPreviousAtMovementGranularityIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_NEXT_HTML_ELEMENT, kNextHtmlElementIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_PREVIOUS_HTML_ELEMENT, kPreviousHtmlElementIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD, kScrollForwardIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD, kScrollBackwardIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_CUT, kCutIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_COPY, kCopyIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_PASTE, kPasteIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_SELECTION, kSetSelectionIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND, kExpandIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE, kCollapseIndex);
|
||||
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT, kSetText);
|
||||
}};
|
||||
}
|
||||
|
@ -18,11 +18,16 @@ androidx.lifecycle:lifecycle-runtime:2.2.0=debugAndroidTestCompileClasspath,debu
|
||||
androidx.lifecycle:lifecycle-viewmodel:2.1.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.loader:loader:1.0.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.savedstate:savedstate:1.0.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.test.espresso:espresso-core:3.2.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.test.espresso:espresso-idling-resource:3.2.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.test:monitor:1.2.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.test:rules:1.2.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.test:runner:1.2.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.tracing:tracing:1.0.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.versionedparcelable:versionedparcelable:1.1.1=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.viewpager:viewpager:1.0.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.window:window-java:1.0.0-beta03=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.window:window:1.0.0-beta03=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.window:window-java:1.0.0-beta04=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.window:window:1.0.0-beta04=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.android.tools.analytics-library:protos:27.1.3=lintClassPath
|
||||
com.android.tools.analytics-library:shared:27.1.3=lintClassPath
|
||||
com.android.tools.analytics-library:tracker:27.1.3=lintClassPath
|
||||
@ -54,16 +59,19 @@ com.android.tools:sdk-common:27.1.3=lintClassPath
|
||||
com.android.tools:sdklib:27.1.3=lintClassPath
|
||||
com.android:signflinger:4.1.3=lintClassPath
|
||||
com.android:zipflinger:4.1.3=lintClassPath
|
||||
com.google.code.findbugs:jsr305:3.0.2=lintClassPath
|
||||
com.google.code.findbugs:jsr305:2.0.1=debugAndroidTestCompileClasspath
|
||||
com.google.code.findbugs:jsr305:3.0.2=debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.google.code.gson:gson:2.8.5=lintClassPath
|
||||
com.google.errorprone:error_prone_annotations:2.3.2=lintClassPath
|
||||
com.google.guava:failureaccess:1.0.1=lintClassPath
|
||||
com.google.errorprone:error_prone_annotations:2.3.2=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.google.guava:failureaccess:1.0.1=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.google.guava:guava:28.1-android=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.google.guava:guava:28.1-jre=lintClassPath
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=lintClassPath
|
||||
com.google.j2objc:j2objc-annotations:1.3=lintClassPath
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.google.j2objc:j2objc-annotations:1.3=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.google.jimfs:jimfs:1.1=lintClassPath
|
||||
com.google.protobuf:protobuf-java:3.10.0=lintClassPath
|
||||
com.googlecode.json-simple:json-simple:1.1=lintClassPath
|
||||
com.squareup:javawriter:2.1.1=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.squareup:javawriter:2.5.0=lintClassPath
|
||||
com.sun.activation:javax.activation:1.2.0=lintClassPath
|
||||
com.sun.istack:istack-commons-runtime:3.0.7=lintClassPath
|
||||
@ -72,21 +80,26 @@ commons-codec:commons-codec:1.10=lintClassPath
|
||||
commons-logging:commons-logging:1.2=lintClassPath
|
||||
it.unimi.dsi:fastutil:7.2.0=lintClassPath
|
||||
javax.activation:javax.activation-api:1.2.0=lintClassPath
|
||||
javax.inject:javax.inject:1=lintClassPath
|
||||
javax.inject:javax.inject:1=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
javax.xml.bind:jaxb-api:2.3.1=lintClassPath
|
||||
junit:junit:4.12=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
net.sf.jopt-simple:jopt-simple:4.9=lintClassPath
|
||||
net.sf.kxml:kxml2:2.3.0=lintClassPath
|
||||
net.sf.kxml:kxml2:2.3.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.apache.commons:commons-compress:1.12=lintClassPath
|
||||
org.apache.httpcomponents:httpclient:4.5.6=lintClassPath
|
||||
org.apache.httpcomponents:httpcore:4.4.10=lintClassPath
|
||||
org.apache.httpcomponents:httpmime:4.5.6=lintClassPath
|
||||
org.bouncycastle:bcpkix-jdk15on:1.56=lintClassPath
|
||||
org.bouncycastle:bcprov-jdk15on:1.56=lintClassPath
|
||||
org.checkerframework:checker-compat-qual:2.5.5=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.checkerframework:checker-qual:2.8.1=lintClassPath
|
||||
org.codehaus.groovy:groovy-all:2.4.15=lintClassPath
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18=lintClassPath
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.glassfish.jaxb:jaxb-runtime:2.3.1=lintClassPath
|
||||
org.glassfish.jaxb:txw2:2.3.1=lintClassPath
|
||||
org.hamcrest:hamcrest-core:1.3=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.hamcrest:hamcrest-integration:1.3=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.hamcrest:hamcrest-library:1.3=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.3.72=lintClassPath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72=lintClassPath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.5.31=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
|
@ -0,0 +1,122 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
androidx.activity:activity:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.annotation:annotation-experimental:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.annotation:annotation:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.arch.core:core-common:2.1.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.arch.core:core-runtime:2.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.collection:collection:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.core:core:1.6.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.customview:customview:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.fragment:fragment:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.lifecycle:lifecycle-common-java8:2.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.lifecycle:lifecycle-common:2.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.lifecycle:lifecycle-livedata-core:2.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.lifecycle:lifecycle-livedata:2.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.lifecycle:lifecycle-runtime:2.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.lifecycle:lifecycle-viewmodel:2.1.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.loader:loader:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.savedstate:savedstate:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.test.espresso:espresso-core:3.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.test.espresso:espresso-idling-resource:3.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.test:monitor:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.test:rules:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.test:runner:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.tracing:tracing:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.versionedparcelable:versionedparcelable:1.1.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.viewpager:viewpager:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.window:window-java:1.0.0-beta04=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
androidx.window:window:1.0.0-beta04=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.android.tools.analytics-library:protos:27.1.3=lintClassPath
|
||||
com.android.tools.analytics-library:shared:27.1.3=lintClassPath
|
||||
com.android.tools.analytics-library:tracker:27.1.3=lintClassPath
|
||||
com.android.tools.build:aapt2-proto:4.1.0-alpha01-6193524=lintClassPath
|
||||
com.android.tools.build:aapt2:4.1.3-6503028=_internal_aapt2_binary
|
||||
com.android.tools.build:apksig:4.1.3=lintClassPath
|
||||
com.android.tools.build:apkzlib:4.1.3=lintClassPath
|
||||
com.android.tools.build:builder-model:4.1.3=lintClassPath
|
||||
com.android.tools.build:builder-test-api:4.1.3=lintClassPath
|
||||
com.android.tools.build:builder:4.1.3=lintClassPath
|
||||
com.android.tools.build:gradle-api:4.1.3=lintClassPath
|
||||
com.android.tools.build:manifest-merger:27.1.3=lintClassPath
|
||||
com.android.tools.ddms:ddmlib:27.1.3=lintClassPath
|
||||
com.android.tools.external.com-intellij:intellij-core:27.1.3=lintClassPath
|
||||
com.android.tools.external.com-intellij:kotlin-compiler:27.1.3=lintClassPath
|
||||
com.android.tools.external.org-jetbrains:uast:27.1.3=lintClassPath
|
||||
com.android.tools.layoutlib:layoutlib-api:27.1.3=lintClassPath
|
||||
com.android.tools.lint:lint-api:27.1.3=lintClassPath
|
||||
com.android.tools.lint:lint-checks:27.1.3=lintClassPath
|
||||
com.android.tools.lint:lint-gradle-api:27.1.3=lintClassPath
|
||||
com.android.tools.lint:lint-gradle:27.1.3=lintClassPath
|
||||
com.android.tools.lint:lint-model:27.1.3=lintClassPath
|
||||
com.android.tools.lint:lint:27.1.3=lintClassPath
|
||||
com.android.tools:annotations:27.1.3=lintClassPath
|
||||
com.android.tools:common:27.1.3=lintClassPath
|
||||
com.android.tools:dvlib:27.1.3=lintClassPath
|
||||
com.android.tools:repository:27.1.3=lintClassPath
|
||||
com.android.tools:sdk-common:27.1.3=lintClassPath
|
||||
com.android.tools:sdklib:27.1.3=lintClassPath
|
||||
com.android:signflinger:4.1.3=lintClassPath
|
||||
com.android:zipflinger:4.1.3=lintClassPath
|
||||
com.google.code.findbugs:jsr305:3.0.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.google.code.gson:gson:2.8.5=lintClassPath
|
||||
com.google.errorprone:error_prone_annotations:2.3.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.google.guava:failureaccess:1.0.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.google.guava:guava:28.1-android=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.google.guava:guava:28.1-jre=lintClassPath
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.google.j2objc:j2objc-annotations:1.3=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.google.jimfs:jimfs:1.1=lintClassPath
|
||||
com.google.protobuf:protobuf-java:3.10.0=lintClassPath
|
||||
com.googlecode.json-simple:json-simple:1.1=lintClassPath
|
||||
com.squareup:javawriter:2.1.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
com.squareup:javawriter:2.5.0=lintClassPath
|
||||
com.sun.activation:javax.activation:1.2.0=lintClassPath
|
||||
com.sun.istack:istack-commons-runtime:3.0.7=lintClassPath
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15=lintClassPath
|
||||
commons-codec:commons-codec:1.10=lintClassPath
|
||||
commons-logging:commons-logging:1.2=lintClassPath
|
||||
it.unimi.dsi:fastutil:7.2.0=lintClassPath
|
||||
javax.activation:javax.activation-api:1.2.0=lintClassPath
|
||||
javax.inject:javax.inject:1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
javax.xml.bind:jaxb-api:2.3.1=lintClassPath
|
||||
junit:junit:4.12=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
net.sf.jopt-simple:jopt-simple:4.9=lintClassPath
|
||||
net.sf.kxml:kxml2:2.3.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.apache.commons:commons-compress:1.12=lintClassPath
|
||||
org.apache.httpcomponents:httpclient:4.5.6=lintClassPath
|
||||
org.apache.httpcomponents:httpcore:4.4.10=lintClassPath
|
||||
org.apache.httpcomponents:httpmime:4.5.6=lintClassPath
|
||||
org.bouncycastle:bcpkix-jdk15on:1.56=lintClassPath
|
||||
org.bouncycastle:bcprov-jdk15on:1.56=lintClassPath
|
||||
org.checkerframework:checker-compat-qual:2.5.5=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.checkerframework:checker-qual:2.8.1=lintClassPath
|
||||
org.codehaus.groovy:groovy-all:2.4.15=lintClassPath
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.glassfish.jaxb:jaxb-runtime:2.3.1=lintClassPath
|
||||
org.glassfish.jaxb:txw2:2.3.1=lintClassPath
|
||||
org.hamcrest:hamcrest-core:1.3=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.hamcrest:hamcrest-integration:1.3=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.hamcrest:hamcrest-library:1.3=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.3.72=lintClassPath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72=lintClassPath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.5.31=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72=lintClassPath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.30=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72=lintClassPath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.3.72=lintClassPath
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.5.31=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.jetbrains.trove4j:trove4j:20160824=lintClassPath
|
||||
org.jetbrains:annotations:13.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
|
||||
org.jvnet.staxex:stax-ex:1.8=lintClassPath
|
||||
org.ow2.asm:asm-analysis:7.0=lintClassPath
|
||||
org.ow2.asm:asm-commons:7.0=lintClassPath
|
||||
org.ow2.asm:asm-tree:7.0=lintClassPath
|
||||
org.ow2.asm:asm-util:7.0=lintClassPath
|
||||
org.ow2.asm:asm:7.0=lintClassPath
|
||||
empty=androidApis,androidTestUtil,compile,coreLibraryDesugaring,debugAndroidTestAnnotationProcessorClasspath,debugAnnotationProcessorClasspath,debugUnitTestAnnotationProcessorClasspath,lintChecks,lintPublish,profileAnnotationProcessorClasspath,profileUnitTestAnnotationProcessorClasspath,releaseAnnotationProcessorClasspath,releaseUnitTestAnnotationProcessorClasspath,testCompile
|
@ -0,0 +1,677 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:android_semantics_testing/android_semantics_testing.dart';
|
||||
import 'package:android_semantics_testing/main.dart' as app;
|
||||
import 'package:android_semantics_testing/test_constants.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
// The accessibility focus actions are added when a semantics node receives or
|
||||
// lose accessibility focus. This test ignores these actions since it is hard to
|
||||
// predict which node has the accessibility focus after a screen changes.
|
||||
const List<AndroidSemanticsAction> ignoredAccessibilityFocusActions = <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.accessibilityFocus,
|
||||
AndroidSemanticsAction.clearAccessibilityFocus,
|
||||
];
|
||||
|
||||
const MethodChannel kSemanticsChannel = MethodChannel('semantics');
|
||||
|
||||
Future<void> setClipboard(String message) async {
|
||||
final Completer<void> completer = Completer<void>();
|
||||
Future<void> completeSetClipboard([Object? _]) async {
|
||||
await kSemanticsChannel.invokeMethod<dynamic>('setClipboard', <String, dynamic>{
|
||||
'message': message,
|
||||
});
|
||||
completer.complete();
|
||||
}
|
||||
if (SchedulerBinding.instance.hasScheduledFrame) {
|
||||
SchedulerBinding.instance.addPostFrameCallback(completeSetClipboard);
|
||||
} else {
|
||||
completeSetClipboard();
|
||||
}
|
||||
await completer.future;
|
||||
}
|
||||
|
||||
Future<AndroidSemanticsNode> getSemantics(Finder finder, WidgetTester tester) async {
|
||||
final int id = tester.getSemantics(finder).id;
|
||||
final Completer<String> completer = Completer<String>();
|
||||
Future<void> completeSemantics([Object? _]) async {
|
||||
final dynamic result = await kSemanticsChannel.invokeMethod<dynamic>('getSemanticsNode', <String, dynamic>{
|
||||
'id': id,
|
||||
});
|
||||
completer.complete(json.encode(result));
|
||||
}
|
||||
if (SchedulerBinding.instance.hasScheduledFrame) {
|
||||
SchedulerBinding.instance.addPostFrameCallback(completeSemantics);
|
||||
} else {
|
||||
completeSemantics();
|
||||
}
|
||||
return AndroidSemanticsNode.deserialize(await completer.future);
|
||||
}
|
||||
|
||||
Future<void> main() async {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('AccessibilityBridge', () {
|
||||
group('TextField', () {
|
||||
Future<void> prepareTextField(WidgetTester tester) async {
|
||||
app.main();
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text(textFieldRoute));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// The text selection menu and related semantics vary depending on if
|
||||
// the clipboard contents are pasteable. Copy some text into the
|
||||
// clipboard to make sure these tests always run with pasteable content
|
||||
// in the clipboard.
|
||||
// Ideally this should test the case where there is nothing on the
|
||||
// clipboard as well, but there is no reliable way to clear the
|
||||
// clipboard on Android devices.
|
||||
await setClipboard('Hello World');
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
testWidgets('TextField has correct Android semantics', (WidgetTester tester) async {
|
||||
final Finder normalTextField = find.descendant(
|
||||
of: find.byKey(const ValueKey<String>(normalTextFieldKeyValue)),
|
||||
matching: find.byType(EditableText),
|
||||
);
|
||||
|
||||
await prepareTextField(tester);
|
||||
expect(
|
||||
await getSemantics(normalTextField, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.editText,
|
||||
isEditable: true,
|
||||
isFocusable: true,
|
||||
isFocused: false,
|
||||
isPassword: false,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
// We can't predict the a11y focus when the screen changes.
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
),
|
||||
);
|
||||
await tester.tap(normalTextField);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
await getSemantics(normalTextField, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.editText,
|
||||
isFocusable: true,
|
||||
isFocused: true,
|
||||
isEditable: true,
|
||||
isPassword: false,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
AndroidSemanticsAction.paste,
|
||||
AndroidSemanticsAction.setSelection,
|
||||
AndroidSemanticsAction.setText,
|
||||
],
|
||||
// We can't predict the a11y focus when the screen changes.
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.enterText(normalTextField, 'hello world');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
await getSemantics(normalTextField, tester),
|
||||
hasAndroidSemantics(
|
||||
text: 'hello world',
|
||||
className: AndroidClassName.editText,
|
||||
isFocusable: true,
|
||||
isFocused: true,
|
||||
isEditable: true,
|
||||
isPassword: false,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
AndroidSemanticsAction.paste,
|
||||
AndroidSemanticsAction.setSelection,
|
||||
AndroidSemanticsAction.setText,
|
||||
AndroidSemanticsAction.previousAtMovementGranularity,
|
||||
],
|
||||
// We can't predict the a11y focus when the screen changes.
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
testWidgets('password TextField has correct Android semantics', (WidgetTester tester) async {
|
||||
final Finder passwordTextField = find.descendant(
|
||||
of: find.byKey(const ValueKey<String>(passwordTextFieldKeyValue)),
|
||||
matching: find.byType(EditableText),
|
||||
);
|
||||
|
||||
await prepareTextField(tester);
|
||||
expect(
|
||||
await getSemantics(passwordTextField, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.editText,
|
||||
isEditable: true,
|
||||
isFocusable: true,
|
||||
isFocused: false,
|
||||
isPassword: true,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
// We can't predict the a11y focus when the screen changes.
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(passwordTextField);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
await getSemantics(passwordTextField, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.editText,
|
||||
isFocusable: true,
|
||||
isFocused: true,
|
||||
isEditable: true,
|
||||
isPassword: true,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
AndroidSemanticsAction.paste,
|
||||
AndroidSemanticsAction.setSelection,
|
||||
AndroidSemanticsAction.setText,
|
||||
],
|
||||
// We can't predict the a11y focus when the screen changes.
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.enterText(passwordTextField, 'hello world');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
await getSemantics(passwordTextField, tester),
|
||||
hasAndroidSemantics(
|
||||
text: '\u{2022}' * ('hello world'.length),
|
||||
className: AndroidClassName.editText,
|
||||
isFocusable: true,
|
||||
isFocused: true,
|
||||
isEditable: true,
|
||||
isPassword: true,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
AndroidSemanticsAction.paste,
|
||||
AndroidSemanticsAction.setSelection,
|
||||
AndroidSemanticsAction.setText,
|
||||
AndroidSemanticsAction.previousAtMovementGranularity,
|
||||
],
|
||||
// We can't predict the a11y focus when the screen changes.
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
});
|
||||
|
||||
group('SelectionControls', () {
|
||||
Future<void> prepareSelectionControls(WidgetTester tester) async {
|
||||
app.main();
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text(selectionControlsRoute));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
testWidgets('Checkbox has correct Android semantics', (WidgetTester tester) async {
|
||||
final Finder checkbox = find.byKey(const ValueKey<String>(checkboxKeyValue));
|
||||
final Finder disabledCheckbox = find.byKey(const ValueKey<String>(disabledCheckboxKeyValue));
|
||||
|
||||
await prepareSelectionControls(tester);
|
||||
expect(
|
||||
await getSemantics(checkbox, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.checkBox,
|
||||
isChecked: false,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(checkbox);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
await getSemantics(checkbox, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.checkBox,
|
||||
isChecked: true,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await getSemantics(disabledCheckbox, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.checkBox,
|
||||
isCheckable: true,
|
||||
isEnabled: false,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: const <AndroidSemanticsAction>[],
|
||||
),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
testWidgets('Radio has correct Android semantics', (WidgetTester tester) async {
|
||||
final Finder radio = find.byKey(const ValueKey<String>(radio2KeyValue));
|
||||
|
||||
await prepareSelectionControls(tester);
|
||||
expect(
|
||||
await getSemantics(radio, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.radio,
|
||||
isChecked: false,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(radio);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
await getSemantics(radio, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.radio,
|
||||
isChecked: true,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
testWidgets('Switch has correct Android semantics', (WidgetTester tester) async {
|
||||
final Finder switchFinder = find.byKey(const ValueKey<String>(switchKeyValue));
|
||||
|
||||
await prepareSelectionControls(tester);
|
||||
expect(
|
||||
await getSemantics(switchFinder, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.toggleSwitch,
|
||||
isChecked: false,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(switchFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
await getSemantics(switchFinder, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.toggleSwitch,
|
||||
isChecked: true,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/20820.
|
||||
testWidgets('Switch can be labeled', (WidgetTester tester) async {
|
||||
final Finder switchFinder = find.byKey(const ValueKey<String>(labeledSwitchKeyValue));
|
||||
|
||||
await prepareSelectionControls(tester);
|
||||
expect(
|
||||
await getSemantics(switchFinder, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.toggleSwitch,
|
||||
isChecked: false,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
contentDescription: switchLabel,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
});
|
||||
|
||||
group('Popup Controls', () {
|
||||
Future<void> preparePopupControls(WidgetTester tester) async {
|
||||
app.main();
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text(popupControlsRoute));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
testWidgets('Popup Menu has correct Android semantics', (WidgetTester tester) async {
|
||||
final Finder popupButton = find.byKey(const ValueKey<String>(popupButtonKeyValue));
|
||||
|
||||
await preparePopupControls(tester);
|
||||
expect(
|
||||
await getSemantics(popupButton, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(popupButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
try {
|
||||
for (final String item in popupItems) {
|
||||
expect(
|
||||
await getSemantics(find.byKey(ValueKey<String>('$popupKeyValue.$item')), tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
reason: "Popup $item doesn't have the right semantics",
|
||||
);
|
||||
}
|
||||
await tester.tap(find.byKey(ValueKey<String>('$popupKeyValue.${popupItems.first}')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Pop up the menu again, to verify that TalkBack gets the right answer
|
||||
// more than just the first time.
|
||||
await tester.tap(popupButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
for (final String item in popupItems) {
|
||||
expect(
|
||||
await getSemantics(find.byKey(ValueKey<String>('$popupKeyValue.$item')), tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
reason: "Popup $item doesn't have the right semantics the second time",
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
await tester.tap(find.byKey(ValueKey<String>('$popupKeyValue.${popupItems.first}')));
|
||||
}
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
testWidgets('Dropdown Menu has correct Android semantics', (WidgetTester tester) async {
|
||||
final Finder dropdownButton = find.byKey(const ValueKey<String>(dropdownButtonKeyValue));
|
||||
|
||||
await preparePopupControls(tester);
|
||||
expect(
|
||||
await getSemantics(dropdownButton, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(dropdownButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
try {
|
||||
for (final String item in popupItems) {
|
||||
// There are two copies of each item, so we want to find the version
|
||||
// that is in the overlay, not the one in the dropdown.
|
||||
expect(
|
||||
await getSemantics(
|
||||
find.descendant(
|
||||
of: find.byType(Scrollable),
|
||||
matching: find.byKey(ValueKey<String>('$dropdownKeyValue.$item')),
|
||||
),
|
||||
tester,
|
||||
),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.view,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
reason: "Dropdown $item doesn't have the right semantics",
|
||||
);
|
||||
}
|
||||
await tester.tap(
|
||||
find.descendant(
|
||||
of: find.byType(Scrollable),
|
||||
matching: find.byKey(ValueKey<String>('$dropdownKeyValue.${popupItems.first}')),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Pop up the dropdown again, to verify that TalkBack gets the right answer
|
||||
// more than just the first time.
|
||||
await tester.tap(dropdownButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
for (final String item in popupItems) {
|
||||
// There are two copies of each item, so we want to find the version
|
||||
// that is in the overlay, not the one in the dropdown.
|
||||
expect(
|
||||
await getSemantics(
|
||||
find.descendant(
|
||||
of: find.byType(Scrollable),
|
||||
matching: find.byKey(ValueKey<String>('$dropdownKeyValue.$item')),
|
||||
),
|
||||
tester,
|
||||
),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.view,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
reason: "Dropdown $item doesn't have the right semantics the second time.",
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
await tester.tap(
|
||||
find.descendant(
|
||||
of: find.byType(Scrollable),
|
||||
matching: find.byKey(ValueKey<String>('$dropdownKeyValue.${popupItems.first}')),
|
||||
),
|
||||
);
|
||||
}
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
testWidgets('Modal alert dialog has correct Android semantics', (WidgetTester tester) async {
|
||||
final Finder alertButton = find.byKey(const ValueKey<String>(alertButtonKeyValue));
|
||||
|
||||
await preparePopupControls(tester);
|
||||
expect(
|
||||
await getSemantics(alertButton, tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(alertButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
try {
|
||||
expect(
|
||||
await getSemantics(find.byKey(const ValueKey<String>('$alertKeyValue.OK')), tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
reason: "Alert OK button doesn't have the right semantics",
|
||||
);
|
||||
|
||||
for (final String item in <String>['Title', 'Body1', 'Body2']) {
|
||||
expect(
|
||||
await getSemantics(find.byKey(ValueKey<String>('$alertKeyValue.$item')), tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.view,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[],
|
||||
),
|
||||
reason: "Alert $item button doesn't have the right semantics",
|
||||
);
|
||||
}
|
||||
|
||||
await tester.tap(find.byKey(const ValueKey<String>('$alertKeyValue.OK')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Pop up the alert again, to verify that TalkBack gets the right answer
|
||||
// more than just the first time.
|
||||
await tester.tap(alertButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
await getSemantics(find.byKey(const ValueKey<String>('$alertKeyValue.OK')), tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
reason: "Alert OK button doesn't have the right semantics",
|
||||
);
|
||||
|
||||
for (final String item in <String>['Title', 'Body1', 'Body2']) {
|
||||
expect(
|
||||
await getSemantics(find.byKey(ValueKey<String>('$alertKeyValue.$item')), tester),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.view,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[],
|
||||
),
|
||||
reason: "Alert $item button doesn't have the right semantics",
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
await tester.tap(find.byKey(const ValueKey<String>('$alertKeyValue.OK')));
|
||||
}
|
||||
}, timeout: Timeout.none);
|
||||
});
|
||||
|
||||
group('Headings', () {
|
||||
Future<void> prepareHeading(WidgetTester tester) async {
|
||||
app.main();
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text(headingsRoute));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
testWidgets('AppBar title has correct Android heading semantics', (WidgetTester tester) async {
|
||||
await prepareHeading(tester);
|
||||
expect(
|
||||
await getSemantics(find.byKey(const ValueKey<String>(appBarTitleKeyValue)), tester),
|
||||
hasAndroidSemantics(isHeading: true),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
testWidgets('body text does not have Android heading semantics', (WidgetTester tester) async {
|
||||
await prepareHeading(tester);
|
||||
expect(
|
||||
await getSemantics(find.byKey(const ValueKey<String>(bodyTextKeyValue)), tester),
|
||||
hasAndroidSemantics(isHeading: false),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
});
|
||||
});
|
||||
}
|
@ -2,13 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_driver/driver_extension.dart';
|
||||
|
||||
import 'src/tests/controls_page.dart';
|
||||
import 'src/tests/headings_page.dart';
|
||||
@ -16,49 +10,9 @@ import 'src/tests/popup_page.dart';
|
||||
import 'src/tests/text_field_page.dart';
|
||||
|
||||
void main() {
|
||||
timeDilation = 0.05; // remove animations.
|
||||
enableFlutterDriverExtension(handler: dataHandler);
|
||||
runApp(const TestApp());
|
||||
}
|
||||
|
||||
const MethodChannel kSemanticsChannel = MethodChannel('semantics');
|
||||
|
||||
Future<String> dataHandler(String? message) async {
|
||||
if (message != null && message.contains('getSemanticsNode')) {
|
||||
final Completer<String> completer = Completer<String>();
|
||||
final int id = int.tryParse(message.split('#')[1]) ?? 0;
|
||||
Future<void> completeSemantics([Object? _]) async {
|
||||
final dynamic result = await kSemanticsChannel.invokeMethod<dynamic>('getSemanticsNode', <String, dynamic>{
|
||||
'id': id,
|
||||
});
|
||||
completer.complete(json.encode(result));
|
||||
}
|
||||
if (SchedulerBinding.instance.hasScheduledFrame) {
|
||||
SchedulerBinding.instance.addPostFrameCallback(completeSemantics);
|
||||
} else {
|
||||
completeSemantics();
|
||||
}
|
||||
return completer.future;
|
||||
}
|
||||
if (message != null && message.contains('setClipboard')) {
|
||||
final Completer<String> completer = Completer<String>();
|
||||
final String str = message.split('#')[1];
|
||||
Future<void> completeSetClipboard([Object? _]) async {
|
||||
await kSemanticsChannel.invokeMethod<dynamic>('setClipboard', <String, dynamic>{
|
||||
'message': str,
|
||||
});
|
||||
completer.complete('');
|
||||
}
|
||||
if (SchedulerBinding.instance.hasScheduledFrame) {
|
||||
SchedulerBinding.instance.addPostFrameCallback(completeSetClipboard);
|
||||
} else {
|
||||
completeSetClipboard();
|
||||
}
|
||||
return completer.future;
|
||||
}
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
|
||||
selectionControlsRoute : (BuildContext context) => const SelectionControlsPage(),
|
||||
popupControlsRoute : (BuildContext context) => const PopupControlsPage(),
|
||||
|
@ -98,6 +98,7 @@ enum AndroidSemanticsAction {
|
||||
/// The Android id of the action.
|
||||
final int id;
|
||||
|
||||
// These indices need to be in sync with android_semantics_testing/android/app/src/main/java/com/yourcompany/platforminteraction/MainActivity.java
|
||||
static const int _kFocusIndex = 1 << 0;
|
||||
static const int _kClearFocusIndex = 1 << 1;
|
||||
static const int _kSelectIndex = 1 << 2;
|
||||
|
@ -77,7 +77,8 @@ class _AndroidSemanticsMatcher extends Matcher {
|
||||
this.isHeading,
|
||||
this.isPassword,
|
||||
this.isLongClickable,
|
||||
});
|
||||
}) : assert(ignoredActions == null || actions != null, 'actions must not be null if ignoredActions is not null'),
|
||||
assert(ignoredActions == null || !actions!.any(ignoredActions.contains));
|
||||
|
||||
final String? text;
|
||||
final String? className;
|
||||
@ -115,6 +116,9 @@ class _AndroidSemanticsMatcher extends Matcher {
|
||||
if (actions != null) {
|
||||
description.add(' with actions: $actions');
|
||||
}
|
||||
if (ignoredActions != null) {
|
||||
description.add(' with ignoredActions: $ignoredActions');
|
||||
}
|
||||
if (rect != null) {
|
||||
description.add(' with rect: $rect');
|
||||
}
|
||||
@ -170,13 +174,15 @@ class _AndroidSemanticsMatcher extends Matcher {
|
||||
}
|
||||
if (actions != null) {
|
||||
final List<AndroidSemanticsAction> itemActions = item.getActions();
|
||||
if (ignoredActions != null) {
|
||||
itemActions.removeWhere(ignoredActions!.contains);
|
||||
}
|
||||
if (!unorderedEquals(actions!).matches(itemActions, matchState)) {
|
||||
final List<String> actionsString = actions!.map<String>((AndroidSemanticsAction action) => action.toString()).toList()..sort();
|
||||
final List<String> itemActionsString = itemActions.map<String>((AndroidSemanticsAction action) => action.toString()).toList()..sort();
|
||||
final Set<AndroidSemanticsAction> unexpected = itemActions.toSet().difference(actions!.toSet());
|
||||
final Set<String> unexpectedInString = itemActionsString.toSet().difference(actionsString.toSet());
|
||||
final Set<String> missingInString = actionsString.toSet().difference(itemActionsString.toSet());
|
||||
if (missingInString.isEmpty && ignoredActions != null && unexpected.every(ignoredActions!.contains)) {
|
||||
if (missingInString.isEmpty && unexpectedInString.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
return _failWithMessage('Expected actions: $actionsString\nActual actions: $itemActionsString\nUnexpected: $unexpectedInString\nMissing: $missingInString', matchState);
|
||||
|
@ -6,7 +6,7 @@ environment:
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_driver:
|
||||
integration_test:
|
||||
sdk: flutter
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
@ -1,702 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:io' as io;
|
||||
|
||||
import 'package:android_semantics_testing/android_semantics_testing.dart';
|
||||
import 'package:android_semantics_testing/test_constants.dart';
|
||||
import 'package:flutter_driver/flutter_driver.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:pub_semver/pub_semver.dart';
|
||||
import 'package:test/test.dart' hide isInstanceOf;
|
||||
|
||||
// The accessibility focus actions are added when a semantics node receives or
|
||||
// lose accessibility focus. This test ignores these actions since it is hard to
|
||||
// predict which node has the accessibility focus after a screen changes.
|
||||
const List<AndroidSemanticsAction> ignoredAccessibilityFocusActions = <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.accessibilityFocus,
|
||||
AndroidSemanticsAction.clearAccessibilityFocus,
|
||||
];
|
||||
|
||||
String adbPath() {
|
||||
final String? androidHome = io.Platform.environment['ANDROID_HOME'] ?? io.Platform.environment['ANDROID_SDK_ROOT'];
|
||||
if (androidHome == null) {
|
||||
return 'adb';
|
||||
} else {
|
||||
return path.join(androidHome, 'platform-tools', 'adb');
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('AccessibilityBridge', () {
|
||||
late FlutterDriver driver;
|
||||
Future<AndroidSemanticsNode> getSemantics(SerializableFinder finder) async {
|
||||
final int id = await driver.getSemanticsId(finder);
|
||||
final String data = await driver.requestData('getSemanticsNode#$id');
|
||||
return AndroidSemanticsNode.deserialize(data);
|
||||
}
|
||||
|
||||
// The version of TalkBack running on the device.
|
||||
Version? talkbackVersion;
|
||||
|
||||
Future<Version> getTalkbackVersion() async {
|
||||
final io.ProcessResult result = await io.Process.run(adbPath(), const <String>[
|
||||
'shell',
|
||||
'dumpsys',
|
||||
'package',
|
||||
'com.google.android.marvin.talkback',
|
||||
]);
|
||||
if (result.exitCode != 0) {
|
||||
throw Exception('Failed to get TalkBack version: ${result.stdout as String}\n${result.stderr as String}');
|
||||
}
|
||||
final List<String> lines = (result.stdout as String).split('\n');
|
||||
String? version;
|
||||
for (final String line in lines) {
|
||||
if (line.contains('versionName')) {
|
||||
version = line.replaceAll(RegExp(r'\s*versionName='), '');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (version == null) {
|
||||
throw Exception('Unable to determine TalkBack version.');
|
||||
}
|
||||
|
||||
// Android doesn't quite use semver, so convert the version string to semver form.
|
||||
final RegExp startVersion = RegExp(r'(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(\.(?<build>\d+))?');
|
||||
final RegExpMatch? match = startVersion.firstMatch(version);
|
||||
if (match == null) {
|
||||
return Version(0, 0, 0);
|
||||
}
|
||||
return Version(
|
||||
int.parse(match.namedGroup('major')!),
|
||||
int.parse(match.namedGroup('minor')!),
|
||||
int.parse(match.namedGroup('patch')!),
|
||||
build: match.namedGroup('build'),
|
||||
);
|
||||
}
|
||||
|
||||
setUpAll(() async {
|
||||
driver = await FlutterDriver.connect();
|
||||
talkbackVersion ??= await getTalkbackVersion();
|
||||
print('TalkBack version is $talkbackVersion');
|
||||
|
||||
// Say the magic words..
|
||||
final io.Process run = await io.Process.start(adbPath(), const <String>[
|
||||
'shell',
|
||||
'settings',
|
||||
'put',
|
||||
'secure',
|
||||
'enabled_accessibility_services',
|
||||
'com.google.android.marvin.talkback/com.google.android.marvin.talkback.TalkBackService',
|
||||
]);
|
||||
await run.exitCode;
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
// ... And turn it off again
|
||||
final io.Process run = await io.Process.start(adbPath(), const <String>[
|
||||
'shell',
|
||||
'settings',
|
||||
'put',
|
||||
'secure',
|
||||
'enabled_accessibility_services',
|
||||
'null',
|
||||
]);
|
||||
await run.exitCode;
|
||||
driver.close();
|
||||
});
|
||||
|
||||
group('TextField', () {
|
||||
setUpAll(() async {
|
||||
await driver.tap(find.text(textFieldRoute));
|
||||
// Delay for TalkBack to update focus as of November 2019 with Pixel 3 and Android API 28
|
||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
// The text selection menu and related semantics vary depending on if
|
||||
// the clipboard contents are pasteable. Copy some text into the
|
||||
// clipboard to make sure these tests always run with pasteable content
|
||||
// in the clipboard.
|
||||
// Ideally this should test the case where there is nothing on the
|
||||
// clipboard as well, but there is no reliable way to clear the
|
||||
// clipboard on Android devices.
|
||||
await driver.requestData('setClipboard#Hello World');
|
||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
||||
});
|
||||
|
||||
test('TextField has correct Android semantics', () async {
|
||||
final SerializableFinder normalTextField = find.descendant(
|
||||
of: find.byValueKey(normalTextFieldKeyValue),
|
||||
matching: find.byType('Semantics'),
|
||||
firstMatchOnly: true,
|
||||
);
|
||||
expect(
|
||||
await getSemantics(normalTextField),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.editText,
|
||||
isEditable: true,
|
||||
isFocusable: true,
|
||||
isFocused: false,
|
||||
isPassword: false,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
// We can't predict the a11y focus when the screen changes.
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
),
|
||||
);
|
||||
|
||||
await driver.tap(normalTextField);
|
||||
// Delay for TalkBack to update focus as of November 2019 with Pixel 3 and Android API 28
|
||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
expect(
|
||||
await getSemantics(normalTextField),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.editText,
|
||||
isFocusable: true,
|
||||
isFocused: true,
|
||||
isEditable: true,
|
||||
isPassword: false,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
AndroidSemanticsAction.copy,
|
||||
AndroidSemanticsAction.setSelection,
|
||||
AndroidSemanticsAction.setText,
|
||||
],
|
||||
// We can't predict the a11y focus when the screen changes.
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
),
|
||||
);
|
||||
|
||||
await driver.enterText('hello world');
|
||||
// Delay for TalkBack to update focus as of November 2019 with Pixel 3 and Android API 28
|
||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
expect(
|
||||
await getSemantics(normalTextField),
|
||||
hasAndroidSemantics(
|
||||
text: 'hello world',
|
||||
className: AndroidClassName.editText,
|
||||
isFocusable: true,
|
||||
isFocused: true,
|
||||
isEditable: true,
|
||||
isPassword: false,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
AndroidSemanticsAction.copy,
|
||||
AndroidSemanticsAction.setSelection,
|
||||
AndroidSemanticsAction.setText,
|
||||
AndroidSemanticsAction.previousAtMovementGranularity,
|
||||
],
|
||||
// We can't predict the a11y focus when the screen changes.
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
test('password TextField has correct Android semantics', () async {
|
||||
final SerializableFinder passwordTextField = find.descendant(
|
||||
of: find.byValueKey(passwordTextFieldKeyValue),
|
||||
matching: find.byType('Semantics'),
|
||||
firstMatchOnly: true,
|
||||
);
|
||||
expect(
|
||||
await getSemantics(passwordTextField),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.editText,
|
||||
isEditable: true,
|
||||
isFocusable: true,
|
||||
isFocused: false,
|
||||
isPassword: true,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
// We can't predict the a11y focus when the screen changes.
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
),
|
||||
);
|
||||
|
||||
await driver.tap(passwordTextField);
|
||||
// Delay for TalkBack to update focus as of November 2019 with Pixel 3 and Android API 28
|
||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
expect(
|
||||
await getSemantics(passwordTextField),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.editText,
|
||||
isFocusable: true,
|
||||
isFocused: true,
|
||||
isEditable: true,
|
||||
isPassword: true,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
AndroidSemanticsAction.copy,
|
||||
AndroidSemanticsAction.setSelection,
|
||||
AndroidSemanticsAction.setText,
|
||||
],
|
||||
// We can't predict the a11y focus when the screen changes.
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
),
|
||||
);
|
||||
|
||||
await driver.enterText('hello world');
|
||||
// Delay for TalkBack to update focus as of November 2019 with Pixel 3 and Android API 28
|
||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
expect(
|
||||
await getSemantics(passwordTextField),
|
||||
hasAndroidSemantics(
|
||||
text: '\u{2022}' * ('hello world'.length),
|
||||
className: AndroidClassName.editText,
|
||||
isFocusable: true,
|
||||
isFocused: true,
|
||||
isEditable: true,
|
||||
isPassword: true,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
AndroidSemanticsAction.copy,
|
||||
AndroidSemanticsAction.setSelection,
|
||||
AndroidSemanticsAction.setText,
|
||||
AndroidSemanticsAction.previousAtMovementGranularity,
|
||||
],
|
||||
// We can't predict the a11y focus when the screen changes.
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
tearDownAll(() async {
|
||||
await driver.tap(find.byValueKey('back'));
|
||||
});
|
||||
});
|
||||
|
||||
group('SelectionControls', () {
|
||||
setUpAll(() async {
|
||||
await driver.tap(find.text(selectionControlsRoute));
|
||||
});
|
||||
|
||||
test('Checkbox has correct Android semantics', () async {
|
||||
Future<AndroidSemanticsNode> getCheckboxSemantics(String key) async {
|
||||
return getSemantics(find.byValueKey(key));
|
||||
}
|
||||
expect(
|
||||
await getCheckboxSemantics(checkboxKeyValue),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.checkBox,
|
||||
isChecked: false,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
await driver.tap(find.byValueKey(checkboxKeyValue));
|
||||
|
||||
expect(
|
||||
await getCheckboxSemantics(checkboxKeyValue),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.checkBox,
|
||||
isChecked: true,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await getCheckboxSemantics(disabledCheckboxKeyValue),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.checkBox,
|
||||
isCheckable: true,
|
||||
isEnabled: false,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: const <AndroidSemanticsAction>[],
|
||||
),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
test('Radio has correct Android semantics', () async {
|
||||
Future<AndroidSemanticsNode> getRadioSemantics(String key) async {
|
||||
return getSemantics(find.byValueKey(key));
|
||||
}
|
||||
expect(
|
||||
await getRadioSemantics(radio2KeyValue),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.radio,
|
||||
isChecked: false,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
await driver.tap(find.byValueKey(radio2KeyValue));
|
||||
|
||||
expect(
|
||||
await getRadioSemantics(radio2KeyValue),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.radio,
|
||||
isChecked: true,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
test('Switch has correct Android semantics', () async {
|
||||
Future<AndroidSemanticsNode> getSwitchSemantics(String key) async {
|
||||
return getSemantics(find.byValueKey(key));
|
||||
}
|
||||
expect(
|
||||
await getSwitchSemantics(switchKeyValue),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.toggleSwitch,
|
||||
isChecked: false,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
await driver.tap(find.byValueKey(switchKeyValue));
|
||||
|
||||
expect(
|
||||
await getSwitchSemantics(switchKeyValue),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.toggleSwitch,
|
||||
isChecked: true,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/20820.
|
||||
test('Switch can be labeled', () async {
|
||||
Future<AndroidSemanticsNode> getSwitchSemantics(String key) async {
|
||||
return getSemantics(find.byValueKey(key));
|
||||
}
|
||||
expect(
|
||||
await getSwitchSemantics(labeledSwitchKeyValue),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.toggleSwitch,
|
||||
isChecked: false,
|
||||
isCheckable: true,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
contentDescription: switchLabel,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
tearDownAll(() async {
|
||||
await driver.tap(find.byValueKey('back'));
|
||||
});
|
||||
});
|
||||
|
||||
group('Popup Controls', () {
|
||||
setUpAll(() async {
|
||||
await driver.tap(find.text(popupControlsRoute));
|
||||
});
|
||||
|
||||
test('Popup Menu has correct Android semantics', () async {
|
||||
expect(
|
||||
await getSemantics(find.byValueKey(popupButtonKeyValue)),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
await driver.tap(find.byValueKey(popupButtonKeyValue));
|
||||
try {
|
||||
// We have to wait wall time here because we're waiting for TalkBack to
|
||||
// catch up.
|
||||
await Future<void>.delayed(const Duration(milliseconds: 1500));
|
||||
|
||||
for (final String item in popupItems) {
|
||||
expect(
|
||||
await getSemantics(find.byValueKey('$popupKeyValue.$item')),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
reason: "Popup $item doesn't have the right semantics");
|
||||
}
|
||||
await driver.tap(find.byValueKey('$popupKeyValue.${popupItems.first}'));
|
||||
|
||||
// Pop up the menu again, to verify that TalkBack gets the right answer
|
||||
// more than just the first time.
|
||||
await driver.tap(find.byValueKey(popupButtonKeyValue));
|
||||
await Future<void>.delayed(const Duration(milliseconds: 1500));
|
||||
|
||||
for (final String item in popupItems) {
|
||||
expect(
|
||||
await getSemantics(find.byValueKey('$popupKeyValue.$item')),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
reason: "Popup $item doesn't have the right semantics the second time");
|
||||
}
|
||||
} finally {
|
||||
await driver.tap(find.byValueKey('$popupKeyValue.${popupItems.first}'));
|
||||
}
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
test('Dropdown Menu has correct Android semantics', () async {
|
||||
expect(
|
||||
await getSemantics(find.byValueKey(dropdownButtonKeyValue)),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
await driver.tap(find.byValueKey(dropdownButtonKeyValue));
|
||||
try {
|
||||
await Future<void>.delayed(const Duration(milliseconds: 1500));
|
||||
|
||||
for (final String item in popupItems) {
|
||||
// There are two copies of each item, so we want to find the version
|
||||
// that is in the overlay, not the one in the dropdown.
|
||||
expect(
|
||||
await getSemantics(find.descendant(
|
||||
of: find.byType('Scrollable'),
|
||||
matching: find.byValueKey('$dropdownKeyValue.$item'),
|
||||
)),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.view,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
reason: "Dropdown $item doesn't have the right semantics");
|
||||
}
|
||||
await driver.tap(
|
||||
find.descendant(
|
||||
of: find.byType('Scrollable'),
|
||||
matching: find.byValueKey('$dropdownKeyValue.${popupItems.first}'),
|
||||
),
|
||||
);
|
||||
|
||||
// Pop up the dropdown again, to verify that TalkBack gets the right answer
|
||||
// more than just the first time.
|
||||
await driver.tap(find.byValueKey(dropdownButtonKeyValue));
|
||||
await Future<void>.delayed(const Duration(milliseconds: 1500));
|
||||
|
||||
for (final String item in popupItems) {
|
||||
// There are two copies of each item, so we want to find the version
|
||||
// that is in the overlay, not the one in the dropdown.
|
||||
expect(
|
||||
await getSemantics(find.descendant(
|
||||
of: find.byType('Scrollable'),
|
||||
matching: find.byValueKey('$dropdownKeyValue.$item'),
|
||||
)),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.view,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
reason: "Dropdown $item doesn't have the right semantics the second time.");
|
||||
}
|
||||
} finally {
|
||||
await driver.tap(
|
||||
find.descendant(
|
||||
of: find.byType('Scrollable'),
|
||||
matching: find.byValueKey('$dropdownKeyValue.${popupItems.first}'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
test('Modal alert dialog has correct Android semantics', () async {
|
||||
expect(
|
||||
await getSemantics(find.byValueKey(alertButtonKeyValue)),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
await driver.tap(find.byValueKey(alertButtonKeyValue));
|
||||
try {
|
||||
await Future<void>.delayed(const Duration(milliseconds: 1500));
|
||||
|
||||
expect(
|
||||
await getSemantics(find.byValueKey('$alertKeyValue.OK')),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
reason: "Alert OK button doesn't have the right semantics");
|
||||
|
||||
for (final String item in <String>['Title', 'Body1', 'Body2']) {
|
||||
expect(
|
||||
await getSemantics(find.byValueKey('$alertKeyValue.$item')),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.view,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[],
|
||||
),
|
||||
reason: "Alert $item button doesn't have the right semantics");
|
||||
}
|
||||
|
||||
await driver.tap(find.byValueKey('$alertKeyValue.OK'));
|
||||
|
||||
// Pop up the alert again, to verify that TalkBack gets the right answer
|
||||
// more than just the first time.
|
||||
await driver.tap(find.byValueKey(alertButtonKeyValue));
|
||||
await Future<void>.delayed(const Duration(milliseconds: 1500));
|
||||
|
||||
expect(
|
||||
await getSemantics(find.byValueKey('$alertKeyValue.OK')),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.button,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
),
|
||||
reason: "Alert OK button doesn't have the right semantics");
|
||||
|
||||
for (final String item in <String>['Title', 'Body1', 'Body2']) {
|
||||
expect(
|
||||
await getSemantics(find.byValueKey('$alertKeyValue.$item')),
|
||||
hasAndroidSemantics(
|
||||
className: AndroidClassName.view,
|
||||
isChecked: false,
|
||||
isCheckable: false,
|
||||
isEnabled: true,
|
||||
isFocusable: true,
|
||||
ignoredActions: ignoredAccessibilityFocusActions,
|
||||
actions: <AndroidSemanticsAction>[],
|
||||
),
|
||||
reason: "Alert $item button doesn't have the right semantics");
|
||||
}
|
||||
} finally {
|
||||
await driver.tap(find.byValueKey('$alertKeyValue.OK'));
|
||||
}
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
tearDownAll(() async {
|
||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
||||
await driver.tap(find.byValueKey('back'));
|
||||
});
|
||||
});
|
||||
|
||||
group('Headings', () {
|
||||
setUpAll(() async {
|
||||
await driver.tap(find.text(headingsRoute));
|
||||
});
|
||||
|
||||
test('AppBar title has correct Android heading semantics', () async {
|
||||
expect(
|
||||
await getSemantics(find.byValueKey(appBarTitleKeyValue)),
|
||||
hasAndroidSemantics(isHeading: true),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
test('body text does not have Android heading semantics', () async {
|
||||
expect(
|
||||
await getSemantics(find.byValueKey(bodyTextKeyValue)),
|
||||
hasAndroidSemantics(isHeading: false),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
tearDownAll(() async {
|
||||
await driver.tap(find.byValueKey('back'));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user