Unverified Commit 0f4ea504 authored by xster's avatar xster Committed by GitHub

Correctly chain finders to ancestor/descendant finders (#20601)

parent 28bc8180
...@@ -86,7 +86,7 @@ double getHintOpacity(WidgetTester tester) { ...@@ -86,7 +86,7 @@ double getHintOpacity(WidgetTester tester) {
find.ancestor( find.ancestor(
of: find.text('hint'), of: find.text('hint'),
matching: find.byType(FadeTransition), matching: find.byType(FadeTransition),
).last ).first
); );
return opacityWidget.opacity.value; return opacityWidget.opacity.value;
} }
......
...@@ -375,38 +375,55 @@ abstract class Finder { ...@@ -375,38 +375,55 @@ abstract class Finder {
} }
} }
class _FirstFinder extends Finder { /// Applies additional filtering against a [parent] [Finder].
_FirstFinder(this.parent); abstract class ChainedFinder extends Finder {
/// Create a Finder chained against the candidates of another [Finder].
ChainedFinder(this.parent) : assert(parent != null);
/// Another [Finder] that will run first.
final Finder parent; final Finder parent;
/// Return another [Iterable] when given an [Iterable] of candidates from a
/// parent [Finder].
///
/// This is the method to implement when subclassing [ChainedFinder].
Iterable<Element> filter(Iterable<Element> parentCandidates);
@override
Iterable<Element> apply(Iterable<Element> candidates) {
return filter(parent.apply(candidates));
}
@override
Iterable<Element> get allCandidates => parent.allCandidates;
}
class _FirstFinder extends ChainedFinder {
_FirstFinder(Finder parent) : super(parent);
@override @override
String get description => '${parent.description} (ignoring all but first)'; String get description => '${parent.description} (ignoring all but first)';
@override @override
Iterable<Element> apply(Iterable<Element> candidates) sync* { Iterable<Element> filter(Iterable<Element> parentCandidates) sync* {
yield parent.apply(candidates).first; yield parentCandidates.first;
} }
} }
class _LastFinder extends Finder { class _LastFinder extends ChainedFinder {
_LastFinder(this.parent); _LastFinder(Finder parent) : super(parent);
final Finder parent;
@override @override
String get description => '${parent.description} (ignoring all but last)'; String get description => '${parent.description} (ignoring all but last)';
@override @override
Iterable<Element> apply(Iterable<Element> candidates) sync* { Iterable<Element> filter(Iterable<Element> parentCandidates) sync* {
yield parent.apply(candidates).last; yield parentCandidates.last;
} }
} }
class _IndexFinder extends Finder { class _IndexFinder extends ChainedFinder {
_IndexFinder(this.parent, this.index); _IndexFinder(Finder parent, this.index) : super(parent);
final Finder parent;
final int index; final int index;
...@@ -414,23 +431,22 @@ class _IndexFinder extends Finder { ...@@ -414,23 +431,22 @@ class _IndexFinder extends Finder {
String get description => '${parent.description} (ignoring all but index $index)'; String get description => '${parent.description} (ignoring all but index $index)';
@override @override
Iterable<Element> apply(Iterable<Element> candidates) sync* { Iterable<Element> filter(Iterable<Element> parentCandidates) sync* {
yield parent.apply(candidates).elementAt(index); yield parentCandidates.elementAt(index);
} }
} }
class _HitTestableFinder extends Finder { class _HitTestableFinder extends ChainedFinder {
_HitTestableFinder(this.parent, this.alignment); _HitTestableFinder(Finder parent, this.alignment) : super(parent);
final Finder parent;
final Alignment alignment; final Alignment alignment;
@override @override
String get description => '${parent.description} (considering only hit-testable ones)'; String get description => '${parent.description} (considering only hit-testable ones)';
@override @override
Iterable<Element> apply(Iterable<Element> candidates) sync* { Iterable<Element> filter(Iterable<Element> parentCandidates) sync* {
for (final Element candidate in parent.apply(candidates)) { for (final Element candidate in parentCandidates) {
final RenderBox box = candidate.renderObject; final RenderBox box = candidate.renderObject;
assert(box != null); assert(box != null);
final Offset absoluteOffset = box.localToGlobal(alignment.alongSize(box.size)); final Offset absoluteOffset = box.localToGlobal(alignment.alongSize(box.size));
......
...@@ -56,6 +56,34 @@ void main() { ...@@ -56,6 +56,34 @@ void main() {
expect(tester.widget(hitTestable).key, const ValueKey<int>(0)); expect(tester.widget(hitTestable).key, const ValueKey<int>(0));
}); });
}); });
testWidgets('ChainedFinders chain properly', (WidgetTester tester) async {
final GlobalKey key1 = new GlobalKey();
await tester.pumpWidget(
_boilerplate(new Column(
children: <Widget>[
new Container(
key: key1,
child: const Text('1'),
),
new Container(
child: const Text('2'),
)
],
)),
);
// Get the text back. By correctly chaining the descendant finder's
// candidates, it should find 1 instead of 2. If the _LastFinder wasn't
// correctly chained after the descendant's candidates, the last element
// with a Text widget would have been 2.
final Text text = find.descendant(
of: find.byKey(key1),
matching: find.byType(Text),
).last.evaluate().single.widget;
expect(text.data, '1');
});
} }
Widget _boilerplate(Widget child) { Widget _boilerplate(Widget child) {
......
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