Unverified Commit b815f762 authored by Darren Austin's avatar Darren Austin Committed by GitHub

Added tests for the new Android heading semantic flag to android_semantics_testing (#44031)

Added tests for the new Android heading semantic flag to android_semantics_testing.
parent bbb2a0f8
......@@ -11,6 +11,7 @@ import java.util.ArrayList;
import java.lang.StringBuilder;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.WindowManager;
......@@ -82,6 +83,10 @@ public class MainActivity extends FlutterActivity {
flags.put("isEnabled", node.isEnabled());
flags.put("isFocusable", node.isFocusable());
flags.put("isFocused", node.isFocused());
// heading flag is only available on Android Pie or newer
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
flags.put("isHeading", node.isHeading());
}
flags.put("isPassword", node.isPassword());
flags.put("isLongClickable", node.isLongClickable());
result.put("flags", flags);
......
......@@ -11,6 +11,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_driver/driver_extension.dart';
import 'src/tests/controls_page.dart';
import 'src/tests/headings_page.dart';
import 'src/tests/popup_constants.dart';
import 'src/tests/popup_page.dart';
import 'src/tests/text_field_page.dart';
......@@ -46,6 +47,7 @@ Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
selectionControlsRoute : (BuildContext context) => SelectionControlsPage(),
popupControlsRoute : (BuildContext context) => PopupControlsPage(),
textFieldRoute : (BuildContext context) => TextFieldPage(),
headingsRoute: (BuildContext context) => HeadingsPage(),
};
class TestApp extends StatelessWidget {
......
......@@ -33,6 +33,7 @@ class AndroidSemanticsNode {
/// "isEnabled": bool,
/// "isFocusable": bool,
/// "isFocused": bool,
/// "isHeading": bool,
/// "isPassword": bool,
/// "isLongClickable": bool,
/// },
......@@ -115,6 +116,9 @@ class AndroidSemanticsNode {
/// Whether the node is focused.
bool get isFocused => _flags['isFocused'];
/// Whether the node is considered a heading.
bool get isHeading => _flags['isHeading'];
/// Whether the node represents a password field.
///
/// Equivalent to [SemanticsFlag.isObscured].
......
......@@ -8,7 +8,10 @@ import 'flutter_test_alternative.dart';
/// Matches an [AndroidSemanticsNode].
///
/// Any properties which aren't supplied are ignored during the comparison.
/// Any properties which aren't supplied are ignored during the comparison,
/// with the exception of `isHeading`. The heading property is not available
/// on all versions of Android. If it is not available on the tested version,
/// it will be match whatever it is compared against.
///
/// This matcher is intended to compare the accessibility values generated by
/// the Android accessibility bridge, and not the semantics object created by
......@@ -28,6 +31,7 @@ Matcher hasAndroidSemantics({
bool isEnabled,
bool isFocusable,
bool isFocused,
bool isHeading,
bool isPassword,
bool isLongClickable,
}) {
......@@ -45,6 +49,7 @@ Matcher hasAndroidSemantics({
isEnabled: isEnabled,
isFocusable: isFocusable,
isFocused: isFocused,
isHeading: isHeading,
isPassword: isPassword,
isLongClickable: isLongClickable,
);
......@@ -65,6 +70,7 @@ class _AndroidSemanticsMatcher extends Matcher {
this.isEditable,
this.isFocusable,
this.isFocused,
this.isHeading,
this.isPassword,
this.isLongClickable,
});
......@@ -82,6 +88,7 @@ class _AndroidSemanticsMatcher extends Matcher {
final bool isEnabled;
final bool isFocusable;
final bool isFocused;
final bool isHeading;
final bool isPassword;
final bool isLongClickable;
......@@ -112,6 +119,8 @@ class _AndroidSemanticsMatcher extends Matcher {
description.add(' with flag isFocusable: $isFocusable');
if (isFocused != null)
description.add(' with flag isFocused: $isFocused');
if (isHeading != null)
description.add(' with flag isHeading: $isHeading');
if (isPassword != null)
description.add(' with flag isPassword: $isPassword');
if (isLongClickable != null)
......@@ -155,6 +164,9 @@ class _AndroidSemanticsMatcher extends Matcher {
return _failWithMessage('Expected isFocusable: $isFocusable', matchState);
if (isFocused != null && isFocused != item.isFocused)
return _failWithMessage('Expected isFocused: $isFocused', matchState);
// Heading is not available in all Android versions, so match anything if it is not set by the platform
if (isHeading != null && isHeading != item.isHeading && item.isHeading != null)
return _failWithMessage('Expected isHeading: $isHeading', matchState);
if (isPassword != null && isPassword != item.isPassword)
return _failWithMessage('Expected isPassword: $isPassword', matchState);
if (isLongClickable != null && isLongClickable != item.isLongClickable)
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// The name of the route containing the test suite.
const String headingsRoute = 'headings';
/// The string supplied to the [ValueKey] for the app bar title widget.
const String appBarTitleKeyValue = 'Headings#AppBarTitle';
/// The string supplied to the [ValueKey] for the body text widget.
const String bodyTextKeyValue = 'Headings#BodyText';
// Copyright 2019 The Chromium 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 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'headings_constants.dart';
export 'headings_constants.dart';
/// A test page with an app bar and some body text for testing heading flags.
class HeadingsPage extends StatelessWidget {
static const ValueKey<String> _appBarTitleKey = ValueKey<String>(appBarTitleKeyValue);
static const ValueKey<String> _bodyTextKey = ValueKey<String>(bodyTextKeyValue);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: const BackButton(key: ValueKey<String>('back')),
title: const Text('Heading', key: _appBarTitleKey),
),
body: const Center(
child: Text('Body text', key: _bodyTextKey),
),
);
}
}
......@@ -3,5 +3,6 @@
// found in the LICENSE file.
export 'src/tests/controls_constants.dart';
export 'src/tests/headings_constants.dart';
export 'src/tests/popup_constants.dart';
export 'src/tests/text_field_constants.dart';
......@@ -61,6 +61,8 @@ void main() {
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));
});
test('TextField has correct Android semantics', () async {
......@@ -85,6 +87,7 @@ void main() {
);
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(
......@@ -105,6 +108,7 @@ void main() {
);
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(
......@@ -148,6 +152,8 @@ void main() {
);
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),
......@@ -167,6 +173,8 @@ void main() {
);
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),
......@@ -630,5 +638,30 @@ void main() {
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),
);
});
test('body text does not have Android heading semantics', () async {
expect(
await getSemantics(find.byValueKey(bodyTextKeyValue)),
hasAndroidSemantics(isHeading: false),
);
});
tearDownAll(() async {
await driver.tap(find.byValueKey('back'));
});
});
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment