mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Fix tapping on a test in flutter run (#13397)
Also: * Remove find.byIcon since it's identical to find.icon. (I sent mail to flutter-dev about this.) * Fix IconData's operator== and hashCode, which had not been updated when we added fields. * Add the byTooltip finder to the list of suggested finders. * Make the suggested Key finder prettier.
This commit is contained in:
parent
b80751cdc3
commit
d25e0eb0d3
@ -11,7 +11,7 @@ final int choiceCount = app_bar_bottom_sample.choices.length;
|
|||||||
IconData iconAt(int index) => app_bar_bottom_sample.choices[index].icon;
|
IconData iconAt(int index) => app_bar_bottom_sample.choices[index].icon;
|
||||||
|
|
||||||
Finder findChoiceCard(IconData icon) {
|
Finder findChoiceCard(IconData icon) {
|
||||||
return find.descendant(of: find.byType(Card), matching: find.icon(icon));
|
return find.descendant(of: find.byType(Card), matching: find.byIcon(icon));
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
@ -12,11 +12,11 @@ IconData iconAt(int index) => basic_app_bar_sample.choices[index].icon;
|
|||||||
String titleAt(int index) => basic_app_bar_sample.choices[index].title;
|
String titleAt(int index) => basic_app_bar_sample.choices[index].title;
|
||||||
|
|
||||||
Finder findAppBarIcon(IconData icon) {
|
Finder findAppBarIcon(IconData icon) {
|
||||||
return find.descendant(of: find.byType(AppBar), matching: find.icon(icon));
|
return find.descendant(of: find.byType(AppBar), matching: find.byIcon(icon));
|
||||||
}
|
}
|
||||||
|
|
||||||
Finder findChoiceCard(IconData icon) {
|
Finder findChoiceCard(IconData icon) {
|
||||||
return find.descendant(of: find.byType(Card), matching: find.icon(icon));
|
return find.descendant(of: find.byType(Card), matching: find.byIcon(icon));
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
@ -11,11 +11,11 @@ final int choiceCount = tabbed_app_bar_sample.choices.length;
|
|||||||
IconData iconAt(int index) => tabbed_app_bar_sample.choices[index].icon;
|
IconData iconAt(int index) => tabbed_app_bar_sample.choices[index].icon;
|
||||||
|
|
||||||
Finder findChoiceCard(IconData icon) {
|
Finder findChoiceCard(IconData icon) {
|
||||||
return find.descendant(of: find.byType(Card), matching: find.icon(icon));
|
return find.descendant(of: find.byType(Card), matching: find.byIcon(icon));
|
||||||
}
|
}
|
||||||
|
|
||||||
Finder findTab(IconData icon) {
|
Finder findTab(IconData icon) {
|
||||||
return find.descendant(of: find.byType(Tab), matching: find.icon(icon));
|
return find.descendant(of: find.byType(Tab), matching: find.byIcon(icon));
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:ui' show hashValues;
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
/// A description of an icon fulfilled by a font glyph.
|
/// A description of an icon fulfilled by a font glyph.
|
||||||
@ -52,11 +54,14 @@ class IconData {
|
|||||||
if (runtimeType != other.runtimeType)
|
if (runtimeType != other.runtimeType)
|
||||||
return false;
|
return false;
|
||||||
final IconData typedOther = other;
|
final IconData typedOther = other;
|
||||||
return codePoint == typedOther.codePoint;
|
return codePoint == typedOther.codePoint
|
||||||
|
&& fontFamily == typedOther.fontFamily
|
||||||
|
&& fontPackage == typedOther.fontPackage
|
||||||
|
&& matchTextDirection == typedOther.matchTextDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => codePoint.hashCode;
|
int get hashCode => hashValues(codePoint, fontFamily, fontPackage, matchTextDirection);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'IconData(U+${codePoint.toRadixString(16).toUpperCase().padLeft(5, '0')})';
|
String toString() => 'IconData(U+${codePoint.toRadixString(16).toUpperCase().padLeft(5, '0')})';
|
||||||
|
@ -80,7 +80,7 @@ void main() {
|
|||||||
expect(find.text('Eclair (0)'), findsOneWidget);
|
expect(find.text('Eclair (0)'), findsOneWidget);
|
||||||
expect(find.text('Gingerbread (0)'), findsNothing);
|
expect(find.text('Gingerbread (0)'), findsNothing);
|
||||||
|
|
||||||
await tester.tap(find.icon(Icons.chevron_left));
|
await tester.tap(find.byIcon(Icons.chevron_left));
|
||||||
|
|
||||||
expect(log, <String>['page-changed: 0']);
|
expect(log, <String>['page-changed: 0']);
|
||||||
log.clear();
|
log.clear();
|
||||||
@ -91,7 +91,7 @@ void main() {
|
|||||||
expect(find.text('Eclair (0)'), findsNothing);
|
expect(find.text('Eclair (0)'), findsNothing);
|
||||||
expect(find.text('Gingerbread (0)'), findsNothing);
|
expect(find.text('Gingerbread (0)'), findsNothing);
|
||||||
|
|
||||||
await tester.tap(find.icon(Icons.chevron_left));
|
await tester.tap(find.byIcon(Icons.chevron_left));
|
||||||
|
|
||||||
expect(log, isEmpty);
|
expect(log, isEmpty);
|
||||||
|
|
||||||
@ -188,7 +188,7 @@ void main() {
|
|||||||
expect(find.text('Gingerbread (1)'), findsNothing);
|
expect(find.text('Gingerbread (1)'), findsNothing);
|
||||||
expect(find.text('Gingerbread (2)'), findsOneWidget);
|
expect(find.text('Gingerbread (2)'), findsOneWidget);
|
||||||
|
|
||||||
await tester.tap(find.icon(Icons.adjust));
|
await tester.tap(find.byIcon(Icons.adjust));
|
||||||
expect(log, <String>['action: adjust']);
|
expect(log, <String>['action: adjust']);
|
||||||
log.clear();
|
log.clear();
|
||||||
});
|
});
|
||||||
|
@ -192,4 +192,16 @@ void main() {
|
|||||||
// semanticLabel to make sure the subtree was not rebuilt.
|
// semanticLabel to make sure the subtree was not rebuilt.
|
||||||
expect(richText2, same(richText1));
|
expect(richText2, same(richText1));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('IconData comparison', (WidgetTester tester) async {
|
||||||
|
expect(const IconData(123), const IconData(123));
|
||||||
|
expect(const IconData(123), isNot(const IconData(123, matchTextDirection: true)));
|
||||||
|
expect(const IconData(123), isNot(const IconData(123, fontFamily: 'f')));
|
||||||
|
expect(const IconData(123), isNot(const IconData(123, fontPackage: 'p')));
|
||||||
|
expect(const IconData(123).hashCode, const IconData(123).hashCode);
|
||||||
|
expect(const IconData(123).hashCode, isNot(const IconData(123, matchTextDirection: true).hashCode));
|
||||||
|
expect(const IconData(123).hashCode, isNot(const IconData(123, fontFamily: 'f').hashCode));
|
||||||
|
expect(const IconData(123).hashCode, isNot(const IconData(123, fontPackage: 'p').hashCode));
|
||||||
|
expect(const IconData(123).toString(), 'IconData(U+0007B)');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -984,8 +984,9 @@ class TestViewConfiguration extends ViewConfiguration {
|
|||||||
super(size: size);
|
super(size: size);
|
||||||
|
|
||||||
static Matrix4 _getMatrix(Size size, double devicePixelRatio) {
|
static Matrix4 _getMatrix(Size size, double devicePixelRatio) {
|
||||||
final double actualWidth = ui.window.physicalSize.width;
|
final double inverseRatio = devicePixelRatio / ui.window.devicePixelRatio;
|
||||||
final double actualHeight = ui.window.physicalSize.height;
|
final double actualWidth = ui.window.physicalSize.width * inverseRatio;
|
||||||
|
final double actualHeight = ui.window.physicalSize.height * inverseRatio;
|
||||||
final double desiredWidth = size.width;
|
final double desiredWidth = size.width;
|
||||||
final double desiredHeight = size.height;
|
final double desiredHeight = size.height;
|
||||||
double scale, shiftX, shiftY;
|
double scale, shiftX, shiftY;
|
||||||
@ -1020,8 +1021,6 @@ class TestViewConfiguration extends ViewConfiguration {
|
|||||||
///
|
///
|
||||||
/// This is useful because pointers are described in logical pixels, as
|
/// This is useful because pointers are described in logical pixels, as
|
||||||
/// opposed to graphics which are expressed in physical pixels.
|
/// opposed to graphics which are expressed in physical pixels.
|
||||||
// TODO(ianh): We should make graphics and pointers use the same coordinate space.
|
|
||||||
// See: https://github.com/flutter/flutter/issues/1360
|
|
||||||
Matrix4 toHitTestMatrix() => _hitTestMatrix.clone();
|
Matrix4 toHitTestMatrix() => _hitTestMatrix.clone();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -34,17 +34,6 @@ class CommonFinders {
|
|||||||
/// nodes that are [Offstage] or that are from inactive [Route]s.
|
/// nodes that are [Offstage] or that are from inactive [Route]s.
|
||||||
Finder text(String text, { bool skipOffstage: true }) => new _TextFinder(text, skipOffstage: skipOffstage);
|
Finder text(String text, { bool skipOffstage: true }) => new _TextFinder(text, skipOffstage: skipOffstage);
|
||||||
|
|
||||||
/// Finds [Icon] widgets containing icon data equal to the `icon`
|
|
||||||
/// argument.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
///
|
|
||||||
/// expect(find.icon(Icons.chevron_left), findsOneWidget);
|
|
||||||
///
|
|
||||||
/// If the `skipOffstage` argument is true (the default), then this skips
|
|
||||||
/// nodes that are [Offstage] or that are from inactive [Route]s.
|
|
||||||
Finder icon(IconData icon, { bool skipOffstage: true }) => new _IconFinder(icon, skipOffstage: skipOffstage);
|
|
||||||
|
|
||||||
/// Looks for widgets that contain a [Text] descendant with `text`
|
/// Looks for widgets that contain a [Text] descendant with `text`
|
||||||
/// in it.
|
/// in it.
|
||||||
///
|
///
|
||||||
@ -90,7 +79,8 @@ class CommonFinders {
|
|||||||
/// nodes that are [Offstage] or that are from inactive [Route]s.
|
/// nodes that are [Offstage] or that are from inactive [Route]s.
|
||||||
Finder byType(Type type, { bool skipOffstage: true }) => new _WidgetTypeFinder(type, skipOffstage: skipOffstage);
|
Finder byType(Type type, { bool skipOffstage: true }) => new _WidgetTypeFinder(type, skipOffstage: skipOffstage);
|
||||||
|
|
||||||
/// Finds widgets by searching for widgets with a particular icon data.
|
/// Finds [Icon] widgets containing icon data equal to the `icon`
|
||||||
|
/// argument.
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
///
|
///
|
||||||
@ -421,23 +411,6 @@ class _TextFinder extends MatchFinder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _IconFinder extends MatchFinder {
|
|
||||||
_IconFinder(this.icon, { bool skipOffstage: true }) : super(skipOffstage: skipOffstage);
|
|
||||||
|
|
||||||
final IconData icon;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get description => 'icon "$icon"';
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool matches(Element candidate) {
|
|
||||||
if (candidate.widget is! Icon)
|
|
||||||
return false;
|
|
||||||
final Icon iconWidget = candidate.widget;
|
|
||||||
return iconWidget.icon == icon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _WidgetWithTextFinder extends Finder {
|
class _WidgetWithTextFinder extends Finder {
|
||||||
_WidgetWithTextFinder(this.widgetType, this.text, { bool skipOffstage: true }) : super(skipOffstage: skipOffstage);
|
_WidgetWithTextFinder(this.widgetType, this.text, { bool skipOffstage: true }) : super(skipOffstage: skipOffstage);
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
@ -306,12 +307,18 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
|
|||||||
if (event is PointerDownEvent) {
|
if (event is PointerDownEvent) {
|
||||||
final RenderObject innerTarget = result.path.firstWhere(
|
final RenderObject innerTarget = result.path.firstWhere(
|
||||||
(HitTestEntry candidate) => candidate.target is RenderObject,
|
(HitTestEntry candidate) => candidate.target is RenderObject,
|
||||||
orElse: () => null
|
).target;
|
||||||
)?.target;
|
final Element innerTargetElement = collectAllElementsFrom(
|
||||||
if (innerTarget == null)
|
binding.renderViewElement,
|
||||||
return null;
|
skipOffstage: true,
|
||||||
final Element innerTargetElement = collectAllElementsFrom(binding.renderViewElement, skipOffstage: true)
|
).lastWhere(
|
||||||
.lastWhere((Element element) => element.renderObject == innerTarget);
|
(Element element) => element.renderObject == innerTarget,
|
||||||
|
orElse: () => null,
|
||||||
|
);
|
||||||
|
if (innerTargetElement == null) {
|
||||||
|
debugPrint('No widgets found at ${binding.globalToLocal(event.position)}.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
final List<Element> candidates = <Element>[];
|
final List<Element> candidates = <Element>[];
|
||||||
innerTargetElement.visitAncestorElements((Element element) {
|
innerTargetElement.visitAncestorElements((Element element) {
|
||||||
candidates.add(element);
|
candidates.add(element);
|
||||||
@ -324,9 +331,18 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
|
|||||||
int totalNumber = 0;
|
int totalNumber = 0;
|
||||||
debugPrint('Some possible finders for the widgets at ${binding.globalToLocal(event.position)}:');
|
debugPrint('Some possible finders for the widgets at ${binding.globalToLocal(event.position)}:');
|
||||||
for (Element element in candidates) {
|
for (Element element in candidates) {
|
||||||
if (totalNumber > 10)
|
if (totalNumber > 13) // an arbitrary number of finders that feels useful without being overwhelming
|
||||||
break;
|
break;
|
||||||
totalNumber += 1;
|
totalNumber += 1; // optimistically assume we'll be able to describe it
|
||||||
|
|
||||||
|
if (element.widget is Tooltip) {
|
||||||
|
final Tooltip widget = element.widget;
|
||||||
|
final Iterable<Element> matches = find.byTooltip(widget.message).evaluate();
|
||||||
|
if (matches.length == 1) {
|
||||||
|
debugPrint(' find.byTooltip(\'${widget.message}\')');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (element.widget is Text) {
|
if (element.widget is Text) {
|
||||||
assert(descendantText == null);
|
assert(descendantText == null);
|
||||||
@ -347,7 +363,7 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
|
|||||||
key is ValueKey<bool>)) {
|
key is ValueKey<bool>)) {
|
||||||
keyLabel = 'const ${element.widget.key.runtimeType}(${key.value})';
|
keyLabel = 'const ${element.widget.key.runtimeType}(${key.value})';
|
||||||
} else if (key is ValueKey<String>) {
|
} else if (key is ValueKey<String>) {
|
||||||
keyLabel = 'const ${element.widget.key.runtimeType}(\'${key.value}\')';
|
keyLabel = 'const Key(\'${key.value}\')';
|
||||||
}
|
}
|
||||||
if (keyLabel != null) {
|
if (keyLabel != null) {
|
||||||
final Iterable<Element> matches = find.byKey(key).evaluate();
|
final Iterable<Element> matches = find.byKey(key).evaluate();
|
||||||
|
Loading…
Reference in New Issue
Block a user