Unverified Commit 87b17c8c authored by creativecreatorormaybenot's avatar creativecreatorormaybenot Committed by GitHub

Add RichText support to find.text() (#87197)

parent db3cc6e3
......@@ -23,8 +23,20 @@ const CommonFinders find = CommonFinders._();
class CommonFinders {
const CommonFinders._();
/// Finds [Text] and [EditableText] widgets containing string equal to the
/// `text` argument.
/// Finds [Text], [EditableText], and optionally [RichText] widgets
/// containing string equal to the `text` argument.
///
/// If `findRichText` is false, all standalone [RichText] widgets are
/// ignored and `text` is matched with [Text.data] or [Text.textSpan].
/// If `findRichText` is true, [RichText] widgets (and therefore also
/// [Text] and [Text.rich] widgets) are matched by comparing the
/// [InlineSpan.toPlainText] with the given `text`.
///
/// For [EditableText] widgets, the `text` is always compared to the current
/// value of the [EditableText.controller].
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
///
/// ## Sample code
///
......@@ -32,9 +44,26 @@ class CommonFinders {
/// expect(find.text('Back'), findsOneWidget);
/// ```
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder text(String text, { bool skipOffstage = true }) => _TextFinder(text, skipOffstage: skipOffstage);
/// This will match [Text], [Text.rich], and [EditableText] widgets that
/// contain the "Back" string.
///
/// ```dart
/// expect(find.text('Close', findRichText: true), findsOneWidget);
/// ```
///
/// This will match [Text], [Text.rich], [EditableText], as well as standalone
/// [RichText] widgets that contain the "Close" string.
Finder text(
String text, {
bool findRichText = false,
bool skipOffstage = true,
}) {
return _TextFinder(
text,
findRichText: findRichText,
skipOffstage: skipOffstage,
);
}
/// Finds [Text] and [EditableText] widgets which contain the given
/// `pattern` argument.
......@@ -548,26 +577,65 @@ abstract class MatchFinder extends Finder {
}
class _TextFinder extends MatchFinder {
_TextFinder(this.text, { bool skipOffstage = true }) : super(skipOffstage: skipOffstage);
_TextFinder(
this.text, {
this.findRichText = false,
bool skipOffstage = true,
}) : super(skipOffstage: skipOffstage);
final String text;
/// Whether standalone [RichText] widgets should be found or not.
///
/// Defaults to `false`.
///
/// If disabled, only [Text] widgets will be matched. [RichText] widgets
/// *without* a [Text] ancestor will be ignored.
/// If enabled, only [RichText] widgets will be matched. This *implicitly*
/// matches [Text] widgets as well since they always insert a [RichText]
/// child.
///
/// In either case, [EditableText] widgets will also be matched.
final bool findRichText;
@override
String get description => 'text "$text"';
@override
bool matches(Element candidate) {
final Widget widget = candidate.widget;
if (widget is EditableText)
return _matchesEditableText(widget);
if (!findRichText)
return _matchesNonRichText(widget);
// It would be sufficient to always use _matchesRichText if we wanted to
// match both standalone RichText widgets as well as Text widgets. However,
// the find.text() finder used to always ignore standalone RichText widgets,
// which is why we need the _matchesNonRichText method in order to not be
// backwards-compatible and not break existing tests.
return _matchesRichText(widget);
}
bool _matchesRichText(Widget widget) {
if (widget is RichText)
return widget.text.toPlainText() == text;
return false;
}
bool _matchesNonRichText(Widget widget) {
if (widget is Text) {
if (widget.data != null)
return widget.data == text;
assert(widget.textSpan != null);
return widget.textSpan!.toPlainText() == text;
} else if (widget is EditableText) {
return widget.controller.text == text;
}
return false;
}
bool _matchesEditableText(EditableText widget) {
return widget.controller.text == text;
}
}
class _TextContainingFinder extends MatchFinder {
......
......@@ -27,6 +27,84 @@ void main() {
expect(find.text('test'), findsOneWidget);
});
group('findRichText', () {
testWidgets('finds RichText widgets when enabled',
(WidgetTester tester) async {
await tester.pumpWidget(_boilerplate(RichText(
text: const TextSpan(
text: 't',
children: <TextSpan>[
TextSpan(text: 'est'),
],
),
)));
expect(find.text('test', findRichText: true), findsOneWidget);
});
testWidgets('finds Text widgets once when enabled',
(WidgetTester tester) async {
await tester.pumpWidget(_boilerplate(const Text('test2')));
expect(find.text('test2', findRichText: true), findsOneWidget);
});
testWidgets('does not find RichText widgets when disabled',
(WidgetTester tester) async {
await tester.pumpWidget(_boilerplate(RichText(
text: const TextSpan(
text: 't',
children: <TextSpan>[
TextSpan(text: 'est'),
],
),
)));
expect(find.text('test', findRichText: false), findsNothing);
});
testWidgets(
'does not find Text and RichText separated by semantics widgets twice',
(WidgetTester tester) async {
// If rich: true found both Text and RichText, this would find two widgets.
await tester.pumpWidget(_boilerplate(
const Text('test', semanticsLabel: 'foo'),
));
expect(find.text('test'), findsOneWidget);
});
testWidgets('finds Text.rich widgets when enabled',
(WidgetTester tester) async {
await tester.pumpWidget(_boilerplate(const Text.rich(
TextSpan(
text: 't',
children: <TextSpan>[
TextSpan(text: 'est'),
TextSpan(text: '3'),
],
),
)));
expect(find.text('test3', findRichText: true), findsOneWidget);
});
testWidgets('finds Text.rich widgets when disabled',
(WidgetTester tester) async {
await tester.pumpWidget(_boilerplate(const Text.rich(
TextSpan(
text: 't',
children: <TextSpan>[
TextSpan(text: 'est'),
TextSpan(text: '3'),
],
),
)));
expect(find.text('test3', findRichText: false), findsOneWidget);
});
});
});
group('textContaining', () {
......
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