Files
flutter/dev/integration_tests/android_semantics_testing/lib/src/matcher.dart
Greg Spencer 89d6c8d90b Enables setting of semantics focused and focusable attributes within Focus widgets. (#41814)
This adds a Semantics node to the Focus and FocusScope widgets, setting the focused and focusable attributes so that the accessibility subsystem can be told when a control has the input focus.

Includes an engine roll to flutter/engine@77252d2, and the following 8 engine changes:

flutter/engine@77252d2 Greg Spencer Add missing focusable testing info (flutter/engine#13013)
flutter/engine@0e42a29 skia-flutter-.. Roll src/third_party/skia 54548626a977..e27a503a0a21 (1 commits) (flutter/engine#13024)
flutter/engine@6b56ed7 gaaclarke Refactor: FlutterDartProject (flutter/engine#13006)
flutter/engine@393480c skia-flutter-.. Roll src/third_party/skia 77dde599c98a..54548626a977 (1 commits) (flutter/engine#13023)
flutter/engine@080b89d skia-flutter-.. Roll src/third_party/skia 2b1a25a4d324..77dde599c98a (1 commits) (flutter/engine#13021)
flutter/engine@90b0f30 Ben Konyi Roll src/third_party/dart f4a72bfc64..bb04f145b2 (18 commits) (flutter/engine#13020)
flutter/engine@049fb89 skia-flutter-.. Roll fuchsia/sdk/core/linux-amd64 from q_uYX... to cknsi... (flutter/engine#13019)
flutter/engine@6925b2a skia-flutter-.. Roll fuchsia/sdk/core/mac-amd64 from wuAtw... to u0JpE... (flutter/engine#13018)

Related Issues
Addresses #40101

Landing on red in order to fix the build: it's red because of the needed engine roll.
2019-10-09 14:45:42 -07:00

176 lines
6.4 KiB
Dart

// Copyright 2018 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 'common.dart';
import 'constants.dart';
import 'flutter_test_alternative.dart';
/// Matches an [AndroidSemanticsNode].
///
/// Any properties which aren't supplied are ignored during the comparison.
///
/// This matcher is intended to compare the accessibility values generated by
/// the Android accessibility bridge, and not the semantics object created by
/// the Flutter framework.
Matcher hasAndroidSemantics({
String text,
String contentDescription,
String className,
int id,
Rect rect,
Size size,
List<AndroidSemanticsAction> actions,
List<AndroidSemanticsNode> children,
bool isChecked,
bool isCheckable,
bool isEditable,
bool isEnabled,
bool isFocusable,
bool isFocused,
bool isPassword,
bool isLongClickable,
}) {
return _AndroidSemanticsMatcher(
text: text,
contentDescription: contentDescription,
className: className,
rect: rect,
size: size,
id: id,
actions: actions,
isChecked: isChecked,
isCheckable: isCheckable,
isEditable: isEditable,
isEnabled: isEnabled,
isFocusable: isFocusable,
isFocused: isFocused,
isPassword: isPassword,
isLongClickable: isLongClickable,
);
}
class _AndroidSemanticsMatcher extends Matcher {
_AndroidSemanticsMatcher({
this.text,
this.contentDescription,
this.className,
this.id,
this.actions,
this.rect,
this.size,
this.isChecked,
this.isCheckable,
this.isEnabled,
this.isEditable,
this.isFocusable,
this.isFocused,
this.isPassword,
this.isLongClickable,
});
final String text;
final String className;
final String contentDescription;
final int id;
final List<AndroidSemanticsAction> actions;
final Rect rect;
final Size size;
final bool isChecked;
final bool isCheckable;
final bool isEditable;
final bool isEnabled;
final bool isFocusable;
final bool isFocused;
final bool isPassword;
final bool isLongClickable;
@override
Description describe(Description description) {
description.add('AndroidSemanticsNode');
if (text != null)
description.add(' with text: $text');
if (contentDescription != null)
description.add( 'with contentDescription $contentDescription');
if (className != null)
description.add(' with className: $className');
if (id != null)
description.add(' with id: $id');
if (actions != null)
description.add(' with actions: $actions');
if (rect != null)
description.add(' with rect: $rect');
if (size != null)
description.add(' with size: $size');
if (isChecked != null)
description.add(' with flag isChecked: $isChecked');
if (isEditable != null)
description.add(' with flag isEditable: $isEditable');
if (isEnabled != null)
description.add(' with flag isEnabled: $isEnabled');
if (isFocusable != null)
description.add(' with flag isFocusable: $isFocusable');
if (isFocused != null)
description.add(' with flag isFocused: $isFocused');
if (isPassword != null)
description.add(' with flag isPassword: $isPassword');
if (isLongClickable != null)
description.add(' with flag isLongClickable: $isLongClickable');
return description;
}
@override
bool matches(covariant AndroidSemanticsNode item, Map<Object, Object> matchState) {
if (text != null && text != item.text)
return _failWithMessage('Expected text: $text', matchState);
if (contentDescription != null && contentDescription != item.contentDescription)
return _failWithMessage('Expected contentDescription: $contentDescription', matchState);
if (className != null && className != item.className)
return _failWithMessage('Expected className: $className', matchState);
if (id != null && id != item.id)
return _failWithMessage('Expected id: $id', matchState);
if (rect != null && rect != item.getRect())
return _failWithMessage('Expected rect: $rect', matchState);
if (size != null && size != item.getSize())
return _failWithMessage('Expected size: $size', matchState);
if (actions != null) {
final List<AndroidSemanticsAction> itemActions = item.getActions();
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<String> unexpected = itemActionsString.toSet().difference(actionsString.toSet());
final Set<String> missing = actionsString.toSet().difference(itemActionsString.toSet());
return _failWithMessage('Expected actions: $actionsString\nActual actions: $itemActionsString\nUnexpected: $unexpected\nMissing: $missing', matchState);
}
}
if (isChecked != null && isChecked != item.isChecked)
return _failWithMessage('Expected isChecked: $isChecked', matchState);
if (isCheckable != null && isCheckable != item.isCheckable)
return _failWithMessage('Expected isCheckable: $isCheckable', matchState);
if (isEditable != null && isEditable != item.isEditable)
return _failWithMessage('Expected isEditable: $isEditable', matchState);
if (isEnabled != null && isEnabled != item.isEnabled)
return _failWithMessage('Expected isEnabled: $isEnabled', matchState);
if (isFocusable != null && isFocusable != item.isFocusable)
return _failWithMessage('Expected isFocusable: $isFocusable', matchState);
if (isFocused != null && isFocused != item.isFocused)
return _failWithMessage('Expected isFocused: $isFocused', matchState);
if (isPassword != null && isPassword != item.isPassword)
return _failWithMessage('Expected isPassword: $isPassword', matchState);
if (isLongClickable != null && isLongClickable != item.isLongClickable)
return _failWithMessage('Expected longClickable: $isLongClickable', matchState);
return true;
}
@override
Description describeMismatch(Object item, Description mismatchDescription,
Map<Object, Object> matchState, bool verbose) {
return mismatchDescription.add(matchState['failure']);
}
bool _failWithMessage(String value, Map<dynamic, dynamic> matchState) {
matchState['failure'] = value;
return false;
}
}