Unverified Commit e070417a authored by Bruno Leroux's avatar Bruno Leroux Committed by GitHub

Add 'Share' button to the selection toolbar on Android (#139479)

## Description

This PR adds the 'Share' button to the text selection toolbar on Android.

## Related Issue

Fixes https://github.com/flutter/flutter/issues/138728

## Tests

Refactor a lot of existing tests in order to:
- make them more readable (avoid duplication by introducing helper functions, specify explictly check which buttons are expected).
- make them more accurate (check that expected buttons are visible instead of just checking the number of buttons).

For instance, previous tests contained sections such as:

```dart

      // Collapsed toolbar shows 3 buttons.
      expect(
        find.byType(CupertinoButton),
        isContextMenuProvidedByPlatform ? findsNothing : isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3)
      );

```

Where the comment is obsolete, the two cases (6 widgets and 3 widgets) are not explicit (which buttons are expected?), and not accurate (will pass if the number of buttons is right but the buttons are the wrong ones).
parent c6741614
...@@ -226,7 +226,7 @@ class AdaptiveTextSelectionToolbar extends StatelessWidget { ...@@ -226,7 +226,7 @@ class AdaptiveTextSelectionToolbar extends StatelessWidget {
case ContextMenuButtonType.searchWeb: case ContextMenuButtonType.searchWeb:
return localizations.searchWebButtonLabel; return localizations.searchWebButtonLabel;
case ContextMenuButtonType.share: case ContextMenuButtonType.share:
return localizations.searchWebButtonLabel; return localizations.shareButtonLabel;
case ContextMenuButtonType.liveTextInput: case ContextMenuButtonType.liveTextInput:
return localizations.scanTextButtonLabel; return localizations.scanTextButtonLabel;
case ContextMenuButtonType.custom: case ContextMenuButtonType.custom:
......
...@@ -1882,6 +1882,10 @@ class EditableText extends StatefulWidget { ...@@ -1882,6 +1882,10 @@ class EditableText extends StatefulWidget {
// If the paste button is enabled, don't render anything until the state // If the paste button is enabled, don't render anything until the state
// of the clipboard is known, since it's used to determine if paste is // of the clipboard is known, since it's used to determine if paste is
// shown. // shown.
// On Android, the share button is before the select all button.
final bool showShareBeforeSelectAll = defaultTargetPlatform == TargetPlatform.android;
resultButtonItem.addAll(<ContextMenuButtonItem>[ resultButtonItem.addAll(<ContextMenuButtonItem>[
if (onCut != null) if (onCut != null)
ContextMenuButtonItem( ContextMenuButtonItem(
...@@ -1898,6 +1902,11 @@ class EditableText extends StatefulWidget { ...@@ -1898,6 +1902,11 @@ class EditableText extends StatefulWidget {
onPressed: onPaste, onPressed: onPaste,
type: ContextMenuButtonType.paste, type: ContextMenuButtonType.paste,
), ),
if (onShare != null && showShareBeforeSelectAll)
ContextMenuButtonItem(
onPressed: onShare,
type: ContextMenuButtonType.share,
),
if (onSelectAll != null) if (onSelectAll != null)
ContextMenuButtonItem( ContextMenuButtonItem(
onPressed: onSelectAll, onPressed: onSelectAll,
...@@ -1913,7 +1922,7 @@ class EditableText extends StatefulWidget { ...@@ -1913,7 +1922,7 @@ class EditableText extends StatefulWidget {
onPressed: onSearchWeb, onPressed: onSearchWeb,
type: ContextMenuButtonType.searchWeb, type: ContextMenuButtonType.searchWeb,
), ),
if (onShare != null) if (onShare != null && !showShareBeforeSelectAll)
ContextMenuButtonItem( ContextMenuButtonItem(
onPressed: onShare, onPressed: onShare,
type: ContextMenuButtonType.share, type: ContextMenuButtonType.share,
...@@ -2300,13 +2309,18 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -2300,13 +2309,18 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
@override @override
bool get shareEnabled { bool get shareEnabled {
if (defaultTargetPlatform != TargetPlatform.iOS) { switch (defaultTargetPlatform) {
return false; case TargetPlatform.android:
case TargetPlatform.iOS:
return !widget.obscureText
&& !textEditingValue.selection.isCollapsed
&& textEditingValue.selection.textInside(textEditingValue.text).trim() != '';
case TargetPlatform.macOS:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
return false;
} }
return !widget.obscureText
&& !textEditingValue.selection.isCollapsed
&& textEditingValue.selection.textInside(textEditingValue.text).trim() != '';
} }
@override @override
...@@ -2516,9 +2530,9 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -2516,9 +2530,9 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
} }
/// Launch the share interface for the current selection, /// Launch the share interface for the current selection,
/// as in the "Share" edit menu button on iOS. /// as in the "Share..." edit menu button on iOS.
/// ///
/// Currently this is only implemented for iOS. /// Currently this is only implemented for iOS and Android.
/// ///
/// When 'obscureText' is true or the selection is empty, /// When 'obscureText' is true or the selection is empty,
/// this function will not do anything /// this function will not do anything
......
...@@ -10,6 +10,7 @@ import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; ...@@ -10,6 +10,7 @@ import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
import '../widgets/clipboard_utils.dart'; import '../widgets/clipboard_utils.dart';
import '../widgets/live_text_utils.dart'; import '../widgets/live_text_utils.dart';
import '../widgets/text_selection_toolbar_utils.dart';
void main() { void main() {
final MockClipboard mockClipboard = MockClipboard(); final MockClipboard mockClipboard = MockClipboard();
...@@ -31,13 +32,6 @@ void main() { ...@@ -31,13 +32,6 @@ void main() {
); );
}); });
Finder findOverflowNextButton() {
return find.byWidgetPredicate((Widget widget) =>
widget is CustomPaint &&
'${widget.painter?.runtimeType}' == '_RightCupertinoChevronPainter',
);
}
testWidgetsWithLeakTracking('Builds the right toolbar on each platform, including web, and shows buttonItems', (WidgetTester tester) async { testWidgetsWithLeakTracking('Builds the right toolbar on each platform, including web, and shows buttonItems', (WidgetTester tester) async {
const String buttonText = 'Click me'; const String buttonText = 'Click me';
...@@ -188,27 +182,55 @@ void main() { ...@@ -188,27 +182,55 @@ void main() {
)); ));
expect(find.byKey(key), findsOneWidget); expect(find.byKey(key), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Select All'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Look Up'), findsOneWidget);
switch (defaultTargetPlatform) { switch (defaultTargetPlatform) {
case TargetPlatform.android: case TargetPlatform.android:
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(6)); expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(6));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Select All'), findsOneWidget);
expect(find.text('Share...'), findsOneWidget);
expect(findCupertinoOverflowNextButton(), findsOneWidget);
await tapCupertinoOverflowNextButton(tester);
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(4));
expect(findCupertinoOverflowBackButton(), findsOneWidget);
expect(find.text('Look Up'), findsOneWidget);
expect(find.text('Search Web'), findsOneWidget);
expect(findLiveTextButton(), findsOneWidget);
case TargetPlatform.fuchsia: case TargetPlatform.fuchsia:
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(6));
case TargetPlatform.iOS: case TargetPlatform.iOS:
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(6)); expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(6));
expect(findOverflowNextButton(), findsOneWidget); expect(find.text('Cut'), findsOneWidget);
await tester.tapAt(tester.getCenter(findOverflowNextButton())); expect(find.text('Copy'), findsOneWidget);
await tester.pumpAndSettle(); expect(find.text('Paste'), findsOneWidget);
expect(find.text('Select All'), findsOneWidget);
expect(find.text('Look Up'), findsOneWidget);
expect(findCupertinoOverflowNextButton(), findsOneWidget);
await tapCupertinoOverflowNextButton(tester);
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(4));
expect(findCupertinoOverflowBackButton(), findsOneWidget);
expect(find.text('Search Web'), findsOneWidget);
expect(find.text('Share...'), findsOneWidget);
expect(findLiveTextButton(), findsOneWidget); expect(findLiveTextButton(), findsOneWidget);
case TargetPlatform.macOS: case TargetPlatform.macOS:
case TargetPlatform.linux: case TargetPlatform.linux:
case TargetPlatform.windows: case TargetPlatform.windows:
expect(find.byType(CupertinoDesktopTextSelectionToolbarButton), findsNWidgets(8)); expect(find.byType(CupertinoDesktopTextSelectionToolbarButton), findsNWidgets(8));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Select All'), findsOneWidget);
expect(find.text('Share...'), findsOneWidget);
expect(find.text('Look Up'), findsOneWidget);
expect(find.text('Search Web'), findsOneWidget);
expect(findLiveTextButton(), findsOneWidget);
} }
}, },
skip: kIsWeb, // [intended] on web the browser handles the context menu. skip: kIsWeb, // [intended] on web the browser handles the context menu.
......
...@@ -20,12 +20,10 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -20,12 +20,10 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
import '../widgets/clipboard_utils.dart'; import '../widgets/clipboard_utils.dart';
import '../widgets/editable_text_utils.dart' show OverflowWidgetTextEditingController; import '../widgets/editable_text_utils.dart' show OverflowWidgetTextEditingController, isContextMenuProvidedByPlatform;
import '../widgets/live_text_utils.dart'; import '../widgets/live_text_utils.dart';
import '../widgets/semantics_tester.dart'; import '../widgets/semantics_tester.dart';
import '../widgets/text_selection_toolbar_utils.dart';
// On web, the context menu (aka toolbar) is provided by the browser.
const bool isContextMenuProvidedByPlatform = isBrowser;
class MockTextSelectionControls extends TextSelectionControls { class MockTextSelectionControls extends TextSelectionControls {
@override @override
...@@ -208,7 +206,6 @@ void main() { ...@@ -208,7 +206,6 @@ void main() {
await Clipboard.setData(const ClipboardData(text: 'Clipboard data')); await Clipboard.setData(const ClipboardData(text: 'Clipboard data'));
}); });
testWidgetsWithLeakTracking( testWidgetsWithLeakTracking(
'Live Text button shows and hides correctly when LiveTextStatus changes', 'Live Text button shows and hides correctly when LiveTextStatus changes',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -353,7 +350,7 @@ void main() { ...@@ -353,7 +350,7 @@ void main() {
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu. skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
); );
testWidgetsWithLeakTracking('Share shows up on iOS only', (WidgetTester tester) async { testWidgetsWithLeakTracking('Share shows up on iOS and Android', (WidgetTester tester) async {
String? lastShare; String? lastShare;
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async { .setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async {
...@@ -378,8 +375,6 @@ void main() { ...@@ -378,8 +375,6 @@ void main() {
), ),
); );
final bool isTargetPlatformiOS = defaultTargetPlatform == TargetPlatform.iOS;
// Long press to put the cursor after the "s". // Long press to put the cursor after the "s".
const int index = 3; const int index = 3;
await tester.longPressAt(textOffsetToPosition(tester, index)); await tester.longPressAt(textOffsetToPosition(tester, index));
...@@ -392,12 +387,10 @@ void main() { ...@@ -392,12 +387,10 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 4)); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 4));
expect(find.text('Share...'), isTargetPlatformiOS? findsOneWidget : findsNothing); expect(find.text('Share...'), findsOneWidget);
if (isTargetPlatformiOS) { await tester.tap(find.text('Share...'));
await tester.tap(find.text('Share...')); expect(lastShare, 'Test');
expect(lastShare, 'Test');
}
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.android }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.android }),
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu. skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
...@@ -694,7 +687,7 @@ void main() { ...@@ -694,7 +687,7 @@ void main() {
await tester.tap(find.byKey(key2)); await tester.tap(find.byKey(key2));
await tester.enterText(find.byKey(key2), 'dcba'); await tester.enterText(find.byKey(key2), 'dcba');
await tester.pump(); await tester.pumpAndSettle();
// Focus and selection is active on first TextField, so the second TextFields // Focus and selection is active on first TextField, so the second TextFields
// selectionColor should be dropped. // selectionColor should be dropped.
...@@ -705,7 +698,7 @@ void main() { ...@@ -705,7 +698,7 @@ void main() {
expect(state1.widget.selectionColor, selectionColor); expect(state1.widget.selectionColor, selectionColor);
expect(state2.widget.selectionColor, null); expect(state2.widget.selectionColor, null);
// Focus and selection is active on second TextField, so the first TextFields // Focus and selection is active on second TextField, so the first TextField
// selectionColor should be dropped. // selectionColor should be dropped.
await tester.tap(find.byKey(key2)); await tester.tap(find.byKey(key2));
await tester.pump(); await tester.pump();
...@@ -1850,7 +1843,7 @@ void main() { ...@@ -1850,7 +1843,7 @@ void main() {
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu. skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
); );
testWidgetsWithLeakTracking('text field toolbar options correctly changes options on non-Apple Platforms', (WidgetTester tester) async { testWidgetsWithLeakTracking('text field toolbar options correctly changes options on non-Apple platforms', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
...@@ -1975,6 +1968,7 @@ void main() { ...@@ -1975,6 +1968,7 @@ void main() {
await tester.tap(find.text('Select All')); await tester.tap(find.text('Select All'));
await tester.pump(); await tester.pump();
await tester.pump(const Duration(milliseconds: 200));
await tester.tap(find.text('Cut')); await tester.tap(find.text('Cut'));
await tester.pump(); await tester.pump();
...@@ -2061,7 +2055,12 @@ void main() { ...@@ -2061,7 +2055,12 @@ void main() {
expect(controller.selection.baseOffset, isTargetPlatformIOS ? 7 : 6); expect(controller.selection.baseOffset, isTargetPlatformIOS ? 7 : 6);
// Toolbar shows on mobile. // Toolbar shows on mobile.
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : isTargetPlatformIOS ? findsNWidgets(2) : findsNothing); if (isTargetPlatformIOS) {
expectCupertinoToolbarForCollapsedSelection();
} else {
// After a tap, macOS does not show a selection toolbar for a collapsed selection.
expectNoCupertinoToolbar();
}
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgetsWithLeakTracking( testWidgetsWithLeakTracking(
...@@ -2109,7 +2108,7 @@ void main() { ...@@ -2109,7 +2108,7 @@ void main() {
// the selection has not changed we toggle the toolbar. // the selection has not changed we toggle the toolbar.
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 35); expect(controller.selection.baseOffset, 35);
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(2)); expectCupertinoToolbarForCollapsedSelection();
// Tap the 'v' position again to hide the toolbar. // Tap the 'v' position again to hide the toolbar.
await tester.tapAt(vPos); await tester.tapAt(vPos);
...@@ -2127,7 +2126,7 @@ void main() { ...@@ -2127,7 +2126,7 @@ void main() {
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 46); expect(controller.selection.baseOffset, 46);
expect(controller.selection.affinity, TextAffinity.upstream); expect(controller.selection.affinity, TextAffinity.upstream);
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(2)); expectCupertinoToolbarForCollapsedSelection();
// Tap at the same position to toggle the toolbar. // Tap at the same position to toggle the toolbar.
await tester.tapAt(endPos); await tester.tapAt(endPos);
...@@ -2135,7 +2134,7 @@ void main() { ...@@ -2135,7 +2134,7 @@ void main() {
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 46); expect(controller.selection.baseOffset, 46);
expect(controller.selection.affinity, TextAffinity.upstream); expect(controller.selection.affinity, TextAffinity.upstream);
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
// Tap at the beginning of the second line to move the cursor to the front of the first word on the // Tap at the beginning of the second line to move the cursor to the front of the first word on the
// second line, where the word wrap is. Since there is a word wrap here, and the direction of the text is LTR, // second line, where the word wrap is. Since there is a word wrap here, and the direction of the text is LTR,
...@@ -2145,7 +2144,7 @@ void main() { ...@@ -2145,7 +2144,7 @@ void main() {
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 46); expect(controller.selection.baseOffset, 46);
expect(controller.selection.affinity, TextAffinity.downstream); expect(controller.selection.affinity, TextAffinity.downstream);
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
...@@ -2194,8 +2193,7 @@ void main() { ...@@ -2194,8 +2193,7 @@ void main() {
const TextSelection(baseOffset: 24, extentOffset: 35), const TextSelection(baseOffset: 24, extentOffset: 35),
); );
// Selected text shows 5 toolbar buttons. expectCupertinoToolbarForPartialSelection();
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6));
// Tap the selected word to hide the toolbar and retain the selection. // Tap the selected word to hide the toolbar and retain the selection.
await tester.tapAt(vPos); await tester.tapAt(vPos);
...@@ -2213,13 +2211,15 @@ void main() { ...@@ -2213,13 +2211,15 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 24, extentOffset: 35), const TextSelection(baseOffset: 24, extentOffset: 35),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6));
expectCupertinoToolbarForPartialSelection();
// Tap past the selected word to move the cursor and hide the toolbar. // Tap past the selected word to move the cursor and hide the toolbar.
await tester.tapAt(ePos); await tester.tapAt(ePos);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 35); expect(controller.selection.baseOffset, 35);
expect(find.byType(CupertinoButton), findsNothing); expect(find.byType(CupertinoButton), findsNothing);
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
...@@ -2270,8 +2270,8 @@ void main() { ...@@ -2270,8 +2270,8 @@ void main() {
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
// Selected text shows 4 toolbar buttons. // The toolbar now shows up.
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectCupertinoToolbarForPartialSelection();
// Tap somewhere else to move the cursor. // Tap somewhere else to move the cursor.
await tester.tapAt(textOffsetToPosition(tester, index)); await tester.tapAt(textOffsetToPosition(tester, index));
...@@ -2323,20 +2323,7 @@ void main() { ...@@ -2323,20 +2323,7 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
if (isContextMenuProvidedByPlatform) { expectCupertinoToolbarForPartialSelection();
expect(find.byType(CupertinoButton), findsNothing);
} else {
switch (defaultTargetPlatform) {
case TargetPlatform.macOS:
expect(find.byType(CupertinoButton), findsNWidgets(3));
case TargetPlatform.iOS:
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(find.byType(CupertinoButton), findsNWidgets(6));
}
}
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -2484,7 +2471,7 @@ void main() { ...@@ -2484,7 +2471,7 @@ void main() {
// Toolbar should re-appear after a drag. // Toolbar should re-appear after a drag.
await gesture.up(); await gesture.up();
await tester.pump(); await tester.pump();
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectCupertinoToolbarForPartialSelection();
// Skip the magnifier hide animation, so it can release resources. // Skip the magnifier hide animation, so it can release resources.
await tester.pump(const Duration(milliseconds: 150)); await tester.pump(const Duration(milliseconds: 150));
...@@ -2552,8 +2539,7 @@ void main() { ...@@ -2552,8 +2539,7 @@ void main() {
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
// Selected text shows 3 toolbar buttons. expectCupertinoToolbarForPartialSelection();
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : (isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3)));
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgetsWithLeakTracking( testWidgetsWithLeakTracking(
...@@ -2587,22 +2573,7 @@ void main() { ...@@ -2587,22 +2573,7 @@ void main() {
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
final Matcher matchToolbarButtons; expectCupertinoToolbarForPartialSelection();
if (isContextMenuProvidedByPlatform) {
matchToolbarButtons = findsNothing;
} else {
switch (defaultTargetPlatform) {
case TargetPlatform.macOS:
case TargetPlatform.iOS:
matchToolbarButtons = findsNWidgets(3);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
matchToolbarButtons = findsNWidgets(4);
}
}
expect(find.byType(CupertinoButton), matchToolbarButtons);
await gesture.up(); await gesture.up();
await tester.pump(); await tester.pump();
...@@ -2612,9 +2583,8 @@ void main() { ...@@ -2612,9 +2583,8 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
expect(find.byType(CupertinoButton), matchToolbarButtons); expectCupertinoToolbarForPartialSelection();
}, }, variant: TargetPlatformVariant.all());
);
testWidgetsWithLeakTracking( testWidgetsWithLeakTracking(
'tap after a double tap select is not affected', 'tap after a double tap select is not affected',
...@@ -2890,7 +2860,7 @@ void main() { ...@@ -2890,7 +2860,7 @@ void main() {
); );
// The selection menu is not present. // The selection menu is not present.
expect(find.byType(CupertinoButton), findsNWidgets(0)); expectNoCupertinoToolbar();
await gesture.up(); await gesture.up();
await tester.pump(); await tester.pump();
...@@ -2900,7 +2870,7 @@ void main() { ...@@ -2900,7 +2870,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 35, extentOffset: 35), const TextSelection(baseOffset: 35, extentOffset: 35),
); );
expect(find.byType(CupertinoButton), findsNWidgets(0)); expectNoCupertinoToolbar();
}, },
); );
...@@ -2939,7 +2909,7 @@ void main() { ...@@ -2939,7 +2909,7 @@ void main() {
); );
// The selection menu is not present. // The selection menu is not present.
expect(find.byType(CupertinoButton), findsNWidgets(0)); expectNoCupertinoToolbar();
await gesture.up(); await gesture.up();
await tester.pump(); await tester.pump();
...@@ -2949,7 +2919,7 @@ void main() { ...@@ -2949,7 +2919,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 35), const TextSelection.collapsed(offset: 35),
); );
expect(find.byType(CupertinoButton), findsNWidgets(0)); expectNoCupertinoToolbar();
}, },
); );
...@@ -2986,7 +2956,7 @@ void main() { ...@@ -2986,7 +2956,7 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 35), const TextSelection(baseOffset: 0, extentOffset: 35),
); );
// Selected text shows paste toolbar buttons. // Selected text shows paste toolbar button.
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(1)); expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(1));
await gesture.up(); await gesture.up();
...@@ -2997,6 +2967,7 @@ void main() { ...@@ -2997,6 +2967,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 35), const TextSelection(baseOffset: 0, extentOffset: 35),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(1)); expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(1));
}, },
); );
...@@ -3074,10 +3045,9 @@ void main() { ...@@ -3074,10 +3045,9 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 7, affinity: TextAffinity.upstream), const TextSelection(baseOffset: 0, extentOffset: 7, affinity: TextAffinity.upstream),
); );
// Non-Collapsed toolbar shows 4 buttons. expectCupertinoToolbarForPartialSelection();
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4));
}, },
variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS}),
); );
testWidgetsWithLeakTracking( testWidgetsWithLeakTracking(
...@@ -3112,15 +3082,7 @@ void main() { ...@@ -3112,15 +3082,7 @@ void main() {
const TextSelection.collapsed(offset: 3, affinity: TextAffinity.upstream), const TextSelection.collapsed(offset: 3, affinity: TextAffinity.upstream),
); );
// Collapsed toolbar shows 2 buttons. expectCupertinoToolbarForCollapsedSelection();
expect(
find.byType(CupertinoButton),
isContextMenuProvidedByPlatform
? findsNothing
: defaultTargetPlatform == TargetPlatform.iOS
? findsNWidgets(2)
: findsNWidgets(1),
);
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -3151,17 +3113,14 @@ void main() { ...@@ -3151,17 +3113,14 @@ void main() {
await tester.longPressAt(ePos); await tester.longPressAt(ePos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS; expectCupertinoToolbarForCollapsedSelection();
if (kIsWeb) {
expect(find.byType(CupertinoButton), findsNothing);
} else {
expect(find.byType(CupertinoButton), findsNWidgets(isTargetPlatformIOS ? 2 : 1));
}
expect(controller.selection.isCollapsed, isTrue); expect(controller.selection.isCollapsed, isTrue);
expect(controller.selection.baseOffset, 6); expect(controller.selection.baseOffset, 6);
// Tap in a slightly different position to avoid hitting the context menu // Tap in a slightly different position to avoid hitting the context menu
// on desktop. // on desktop.
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
final Offset secondTapPos = isTargetPlatformIOS final Offset secondTapPos = isTargetPlatformIOS
? ePos ? ePos
: ePos + const Offset(-1.0, 0.0); : ePos + const Offset(-1.0, 0.0);
...@@ -3173,7 +3132,7 @@ void main() { ...@@ -3173,7 +3132,7 @@ void main() {
expect(controller.selection.baseOffset, 6); expect(controller.selection.baseOffset, 6);
// The toolbar from the long press is now dismissed by the second tap. // The toolbar from the long press is now dismissed by the second tap.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgetsWithLeakTracking( testWidgetsWithLeakTracking(
...@@ -3205,7 +3164,7 @@ void main() { ...@@ -3205,7 +3164,7 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 7, affinity: TextAffinity.upstream), const TextSelection(baseOffset: 0, extentOffset: 7, affinity: TextAffinity.upstream),
); );
// Toolbar only shows up on long press up. // Toolbar only shows up on long press up.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.moveBy(const Offset(100, 0)); await gesture.moveBy(const Offset(100, 0));
await tester.pump(); await tester.pump();
...@@ -3215,7 +3174,7 @@ void main() { ...@@ -3215,7 +3174,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 12, affinity: TextAffinity.upstream), const TextSelection(baseOffset: 0, extentOffset: 12, affinity: TextAffinity.upstream),
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.moveBy(const Offset(200, 0)); await gesture.moveBy(const Offset(200, 0));
await tester.pump(); await tester.pump();
...@@ -3225,7 +3184,7 @@ void main() { ...@@ -3225,7 +3184,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 23, affinity: TextAffinity.upstream), const TextSelection(baseOffset: 0, extentOffset: 23, affinity: TextAffinity.upstream),
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.up(); await gesture.up();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -3235,8 +3194,9 @@ void main() { ...@@ -3235,8 +3194,9 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 23, affinity: TextAffinity.upstream), const TextSelection(baseOffset: 0, extentOffset: 23, affinity: TextAffinity.upstream),
); );
// The toolbar now shows up. // The toolbar now shows up.
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectCupertinoToolbarForPartialSelection();
}, },
variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -3274,7 +3234,7 @@ void main() { ...@@ -3274,7 +3234,7 @@ void main() {
const TextSelection.collapsed(offset: 3, affinity: TextAffinity.upstream), const TextSelection.collapsed(offset: 3, affinity: TextAffinity.upstream),
); );
// Toolbar only shows up on long press up. // Toolbar only shows up on long press up.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.moveBy(const Offset(50, 0)); await gesture.moveBy(const Offset(50, 0));
await tester.pump(); await tester.pump();
...@@ -3284,7 +3244,7 @@ void main() { ...@@ -3284,7 +3244,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 6, affinity: TextAffinity.upstream), const TextSelection.collapsed(offset: 6, affinity: TextAffinity.upstream),
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.moveBy(const Offset(50, 0)); await gesture.moveBy(const Offset(50, 0));
await tester.pump(); await tester.pump();
...@@ -3294,7 +3254,7 @@ void main() { ...@@ -3294,7 +3254,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 9, affinity: TextAffinity.upstream), const TextSelection.collapsed(offset: 9, affinity: TextAffinity.upstream),
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.up(); await gesture.up();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -3305,14 +3265,7 @@ void main() { ...@@ -3305,14 +3265,7 @@ void main() {
const TextSelection.collapsed(offset: 9, affinity: TextAffinity.upstream), const TextSelection.collapsed(offset: 9, affinity: TextAffinity.upstream),
); );
// The toolbar now shows up. // The toolbar now shows up.
expect( expectCupertinoToolbarForCollapsedSelection();
find.byType(CupertinoButton),
isContextMenuProvidedByPlatform
? findsNothing
: defaultTargetPlatform == TargetPlatform.iOS
? findsNWidgets(2)
: findsNWidgets(1),
);
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -3385,8 +3338,9 @@ void main() { ...@@ -3385,8 +3338,9 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 66, affinity: TextAffinity.upstream), const TextSelection(baseOffset: 0, extentOffset: 66, affinity: TextAffinity.upstream),
); );
// The toolbar now shows up. // The toolbar now shows up.
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3)); expectCupertinoToolbarForFullSelection();
lastCharEndpoint = renderEditable.getEndpointsForSelection( lastCharEndpoint = renderEditable.getEndpointsForSelection(
const TextSelection.collapsed(offset: 66), // Last character's position. const TextSelection.collapsed(offset: 66), // Last character's position.
...@@ -3479,16 +3433,7 @@ void main() { ...@@ -3479,16 +3433,7 @@ void main() {
const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream), const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream),
); );
// The toolbar now shows up. // The toolbar now shows up.
final int toolbarButtons; expectCupertinoToolbarForCollapsedSelection();
if (isContextMenuProvidedByPlatform) {
toolbarButtons = 0;
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
toolbarButtons = 2;
} else {
// MacOS has no 'Select all' button.
toolbarButtons = 1;
}
expect(find.byType(CupertinoButton), findsNWidgets(toolbarButtons));
lastCharEndpoint = renderEditable.getEndpointsForSelection( lastCharEndpoint = renderEditable.getEndpointsForSelection(
const TextSelection.collapsed(offset: 66), // Last character's position. const TextSelection.collapsed(offset: 66), // Last character's position.
...@@ -3547,16 +3492,7 @@ void main() { ...@@ -3547,16 +3492,7 @@ void main() {
); );
// Long press toolbar. // Long press toolbar.
final int toolbarButtons; expectCupertinoToolbarForCollapsedSelection();
if (isContextMenuProvidedByPlatform) {
toolbarButtons = 0;
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
toolbarButtons = 2;
} else {
// MacOS has no 'Select all' button.
toolbarButtons = 1;
}
expect(find.byType(CupertinoButton), findsNWidgets(toolbarButtons));
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgetsWithLeakTracking( testWidgetsWithLeakTracking(
...@@ -3593,11 +3529,7 @@ void main() { ...@@ -3593,11 +3529,7 @@ void main() {
expect(controller.selection.isCollapsed, isTrue); expect(controller.selection.isCollapsed, isTrue);
expect(controller.selection.baseOffset, 3); expect(controller.selection.baseOffset, 3);
if (isContextMenuProvidedByPlatform) { expectCupertinoToolbarForCollapsedSelection();
expect(find.byType(CupertinoButton), findsNothing);
} else {
expect(find.byType(CupertinoButton), isTargetPlatformIOS ? findsNWidgets(2) : findsNWidgets(1));
}
await tester.tapAt(pPos); await tester.tapAt(pPos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -3615,8 +3547,7 @@ void main() { ...@@ -3615,8 +3547,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
// Shows toolbar. expectCupertinoToolbarForPartialSelection();
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : (isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3)));
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgetsWithLeakTracking( testWidgetsWithLeakTracking(
...@@ -3637,7 +3568,6 @@ void main() { ...@@ -3637,7 +3568,6 @@ void main() {
); );
final Offset textFieldStart = tester.getTopLeft(find.byType(CupertinoTextField)); final Offset textFieldStart = tester.getTopLeft(find.byType(CupertinoTextField));
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
await tester.tapAt(textFieldStart + const Offset(50.0, 5.0)); await tester.tapAt(textFieldStart + const Offset(50.0, 5.0));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -3651,7 +3581,7 @@ void main() { ...@@ -3651,7 +3581,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6)); expectCupertinoToolbarForPartialSelection();
// Double tap selecting the same word somewhere else is fine. // Double tap selecting the same word somewhere else is fine.
await tester.tapAt(textFieldStart + const Offset(100.0, 5.0)); await tester.tapAt(textFieldStart + const Offset(100.0, 5.0));
...@@ -3671,7 +3601,7 @@ void main() { ...@@ -3671,7 +3601,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : (isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3))); expectCupertinoToolbarForPartialSelection();
await tester.tapAt(textFieldStart + const Offset(150.0, 5.0)); await tester.tapAt(textFieldStart + const Offset(150.0, 5.0));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -3687,7 +3617,7 @@ void main() { ...@@ -3687,7 +3617,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : (isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3))); expectCupertinoToolbarForPartialSelection();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
group('Triple tap/click', () { group('Triple tap/click', () {
...@@ -3967,7 +3897,7 @@ void main() { ...@@ -3967,7 +3897,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectCupertinoToolbarForPartialSelection();
await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(50.0, 9.0));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -3981,7 +3911,8 @@ void main() { ...@@ -3981,7 +3911,8 @@ void main() {
// First tap hides the toolbar and moves the selection. // First tap hides the toolbar and moves the selection.
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 6); expect(controller.selection.baseOffset, 6);
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
// Second tap shows the toolbar and selects the word. // Second tap shows the toolbar and selects the word.
await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(100.0, 9.0));
await tester.pump(); await tester.pump();
...@@ -3989,7 +3920,7 @@ void main() { ...@@ -3989,7 +3920,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectCupertinoToolbarForPartialSelection();
// Third tap shows the toolbar and selects the paragraph. // Third tap shows the toolbar and selects the paragraph.
await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(100.0, 9.0));
...@@ -3998,7 +3929,7 @@ void main() { ...@@ -3998,7 +3929,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 35), const TextSelection(baseOffset: 0, extentOffset: 35),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3)); expectCupertinoToolbarForFullSelection();
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 9.0));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -4013,7 +3944,7 @@ void main() { ...@@ -4013,7 +3944,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectCupertinoToolbarForPartialSelection();
// Third tap selects the paragraph and shows the toolbar. // Third tap selects the paragraph and shows the toolbar.
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 9.0));
...@@ -4022,7 +3953,7 @@ void main() { ...@@ -4022,7 +3953,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 35), const TextSelection(baseOffset: 0, extentOffset: 35),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3)); expectCupertinoToolbarForFullSelection();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia }),
); );
...@@ -4048,7 +3979,6 @@ void main() { ...@@ -4048,7 +3979,6 @@ void main() {
); );
final Offset textfieldStart = tester.getTopLeft(find.byType(CupertinoTextField)); final Offset textfieldStart = tester.getTopLeft(find.byType(CupertinoTextField));
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(50.0, 9.0));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -4061,7 +3991,7 @@ void main() { ...@@ -4061,7 +3991,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6)); expectCupertinoToolbarForPartialSelection();
await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(50.0, 9.0));
await tester.pumpAndSettle(kDoubleTapTimeout); await tester.pumpAndSettle(kDoubleTapTimeout);
...@@ -4087,7 +4017,7 @@ void main() { ...@@ -4087,7 +4017,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : (isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3))); expectCupertinoToolbarForPartialSelection();
// Third tap shows the toolbar and selects the paragraph. // Third tap shows the toolbar and selects the paragraph.
await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(100.0, 9.0));
...@@ -4096,7 +4026,7 @@ void main() { ...@@ -4096,7 +4026,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 36), const TextSelection(baseOffset: 0, extentOffset: 36),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : (isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3))); expectCupertinoToolbarForPartialSelection();
await tester.tapAt(textfieldStart + const Offset(150.0, 25.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 25.0));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -4114,7 +4044,7 @@ void main() { ...@@ -4114,7 +4044,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 44, extentOffset: 50), const TextSelection(baseOffset: 44, extentOffset: 50),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : (isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3))); expectCupertinoToolbarForPartialSelection();
// Third tap selects the paragraph and shows the toolbar. // Third tap selects the paragraph and shows the toolbar.
await tester.tapAt(textfieldStart + const Offset(150.0, 25.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 25.0));
...@@ -4123,7 +4053,7 @@ void main() { ...@@ -4123,7 +4053,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 36, extentOffset: 66), const TextSelection(baseOffset: 36, extentOffset: 66),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : (isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3))); expectCupertinoToolbarForPartialSelection();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
...@@ -5114,22 +5044,7 @@ void main() { ...@@ -5114,22 +5044,7 @@ void main() {
await gesture.up(); await gesture.up();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Shows toolbar. // Shows toolbar.
final Matcher matchToolbarButtons; expectCupertinoToolbarForPartialSelection();
if (isContextMenuProvidedByPlatform) {
matchToolbarButtons = findsNothing;
} else {
switch (defaultTargetPlatform) {
case TargetPlatform.macOS:
case TargetPlatform.iOS:
matchToolbarButtons = findsNWidgets(3);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
matchToolbarButtons = findsNWidgets(4);
}
}
expect(find.byType(CupertinoButton), matchToolbarButtons);
}); });
testWidgetsWithLeakTracking('force press on unsupported devices falls back to tap', (WidgetTester tester) async { testWidgetsWithLeakTracking('force press on unsupported devices falls back to tap', (WidgetTester tester) async {
......
...@@ -11,6 +11,7 @@ import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; ...@@ -11,6 +11,7 @@ import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
import '../widgets/clipboard_utils.dart'; import '../widgets/clipboard_utils.dart';
import '../widgets/editable_text_utils.dart'; import '../widgets/editable_text_utils.dart';
import '../widgets/live_text_utils.dart'; import '../widgets/live_text_utils.dart';
import '../widgets/text_selection_toolbar_utils.dart';
void main() { void main() {
final MockClipboard mockClipboard = MockClipboard(); final MockClipboard mockClipboard = MockClipboard();
...@@ -26,13 +27,6 @@ void main() { ...@@ -26,13 +27,6 @@ void main() {
await Clipboard.setData(const ClipboardData(text: 'Clipboard data')); await Clipboard.setData(const ClipboardData(text: 'Clipboard data'));
}); });
Finder findOverflowNextButton() {
return find.byWidgetPredicate((Widget widget) =>
widget is CustomPaint &&
'${widget.painter?.runtimeType}' == '_RightCupertinoChevronPainter',
);
}
testWidgetsWithLeakTracking('Builds the right toolbar on each platform, including web, and shows buttonItems', (WidgetTester tester) async { testWidgetsWithLeakTracking('Builds the right toolbar on each platform, including web, and shows buttonItems', (WidgetTester tester) async {
const String buttonText = 'Click me'; const String buttonText = 'Click me';
...@@ -206,30 +200,74 @@ void main() { ...@@ -206,30 +200,74 @@ void main() {
); );
expect(find.byKey(key), findsOneWidget); expect(find.byKey(key), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
switch (defaultTargetPlatform) { switch (defaultTargetPlatform) {
case TargetPlatform.android: case TargetPlatform.android:
expect(find.text('Select all'), findsOneWidget);
expect(find.byType(TextSelectionToolbarTextButton), findsNWidgets(6)); expect(find.byType(TextSelectionToolbarTextButton), findsNWidgets(6));
case TargetPlatform.fuchsia: expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Share'), findsOneWidget);
expect(find.text('Select all'), findsOneWidget); expect(find.text('Select all'), findsOneWidget);
expect(find.byType(TextSelectionToolbarTextButton), findsNWidgets(8)); expect(find.text('Look Up'), findsOneWidget);
expect(findMaterialOverflowNextButton(), findsOneWidget); // Material overflow buttons are not TextSelectionToolbarTextButton.
await tapMaterialOverflowNextButton(tester);
expect(find.byType(TextSelectionToolbarTextButton), findsNWidgets(2));
expect(find.text('Search Web'), findsOneWidget);
expect(findLiveTextButton(), findsOneWidget);
expect(findMaterialOverflowBackButton(), findsOneWidget); // Material overflow buttons are not TextSelectionToolbarTextButton.
case TargetPlatform.iOS: case TargetPlatform.iOS:
expect(find.text('Select All'), findsOneWidget);
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(6)); expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(6));
await tester.tapAt(tester.getCenter(findOverflowNextButton())); expect(find.text('Cut'), findsOneWidget);
await tester.pumpAndSettle(); expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Select All'), findsOneWidget);
expect(find.text('Look Up'), findsOneWidget);
expect(findCupertinoOverflowNextButton(), findsOneWidget);
await tapCupertinoOverflowNextButton(tester);
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(4));
expect(findCupertinoOverflowBackButton(), findsOneWidget);
expect(find.text('Search Web'), findsOneWidget);
expect(find.text('Share...'), findsOneWidget);
expect(findLiveTextButton(), findsOneWidget); expect(findLiveTextButton(), findsOneWidget);
case TargetPlatform.fuchsia:
expect(find.byType(TextSelectionToolbarTextButton), findsNWidgets(8));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Select all'), findsOneWidget);
expect(find.text('Look Up'), findsOneWidget);
expect(find.text('Search Web'), findsOneWidget);
expect(find.text('Share'), findsOneWidget);
case TargetPlatform.linux: case TargetPlatform.linux:
case TargetPlatform.windows: case TargetPlatform.windows:
expect(find.text('Select all'), findsOneWidget);
expect(find.byType(DesktopTextSelectionToolbarButton), findsNWidgets(8)); expect(find.byType(DesktopTextSelectionToolbarButton), findsNWidgets(8));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Select all'), findsOneWidget);
expect(find.text('Look Up'), findsOneWidget);
expect(find.text('Search Web'), findsOneWidget);
expect(find.text('Share'), findsOneWidget);
expect(findLiveTextButton(), findsOneWidget);
case TargetPlatform.macOS: case TargetPlatform.macOS:
expect(find.text('Select All'), findsOneWidget);
expect(find.byType(CupertinoDesktopTextSelectionToolbarButton), findsNWidgets(8)); expect(find.byType(CupertinoDesktopTextSelectionToolbarButton), findsNWidgets(8));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Select All'), findsOneWidget);
expect(find.text('Look Up'), findsOneWidget);
expect(find.text('Search Web'), findsOneWidget);
expect(find.text('Share...'), findsOneWidget);
expect(findLiveTextButton(), findsOneWidget);
} }
}, },
skip: kIsWeb, // [intended] on web the browser handles the context menu. skip: kIsWeb, // [intended] on web the browser handles the context menu.
......
...@@ -29,6 +29,7 @@ import '../widgets/clipboard_utils.dart'; ...@@ -29,6 +29,7 @@ import '../widgets/clipboard_utils.dart';
import '../widgets/editable_text_utils.dart'; import '../widgets/editable_text_utils.dart';
import '../widgets/live_text_utils.dart'; import '../widgets/live_text_utils.dart';
import '../widgets/semantics_tester.dart'; import '../widgets/semantics_tester.dart';
import '../widgets/text_selection_toolbar_utils.dart';
import 'feedback_tester.dart'; import 'feedback_tester.dart';
void main() { void main() {
...@@ -278,7 +279,7 @@ void main() { ...@@ -278,7 +279,7 @@ void main() {
); );
// Initially, the menu is not shown and there is no selection. // Initially, the menu is not shown and there is no selection.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
expect(controller.selection, const TextSelection(baseOffset: -1, extentOffset: -1)); expect(controller.selection, const TextSelection(baseOffset: -1, extentOffset: -1));
final Offset midBlah1 = textOffsetToPosition(tester, 2); final Offset midBlah1 = textOffsetToPosition(tester, 2);
...@@ -303,7 +304,7 @@ void main() { ...@@ -303,7 +304,7 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.text, 'blah1 blah2'); expect(controller.text, 'blah1 blah2');
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5)); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
// Paste it at the end. // Paste it at the end.
await gesture.down(textOffsetToPosition(tester, controller.text.length)); await gesture.down(textOffsetToPosition(tester, controller.text.length));
...@@ -332,7 +333,7 @@ void main() { ...@@ -332,7 +333,7 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.text, ' blah2blah1'); expect(controller.text, ' blah2blah1');
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 0)); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 0));
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.macOS }),
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu. skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
...@@ -353,7 +354,7 @@ void main() { ...@@ -353,7 +354,7 @@ void main() {
); );
// Initially, the menu is not shown and there is no selection. // Initially, the menu is not shown and there is no selection.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
expect(controller.selection, const TextSelection(baseOffset: -1, extentOffset: -1)); expect(controller.selection, const TextSelection(baseOffset: -1, extentOffset: -1));
final Offset midBlah1 = textOffsetToPosition(tester, 2); final Offset midBlah1 = textOffsetToPosition(tester, 2);
...@@ -412,7 +413,7 @@ void main() { ...@@ -412,7 +413,7 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.text, 'blah1 blah2'); expect(controller.text, 'blah1 blah2');
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5)); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
// Paste it at the end. // Paste it at the end.
gesture = await tester.startGesture( gesture = await tester.startGesture(
...@@ -476,7 +477,7 @@ void main() { ...@@ -476,7 +477,7 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.text, ' blah2blah1'); expect(controller.text, ' blah2blah1');
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 0)); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 0));
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux, TargetPlatform.windows }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux, TargetPlatform.windows }),
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu. skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
...@@ -580,7 +581,7 @@ void main() { ...@@ -580,7 +581,7 @@ void main() {
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu. skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
); );
testWidgetsWithLeakTracking('Share shows up on iOS only', (WidgetTester tester) async { testWidgetsWithLeakTracking('Share shows up on iOS and Android', (WidgetTester tester) async {
String? lastShare; String? lastShare;
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async { .setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async {
...@@ -618,12 +619,15 @@ void main() { ...@@ -618,12 +619,15 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 4)); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 4));
expect(find.text('Share...'), isTargetPlatformiOS? findsOneWidget : findsNothing);
if (isTargetPlatformiOS) { if (isTargetPlatformiOS) {
expect(find.text('Share...'), findsOneWidget);
await tester.tap(find.text('Share...')); await tester.tap(find.text('Share...'));
expect(lastShare, 'Test'); } else {
expect(find.text('Share'), findsOneWidget);
await tester.tap(find.text('Share'));
} }
expect(lastShare, 'Test');
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.android }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.android }),
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu. skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
...@@ -9256,7 +9260,7 @@ void main() { ...@@ -9256,7 +9260,7 @@ void main() {
); );
// But don't trigger the toolbar. // But don't trigger the toolbar.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
...@@ -9325,7 +9329,7 @@ void main() { ...@@ -9325,7 +9329,7 @@ void main() {
); );
// But don't trigger the toolbar. // But don't trigger the toolbar.
expect(find.byType(TextButton), findsNothing); expectNoMaterialToolbar();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows }));
testWidgetsWithLeakTracking( testWidgetsWithLeakTracking(
...@@ -9360,8 +9364,13 @@ void main() { ...@@ -9360,8 +9364,13 @@ void main() {
expect(controller.selection.isCollapsed, isTrue); expect(controller.selection.isCollapsed, isTrue);
expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6); expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
// Toolbar shows on iOS. // Toolbar shows on mobile only.
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : isTargetPlatformMobile ? findsNWidgets(2) : findsNothing); if (isTargetPlatformMobile) {
expectCupertinoToolbarForCollapsedSelection();
} else {
// After a tap, macOS does not show a selection toolbar for a collapsed selection.
expectNoCupertinoToolbar();
}
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -9404,7 +9413,7 @@ void main() { ...@@ -9404,7 +9413,7 @@ void main() {
// at the end of the word 'Bonaventure|'. // at the end of the word 'Bonaventure|'.
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 35); expect(controller.selection.baseOffset, 35);
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await tester.tapAt(vPos); await tester.tapAt(vPos);
await tester.pumpAndSettle(const Duration(milliseconds: 500)); await tester.pumpAndSettle(const Duration(milliseconds: 500));
...@@ -9412,14 +9421,14 @@ void main() { ...@@ -9412,14 +9421,14 @@ void main() {
// the selection has not changed we toggle the toolbar. // the selection has not changed we toggle the toolbar.
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 35); expect(controller.selection.baseOffset, 35);
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(2)); expectCupertinoToolbarForCollapsedSelection();
// Tap the 'v' position again to hide the toolbar. // Tap the 'v' position again to hide the toolbar.
await tester.tapAt(vPos); await tester.tapAt(vPos);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 35); expect(controller.selection.baseOffset, 35);
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
// Long press at the end of the first line to move the cursor to the end of the first line // Long press at the end of the first line to move the cursor to the end of the first line
// where the word wrap is. Since there is a word wrap here, and the direction of the text is LTR, // where the word wrap is. Since there is a word wrap here, and the direction of the text is LTR,
...@@ -9430,7 +9439,7 @@ void main() { ...@@ -9430,7 +9439,7 @@ void main() {
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 46); expect(controller.selection.baseOffset, 46);
expect(controller.selection.affinity, TextAffinity.upstream); expect(controller.selection.affinity, TextAffinity.upstream);
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(2)); expectCupertinoToolbarForCollapsedSelection();
// Tap at the same position to toggle the toolbar. // Tap at the same position to toggle the toolbar.
await tester.tapAt(endPos); await tester.tapAt(endPos);
...@@ -9438,7 +9447,7 @@ void main() { ...@@ -9438,7 +9447,7 @@ void main() {
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 46); expect(controller.selection.baseOffset, 46);
expect(controller.selection.affinity, TextAffinity.upstream); expect(controller.selection.affinity, TextAffinity.upstream);
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
// Tap at the beginning of the second line to move the cursor to the front of the first word on the // Tap at the beginning of the second line to move the cursor to the front of the first word on the
// second line, where the word wrap is. Since there is a word wrap here, and the direction of the text is LTR, // second line, where the word wrap is. Since there is a word wrap here, and the direction of the text is LTR,
...@@ -9448,7 +9457,7 @@ void main() { ...@@ -9448,7 +9457,7 @@ void main() {
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 46); expect(controller.selection.baseOffset, 46);
expect(controller.selection.affinity, TextAffinity.downstream); expect(controller.selection.affinity, TextAffinity.downstream);
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
...@@ -9498,8 +9507,8 @@ void main() { ...@@ -9498,8 +9507,8 @@ void main() {
const TextSelection(baseOffset: 24, extentOffset: 35), const TextSelection(baseOffset: 24, extentOffset: 35),
); );
// Selected text shows 4 toolbar buttons. // The toolbar shows up.
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6)); expectCupertinoToolbarForPartialSelection();
// Tap the selected word to hide the toolbar and retain the selection. // Tap the selected word to hide the toolbar and retain the selection.
await tester.tapAt(vPos); await tester.tapAt(vPos);
...@@ -9508,7 +9517,7 @@ void main() { ...@@ -9508,7 +9517,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 24, extentOffset: 35), const TextSelection(baseOffset: 24, extentOffset: 35),
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
// Tap the selected word to show the toolbar and retain the selection. // Tap the selected word to show the toolbar and retain the selection.
await tester.tapAt(vPos); await tester.tapAt(vPos);
...@@ -9517,14 +9526,14 @@ void main() { ...@@ -9517,14 +9526,14 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 24, extentOffset: 35), const TextSelection(baseOffset: 24, extentOffset: 35),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6)); expectCupertinoToolbarForPartialSelection();
// Tap past the selected word to move the cursor and hide the toolbar. // Tap past the selected word to move the cursor and hide the toolbar.
await tester.tapAt(ePos); await tester.tapAt(ePos);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 35); expect(controller.selection.baseOffset, 35);
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
...@@ -9571,8 +9580,8 @@ void main() { ...@@ -9571,8 +9580,8 @@ void main() {
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
// Selected text shows 5 toolbar buttons. // The toolbar shows up.
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6)); expectCupertinoToolbarForPartialSelection();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
...@@ -9651,8 +9660,8 @@ void main() { ...@@ -9651,8 +9660,8 @@ void main() {
const TextSelection.collapsed(offset: 35), const TextSelection.collapsed(offset: 35),
); );
// Selected text shows nothing. // Selected text shows no toolbar.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -9698,8 +9707,8 @@ void main() { ...@@ -9698,8 +9707,8 @@ void main() {
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
// Selected text shows 4 toolbar buttons: cut, copy, paste, select all // The toolbar shows up.
expect(find.byType(TextButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectMaterialToolbarForPartialSelection();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows }),
); );
...@@ -9733,6 +9742,10 @@ void main() { ...@@ -9733,6 +9742,10 @@ void main() {
// Selected text shows 4 toolbar buttons: cut, copy, paste, select all // Selected text shows 4 toolbar buttons: cut, copy, paste, select all
expect(find.byType(TextButton), findsNWidgets(4)); expect(find.byType(TextButton), findsNWidgets(4));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Select all'), findsOneWidget);
}, },
variant: TargetPlatformVariant.all(), variant: TargetPlatformVariant.all(),
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu., skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.,
...@@ -9884,14 +9897,14 @@ void main() { ...@@ -9884,14 +9897,14 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Toolbar should be hidden during a drag. // Toolbar should be hidden during a drag.
expect(find.byType(TextButton), findsNothing); expectNoMaterialToolbar();
expect(controller.selection.baseOffset, testValue.indexOf('d')); expect(controller.selection.baseOffset, testValue.indexOf('d'));
expect(controller.selection.extentOffset, testValue.indexOf('i') + 1); expect(controller.selection.extentOffset, testValue.indexOf('i') + 1);
// Toolbar should re-appear after a drag. // Toolbar should re-appear after a drag.
await gesture.up(); await gesture.up();
await tester.pump(); await tester.pump();
expect(find.byType(TextButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectMaterialToolbarForPartialSelection();
}, },
); );
...@@ -10160,7 +10173,7 @@ void main() { ...@@ -10160,7 +10173,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(TextButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectMaterialToolbarForPartialSelection();
await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(50.0, 9.0));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -10174,7 +10187,7 @@ void main() { ...@@ -10174,7 +10187,7 @@ void main() {
// First tap hides the toolbar and moves the selection. // First tap hides the toolbar and moves the selection.
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 6); expect(controller.selection.baseOffset, 6);
expect(find.byType(TextButton), findsNothing); expectNoMaterialToolbar();
// Second tap shows the toolbar and selects the word. // Second tap shows the toolbar and selects the word.
await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(100.0, 9.0));
await tester.pump(); await tester.pump();
...@@ -10182,7 +10195,7 @@ void main() { ...@@ -10182,7 +10195,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(TextButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectMaterialToolbarForPartialSelection();
// Third tap shows the toolbar and selects the paragraph. // Third tap shows the toolbar and selects the paragraph.
await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(100.0, 9.0));
...@@ -10191,14 +10204,14 @@ void main() { ...@@ -10191,14 +10204,14 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 35), const TextSelection(baseOffset: 0, extentOffset: 35),
); );
expect(find.byType(TextButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3)); expectMaterialToolbarForFullSelection();
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 9.0));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
// First tap moved the cursor and hid the toolbar. // First tap moved the cursor and hid the toolbar.
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, 9); expect(controller.selection.baseOffset, 9);
expect(find.byType(TextButton), findsNothing); expectNoMaterialToolbar();
// Second tap selects the word. // Second tap selects the word.
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 9.0));
await tester.pump(); await tester.pump();
...@@ -10206,7 +10219,7 @@ void main() { ...@@ -10206,7 +10219,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
expect(find.byType(TextButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectMaterialToolbarForPartialSelection();
// Third tap selects the paragraph and shows the toolbar. // Third tap selects the paragraph and shows the toolbar.
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 9.0));
...@@ -10215,7 +10228,7 @@ void main() { ...@@ -10215,7 +10228,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 35), const TextSelection(baseOffset: 0, extentOffset: 35),
); );
expect(find.byType(TextButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3)); expectMaterialToolbarForFullSelection();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia }),
); );
...@@ -10252,7 +10265,7 @@ void main() { ...@@ -10252,7 +10265,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6)); expectCupertinoToolbarForPartialSelection();
await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(50.0, 9.0));
await tester.pumpAndSettle(kDoubleTapTimeout); await tester.pumpAndSettle(kDoubleTapTimeout);
...@@ -10268,7 +10281,7 @@ void main() { ...@@ -10268,7 +10281,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 36), const TextSelection(baseOffset: 0, extentOffset: 36),
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
// Second tap shows the toolbar and selects the word. // Second tap shows the toolbar and selects the word.
await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(100.0, 9.0));
await tester.pump(); await tester.pump();
...@@ -10276,7 +10289,7 @@ void main() { ...@@ -10276,7 +10289,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6)); expectCupertinoToolbarForPartialSelection();
// Third tap shows the toolbar and selects the paragraph. // Third tap shows the toolbar and selects the paragraph.
await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(100.0, 9.0));
...@@ -10285,7 +10298,7 @@ void main() { ...@@ -10285,7 +10298,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 36), const TextSelection(baseOffset: 0, extentOffset: 36),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6)); expectCupertinoToolbarForPartialSelection();
await tester.tapAt(textfieldStart + const Offset(150.0, 50.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 50.0));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -10294,7 +10307,7 @@ void main() { ...@@ -10294,7 +10307,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 50, affinity: TextAffinity.upstream), const TextSelection.collapsed(offset: 50, affinity: TextAffinity.upstream),
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
// Second tap selects the word. // Second tap selects the word.
await tester.tapAt(textfieldStart + const Offset(150.0, 50.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 50.0));
await tester.pump(); await tester.pump();
...@@ -10302,7 +10315,7 @@ void main() { ...@@ -10302,7 +10315,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 44, extentOffset: 50), const TextSelection(baseOffset: 44, extentOffset: 50),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6)); expectCupertinoToolbarForPartialSelection();
// Third tap selects the paragraph and shows the toolbar. // Third tap selects the paragraph and shows the toolbar.
await tester.tapAt(textfieldStart + const Offset(150.0, 50.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 50.0));
...@@ -10311,7 +10324,7 @@ void main() { ...@@ -10311,7 +10324,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 36, extentOffset: 66), const TextSelection(baseOffset: 36, extentOffset: 66),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6)); expectCupertinoToolbarForPartialSelection();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
...@@ -11300,8 +11313,8 @@ void main() { ...@@ -11300,8 +11313,8 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
// Selected text shows 4 toolbar buttons: cut, copy, paste, select all // The toolbar shows up.
expect(find.byType(TextButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectMaterialToolbarForPartialSelection();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows }),
); );
...@@ -11503,7 +11516,6 @@ void main() { ...@@ -11503,7 +11516,6 @@ void main() {
); );
final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); final Offset textfieldStart = tester.getTopLeft(find.byType(TextField));
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 9.0));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -11517,8 +11529,8 @@ void main() { ...@@ -11517,8 +11529,8 @@ void main() {
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
// Selected text shows 4 toolbar buttons on iOS, and 3 on macOS. // The toolbar shows up.
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3)); expectCupertinoToolbarForPartialSelection();
await gesture.up(); await gesture.up();
await tester.pump(); await tester.pump();
...@@ -11530,7 +11542,7 @@ void main() { ...@@ -11530,7 +11542,7 @@ void main() {
); );
// The toolbar is still showing. // The toolbar is still showing.
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3)); expectCupertinoToolbarForPartialSelection();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -11580,7 +11592,7 @@ void main() { ...@@ -11580,7 +11592,7 @@ void main() {
expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6); expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
// No toolbar. // No toolbar.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -11619,12 +11631,7 @@ void main() { ...@@ -11619,12 +11631,7 @@ void main() {
const TextSelection.collapsed(offset: 3), const TextSelection.collapsed(offset: 3),
); );
// Collapsed toolbar shows 2 buttons. expectCupertinoToolbarForCollapsedSelection();
final int buttons = defaultTargetPlatform == TargetPlatform.iOS ? 2 : 1;
expect(
find.byType(CupertinoButton),
isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(buttons),
);
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -11648,7 +11655,6 @@ void main() { ...@@ -11648,7 +11655,6 @@ void main() {
); );
final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); final Offset textfieldStart = tester.getTopLeft(find.byType(TextField));
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
await tester.longPressAt(textfieldStart + const Offset(50.0, 9.0)); await tester.longPressAt(textfieldStart + const Offset(50.0, 9.0));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -11659,11 +11665,8 @@ void main() { ...@@ -11659,11 +11665,8 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
// Collapsed toolbar shows 3 buttons. // The toolbar shows up.
expect( expectCupertinoToolbarForPartialSelection();
find.byType(CupertinoButton),
isContextMenuProvidedByPlatform ? findsNothing : isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3)
);
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -11696,8 +11699,8 @@ void main() { ...@@ -11696,8 +11699,8 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
// Collapsed toolbar shows 4 buttons: cut, copy, paste, select all // The toolbar shows up.
expect(find.byType(TextButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectMaterialToolbarForPartialSelection();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows }),
); );
...@@ -11743,7 +11746,7 @@ void main() { ...@@ -11743,7 +11746,7 @@ void main() {
expect(controller.selection.baseOffset, 6); expect(controller.selection.baseOffset, 6);
// The toolbar from the long press is now dismissed by the second tap. // The toolbar from the long press is now dismissed by the second tap.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -11776,7 +11779,7 @@ void main() { ...@@ -11776,7 +11779,7 @@ void main() {
const TextSelection(baseOffset: 13, extentOffset: 23), const TextSelection(baseOffset: 13, extentOffset: 23),
); );
// Cursor move doesn't trigger a toolbar initially. // Cursor move doesn't trigger a toolbar initially.
expect(find.byType(TextButton), findsNothing); expectNoMaterialToolbar();
await gesture.moveBy(const Offset(100, 0)); await gesture.moveBy(const Offset(100, 0));
await tester.pump(); await tester.pump();
...@@ -11787,7 +11790,7 @@ void main() { ...@@ -11787,7 +11790,7 @@ void main() {
const TextSelection(baseOffset: 13, extentOffset: 35), const TextSelection(baseOffset: 13, extentOffset: 35),
); );
// Still no toolbar. // Still no toolbar.
expect(find.byType(TextButton), findsNothing); expectNoMaterialToolbar();
// The selection is moved on a backwards drag. // The selection is moved on a backwards drag.
await gesture.moveBy(const Offset(-200, 0)); await gesture.moveBy(const Offset(-200, 0));
...@@ -11799,7 +11802,7 @@ void main() { ...@@ -11799,7 +11802,7 @@ void main() {
const TextSelection(baseOffset: 23, extentOffset: 8), const TextSelection(baseOffset: 23, extentOffset: 8),
); );
// Still no toolbar. // Still no toolbar.
expect(find.byType(TextButton), findsNothing); expectNoMaterialToolbar();
await gesture.moveBy(const Offset(-100, 0)); await gesture.moveBy(const Offset(-100, 0));
await tester.pump(); await tester.pump();
...@@ -11810,7 +11813,7 @@ void main() { ...@@ -11810,7 +11813,7 @@ void main() {
const TextSelection(baseOffset: 23, extentOffset: 0), const TextSelection(baseOffset: 23, extentOffset: 0),
); );
// Still no toolbar. // Still no toolbar.
expect(find.byType(TextButton), findsNothing); expectNoMaterialToolbar();
await gesture.up(); await gesture.up();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -11821,7 +11824,7 @@ void main() { ...@@ -11821,7 +11824,7 @@ void main() {
const TextSelection(baseOffset: 23, extentOffset: 0), const TextSelection(baseOffset: 23, extentOffset: 0),
); );
// The toolbar now shows up. // The toolbar now shows up.
expect(find.byType(TextButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); expectMaterialToolbarForPartialSelection();
}, },
variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -11860,7 +11863,7 @@ void main() { ...@@ -11860,7 +11863,7 @@ void main() {
const TextSelection.collapsed(offset: 3), const TextSelection.collapsed(offset: 3),
); );
// Cursor move doesn't trigger a toolbar initially. // Cursor move doesn't trigger a toolbar initially.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.moveBy(const Offset(50, 0)); await gesture.moveBy(const Offset(50, 0));
await tester.pump(); await tester.pump();
...@@ -11871,7 +11874,7 @@ void main() { ...@@ -11871,7 +11874,7 @@ void main() {
const TextSelection.collapsed(offset: 6), const TextSelection.collapsed(offset: 6),
); );
// Still no toolbar. // Still no toolbar.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.moveBy(const Offset(50, 0)); await gesture.moveBy(const Offset(50, 0));
await tester.pump(); await tester.pump();
...@@ -11882,7 +11885,7 @@ void main() { ...@@ -11882,7 +11885,7 @@ void main() {
const TextSelection.collapsed(offset: 9), const TextSelection.collapsed(offset: 9),
); );
// Still no toolbar. // Still no toolbar.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.up(); await gesture.up();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -11893,11 +11896,7 @@ void main() { ...@@ -11893,11 +11896,7 @@ void main() {
const TextSelection.collapsed(offset: 9), const TextSelection.collapsed(offset: 9),
); );
// The toolbar now shows up. // The toolbar now shows up.
final int buttons = defaultTargetPlatform == TargetPlatform.iOS ? 2 : 1; expectCupertinoToolbarForCollapsedSelection();
expect(
find.byType(CupertinoButton),
isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(buttons),
);
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -11921,7 +11920,6 @@ void main() { ...@@ -11921,7 +11920,6 @@ void main() {
); );
final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); final Offset textfieldStart = tester.getTopLeft(find.byType(TextField));
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
final TestGesture gesture = final TestGesture gesture =
await tester.startGesture(textfieldStart + const Offset(50.0, 9.0)); await tester.startGesture(textfieldStart + const Offset(50.0, 9.0));
...@@ -11933,7 +11931,7 @@ void main() { ...@@ -11933,7 +11931,7 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
// Cursor move doesn't trigger a toolbar initially. // Cursor move doesn't trigger a toolbar initially.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.moveBy(const Offset(100, 0)); await gesture.moveBy(const Offset(100, 0));
await tester.pump(); await tester.pump();
...@@ -11944,7 +11942,7 @@ void main() { ...@@ -11944,7 +11942,7 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 12), const TextSelection(baseOffset: 0, extentOffset: 12),
); );
// Still no toolbar. // Still no toolbar.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.moveBy(const Offset(100, 0)); await gesture.moveBy(const Offset(100, 0));
await tester.pump(); await tester.pump();
...@@ -11955,7 +11953,7 @@ void main() { ...@@ -11955,7 +11953,7 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 23), const TextSelection(baseOffset: 0, extentOffset: 23),
); );
// Still no toolbar. // Still no toolbar.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.up(); await gesture.up();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -11966,7 +11964,7 @@ void main() { ...@@ -11966,7 +11964,7 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 23), const TextSelection(baseOffset: 0, extentOffset: 23),
); );
// The toolbar now shows up. // The toolbar now shows up.
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3)); expectCupertinoToolbarForPartialSelection();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -11995,7 +11993,7 @@ void main() { ...@@ -11995,7 +11993,7 @@ void main() {
); );
expect(lastCharEndpoint.length, 1); expect(lastCharEndpoint.length, 1);
// Just testing the test and making sure that the last character is off // Just testing the text and making sure that the last character is off
// the right side of the screen. // the right side of the screen.
expect(lastCharEndpoint[0].point.dx, 1056); expect(lastCharEndpoint[0].point.dx, 1056);
...@@ -12009,7 +12007,7 @@ void main() { ...@@ -12009,7 +12007,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7, affinity: TextAffinity.upstream), const TextSelection(baseOffset: 0, extentOffset: 7, affinity: TextAffinity.upstream),
); );
expect(find.byType(TextButton), findsNothing); expectNoMaterialToolbar();
await gesture.moveBy(const Offset(900, 5)); await gesture.moveBy(const Offset(900, 5));
// To the edge of the screen basically. // To the edge of the screen basically.
...@@ -12031,7 +12029,7 @@ void main() { ...@@ -12031,7 +12029,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 66, affinity: TextAffinity.upstream), const TextSelection(baseOffset: 0, extentOffset: 66, affinity: TextAffinity.upstream),
); // We're at the edge now. ); // We're at the edge now.
expect(find.byType(TextButton), findsNothing); expectNoMaterialToolbar();
await gesture.up(); await gesture.up();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -12042,7 +12040,7 @@ void main() { ...@@ -12042,7 +12040,7 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 66, affinity: TextAffinity.upstream), const TextSelection(baseOffset: 0, extentOffset: 66, affinity: TextAffinity.upstream),
); );
// The toolbar now shows up. // The toolbar now shows up.
expect(find.byType(TextButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3)); expectMaterialToolbarForFullSelection();
lastCharEndpoint = renderEditable.getEndpointsForSelection( lastCharEndpoint = renderEditable.getEndpointsForSelection(
const TextSelection.collapsed(offset: 66), // Last character's position. const TextSelection.collapsed(offset: 66), // Last character's position.
...@@ -12078,7 +12076,6 @@ void main() { ...@@ -12078,7 +12076,6 @@ void main() {
); );
final RenderEditable renderEditable = findRenderEditable(tester); final RenderEditable renderEditable = findRenderEditable(tester);
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
List<TextSelectionPoint> lastCharEndpoint = renderEditable.getEndpointsForSelection( List<TextSelectionPoint> lastCharEndpoint = renderEditable.getEndpointsForSelection(
const TextSelection.collapsed(offset: 66), // Last character's position. const TextSelection.collapsed(offset: 66), // Last character's position.
...@@ -12099,7 +12096,7 @@ void main() { ...@@ -12099,7 +12096,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7, affinity: TextAffinity.upstream), const TextSelection(baseOffset: 0, extentOffset: 7, affinity: TextAffinity.upstream),
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.moveBy(const Offset(900, 5)); await gesture.moveBy(const Offset(900, 5));
// To the edge of the screen basically. // To the edge of the screen basically.
...@@ -12121,7 +12118,7 @@ void main() { ...@@ -12121,7 +12118,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 66, affinity: TextAffinity.upstream), const TextSelection(baseOffset: 0, extentOffset: 66, affinity: TextAffinity.upstream),
); // We're at the edge now. ); // We're at the edge now.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.up(); await gesture.up();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -12132,7 +12129,7 @@ void main() { ...@@ -12132,7 +12129,7 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 66, affinity: TextAffinity.upstream), const TextSelection(baseOffset: 0, extentOffset: 66, affinity: TextAffinity.upstream),
); );
// The toolbar now shows up. // The toolbar now shows up.
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3)); expectCupertinoToolbarForFullSelection();
lastCharEndpoint = renderEditable.getEndpointsForSelection( lastCharEndpoint = renderEditable.getEndpointsForSelection(
const TextSelection.collapsed(offset: 66), // Last character's position. const TextSelection.collapsed(offset: 66), // Last character's position.
...@@ -12192,7 +12189,7 @@ void main() { ...@@ -12192,7 +12189,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 19, affinity: TextAffinity.upstream), const TextSelection.collapsed(offset: 19, affinity: TextAffinity.upstream),
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.moveBy(const Offset(600, 0)); await gesture.moveBy(const Offset(600, 0));
// To the edge of the screen basically. // To the edge of the screen basically.
...@@ -12214,7 +12211,7 @@ void main() { ...@@ -12214,7 +12211,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream), const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream),
); // We're at the edge now. ); // We're at the edge now.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.up(); await gesture.up();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -12225,11 +12222,7 @@ void main() { ...@@ -12225,11 +12222,7 @@ void main() {
const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream), const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream),
); );
// The toolbar now shows up. // The toolbar now shows up.
final int buttons = defaultTargetPlatform == TargetPlatform.iOS ? 2 : 1; expectCupertinoToolbarForCollapsedSelection();
expect(
find.byType(CupertinoButton),
isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(buttons),
);
lastCharEndpoint = renderEditable.getEndpointsForSelection( lastCharEndpoint = renderEditable.getEndpointsForSelection(
const TextSelection.collapsed(offset: 66), // Last character's position. const TextSelection.collapsed(offset: 66), // Last character's position.
...@@ -12295,7 +12288,7 @@ void main() { ...@@ -12295,7 +12288,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 19, extentOffset: 66), const TextSelection(baseOffset: 19, extentOffset: 66),
); // We're at the edge now. ); // We're at the edge now.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.up(); await gesture.up();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -12667,12 +12660,8 @@ void main() { ...@@ -12667,12 +12660,8 @@ void main() {
const TextSelection.collapsed(offset: 6), const TextSelection.collapsed(offset: 6),
); );
// Long press toolbar. // The toolbar shows up.
final int buttons = defaultTargetPlatform == TargetPlatform.iOS ? 2 : 1; expectCupertinoToolbarForCollapsedSelection();
expect(
find.byType(CupertinoButton),
isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(buttons),
);
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -12730,7 +12719,7 @@ void main() { ...@@ -12730,7 +12719,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : isTargetPlatformMobile ? findsNWidgets(6) : findsNWidgets(3)); expectCupertinoToolbarForPartialSelection();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -12789,7 +12778,7 @@ void main() { ...@@ -12789,7 +12778,7 @@ void main() {
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
// The text selection toolbar isn't shown on Mac without a right click. // The text selection toolbar isn't shown on Mac without a right click.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
}, },
variant: TargetPlatformVariant.desktop(), variant: TargetPlatformVariant.desktop(),
); );
...@@ -12814,7 +12803,6 @@ void main() { ...@@ -12814,7 +12803,6 @@ void main() {
); );
final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); final Offset textfieldStart = tester.getTopLeft(find.byType(TextField));
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(50.0, 9.0));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -12828,7 +12816,7 @@ void main() { ...@@ -12828,7 +12816,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6)); expectCupertinoToolbarForPartialSelection();
// Double tap selecting the same word somewhere else is fine. // Double tap selecting the same word somewhere else is fine.
await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(100.0, 9.0));
...@@ -12838,7 +12826,8 @@ void main() { ...@@ -12838,7 +12826,8 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
// Second tap shows the toolbar and retains the selection. // Second tap shows the toolbar and retains the selection.
await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(100.0, 9.0));
// Wait for the consecutive tap timer to timeout so the next // Wait for the consecutive tap timer to timeout so the next
...@@ -12848,7 +12837,7 @@ void main() { ...@@ -12848,7 +12837,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3)); expectCupertinoToolbarForPartialSelection();
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 9.0));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -12857,14 +12846,14 @@ void main() { ...@@ -12857,14 +12846,14 @@ void main() {
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 12, affinity: TextAffinity.upstream) const TextSelection.collapsed(offset: 12, affinity: TextAffinity.upstream)
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 9.0));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect( expect(
controller.selection, controller.selection,
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3)); expectCupertinoToolbarForPartialSelection();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
...@@ -12915,7 +12904,7 @@ void main() { ...@@ -12915,7 +12904,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
// Double tap selecting the same word somewhere else is fine. // Double tap selecting the same word somewhere else is fine.
await gesture.down(textFieldStart + const Offset(100.0, 9.0)); await gesture.down(textFieldStart + const Offset(100.0, 9.0));
...@@ -12937,7 +12926,7 @@ void main() { ...@@ -12937,7 +12926,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
await gesture.down(textFieldStart + const Offset(150.0, 9.0)); await gesture.down(textFieldStart + const Offset(150.0, 9.0));
await tester.pump(); await tester.pump();
...@@ -12956,7 +12945,7 @@ void main() { ...@@ -12956,7 +12945,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.macOS, TargetPlatform.windows, TargetPlatform.linux }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.macOS, TargetPlatform.windows, TargetPlatform.linux }),
); );
...@@ -13199,7 +13188,7 @@ void main() { ...@@ -13199,7 +13188,7 @@ void main() {
// We don't want this gesture to select any word on Android. // We don't want this gesture to select any word on Android.
expect(controller.selection, const TextSelection.collapsed(offset: -1)); expect(controller.selection, const TextSelection.collapsed(offset: -1));
expect(find.byType(TextButton), findsNothing); expectNoMaterialToolbar();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia }));
testWidgetsWithLeakTracking('Force press sets selection on desktop platforms that do not support it', (WidgetTester tester) async { testWidgetsWithLeakTracking('Force press sets selection on desktop platforms that do not support it', (WidgetTester tester) async {
...@@ -13242,7 +13231,7 @@ void main() { ...@@ -13242,7 +13231,7 @@ void main() {
// We don't want this gesture to select any word on Android. // We don't want this gesture to select any word on Android.
expect(controller.selection, const TextSelection.collapsed(offset: 9)); expect(controller.selection, const TextSelection.collapsed(offset: 9));
expect(find.byType(TextButton), findsNothing); expectNoMaterialToolbar();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux, TargetPlatform.windows })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux, TargetPlatform.windows }));
testWidgetsWithLeakTracking('force press selects word', (WidgetTester tester) async { testWidgetsWithLeakTracking('force press selects word', (WidgetTester tester) async {
...@@ -13291,7 +13280,7 @@ void main() { ...@@ -13291,7 +13280,7 @@ void main() {
await gesture.up(); await gesture.up();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(6)); expectCupertinoToolbarForPartialSelection();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
testWidgetsWithLeakTracking('tap on non-force-press-supported devices work', (WidgetTester tester) async { testWidgetsWithLeakTracking('tap on non-force-press-supported devices work', (WidgetTester tester) async {
...@@ -13344,7 +13333,7 @@ void main() { ...@@ -13344,7 +13333,7 @@ void main() {
await tester.pump(); await tester.pump();
// Single taps shouldn't trigger the toolbar. // Single taps shouldn't trigger the toolbar.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
// TODO(gspencergoog): Add in TargetPlatform.macOS in the line below when we figure out what global state is leaking. // TODO(gspencergoog): Add in TargetPlatform.macOS in the line below when we figure out what global state is leaking.
// https://github.com/flutter/flutter/issues/43445 // https://github.com/flutter/flutter/issues/43445
...@@ -15900,7 +15889,7 @@ void main() { ...@@ -15900,7 +15889,7 @@ void main() {
); );
// Initially, the menu is not shown and there is no selection. // Initially, the menu is not shown and there is no selection.
expect(find.byType(CupertinoButton), findsNothing); expectNoCupertinoToolbar();
expect(controller.selection, const TextSelection(baseOffset: -1, extentOffset: -1)); expect(controller.selection, const TextSelection(baseOffset: -1, extentOffset: -1));
final Offset midBlah1 = textOffsetToPosition(tester, 2); final Offset midBlah1 = textOffsetToPosition(tester, 2);
......
...@@ -407,8 +407,9 @@ void main() { ...@@ -407,8 +407,9 @@ void main() {
expect(find.text('Cut'), findsNothing); expect(find.text('Cut'), findsNothing);
expect(find.text('Copy'), findsNothing); expect(find.text('Copy'), findsNothing);
expect(find.text('Paste'), findsNothing); expect(find.text('Paste'), findsNothing);
expect(find.text('Share'), findsNothing);
expect(find.text('Select all'), findsNothing); expect(find.text('Select all'), findsNothing);
expect(find.byType(IconButton), findsNothing); expect(find.byType(IconButton), findsNothing); // 'More' button.
// Tap to place the cursor and tap again to show the menu without a // Tap to place the cursor and tap again to show the menu without a
// selection. // selection.
...@@ -426,6 +427,7 @@ void main() { ...@@ -426,6 +427,7 @@ void main() {
expect(find.text('Cut'), findsNothing); expect(find.text('Cut'), findsNothing);
expect(find.text('Copy'), findsNothing); expect(find.text('Copy'), findsNothing);
expect(find.text('Paste'), findsOneWidget); expect(find.text('Paste'), findsOneWidget);
expect(find.text('Share'), findsNothing);
expect(find.text('Select all'), findsOneWidget); expect(find.text('Select all'), findsOneWidget);
expect(find.byType(IconButton), findsNothing); expect(find.byType(IconButton), findsNothing);
...@@ -436,8 +438,9 @@ void main() { ...@@ -436,8 +438,9 @@ void main() {
expect(find.text('Cut'), findsOneWidget); expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget); expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget); expect(find.text('Paste'), findsOneWidget);
expect(find.text('Share'), findsNothing);
expect(find.text('Select all'), findsNothing); expect(find.text('Select all'), findsNothing);
expect(find.byType(IconButton), findsNothing); expect(find.byType(IconButton), findsOneWidget); // 'More' button.
final Offset cutOffset = tester.getTopLeft(find.text('Cut')); final Offset cutOffset = tester.getTopLeft(find.text('Cut'));
// Tap to clear the selection. // Tap to clear the selection.
...@@ -447,37 +450,39 @@ void main() { ...@@ -447,37 +450,39 @@ void main() {
expect(find.text('Copy'), findsNothing); expect(find.text('Copy'), findsNothing);
expect(find.text('Paste'), findsNothing); expect(find.text('Paste'), findsNothing);
expect(find.text('Select all'), findsNothing); expect(find.text('Select all'), findsNothing);
expect(find.byType(IconButton), findsNothing); expect(find.byType(IconButton), findsNothing); // 'More' button.
// Long press to show the menu. // Long press to show the menu.
await tester.longPressAt(textOffsetToPosition(tester, 1)); await tester.longPressAt(textOffsetToPosition(tester, 1));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// The last button is missing, and a more button is shown. // The last buttons (share and select all) are missing, and a more button is shown.
expect(find.text('Cut'), findsOneWidget); expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget); expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget); expect(find.text('Paste'), findsOneWidget);
expect(find.text('Share'), findsNothing);
expect(find.text('Select all'), findsNothing); expect(find.text('Select all'), findsNothing);
expect(find.byType(IconButton), findsOneWidget); expect(find.byType(IconButton), findsOneWidget); // 'More' button.
// Tapping the button shows the overflow menu. // Tapping the more button shows the overflow menu.
await tester.tap(find.byType(IconButton)); await tester.tap(find.byType(IconButton));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.text('Cut'), findsNothing); expect(find.text('Cut'), findsNothing);
expect(find.text('Copy'), findsNothing); expect(find.text('Copy'), findsNothing);
expect(find.text('Paste'), findsNothing); expect(find.text('Paste'), findsNothing);
expect(find.text('Share'), findsOneWidget);
expect(find.text('Select all'), findsOneWidget); expect(find.text('Select all'), findsOneWidget);
expect(find.byType(IconButton), findsOneWidget); expect(find.byType(IconButton), findsOneWidget); // Back button.
// Tapping Select all changes the menu items so that there is no longer // Tapping 'Select all' closes the overflow menu.
// any overflow.
await tester.tap(find.text('Select all')); await tester.tap(find.text('Select all'));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.text('Cut'), findsOneWidget); expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget); expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget); expect(find.text('Paste'), findsOneWidget);
expect(find.text('Share'), findsNothing);
expect(find.text('Select all'), findsNothing); expect(find.text('Select all'), findsNothing);
expect(find.byType(IconButton), findsNothing); expect(find.byType(IconButton), findsOneWidget); // 'More' button.
final Offset newCutOffset = tester.getTopLeft(find.text('Cut')); final Offset newCutOffset = tester.getTopLeft(find.text('Cut'));
expect(newCutOffset, equals(cutOffset)); expect(newCutOffset, equals(cutOffset));
}, },
......
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
// 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 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
/// A mock class to control the return result of Live Text input functions. /// A mock class to control the return result of Live Text input functions.
...@@ -34,7 +35,23 @@ class LiveTextInputTester { ...@@ -34,7 +35,23 @@ class LiveTextInputTester {
} }
/// A function to find the live text button. /// A function to find the live text button.
Finder findLiveTextButton() => find.byWidgetPredicate((Widget widget) => ///
widget is CustomPaint && /// LiveText button is displayed either using a custom painter,
'${widget.painter?.runtimeType}' == '_LiveTextIconPainter', /// a Text with an empty label, or a Text with the 'Scan text' label.
); Finder findLiveTextButton() {
final bool isMobile = defaultTargetPlatform == TargetPlatform.android ||
defaultTargetPlatform == TargetPlatform.fuchsia ||
defaultTargetPlatform == TargetPlatform.iOS;
if (isMobile) {
return find.byWidgetPredicate((Widget widget) {
return (widget is CustomPaint && '${widget.painter?.runtimeType}' == '_LiveTextIconPainter')
|| (widget is Text && widget.data == 'Scan text'); // Android and Fuchsia when inside a MaterialApp.
});
}
if (defaultTargetPlatform == TargetPlatform.macOS) {
return find.ancestor(of: find.text(''), matching: find.byType(CupertinoDesktopTextSelectionToolbarButton));
}
return find.byWidgetPredicate((Widget widget) {
return widget is Text && (widget.data == '' || widget.data == 'Scan text');
});
}
...@@ -108,7 +108,6 @@ Widget boilerplate({ Widget? child }) { ...@@ -108,7 +108,6 @@ Widget boilerplate({ Widget? child }) {
); );
} }
Future<void> skipPastScrollingAnimation(WidgetTester tester) async { Future<void> skipPastScrollingAnimation(WidgetTester tester) async {
await tester.pump(); await tester.pump();
await tester.pump(const Duration(milliseconds: 200)); await tester.pump(const Duration(milliseconds: 200));
...@@ -144,6 +143,41 @@ void main() { ...@@ -144,6 +143,41 @@ void main() {
return renderEditable; return renderEditable;
} }
// Check that the Cupertino text selection toolbar is the expected one on iOS and macOS.
// TODO(bleroux): try to merge this into text_selection_toolbar_utils.dart
// (for instance by adding a 'readOnly' flag).
void expectCupertinoSelectionToolbar() {
// This function is valid only for tests running on Apple platforms.
expect(defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.macOS, isTrue);
if (defaultTargetPlatform == TargetPlatform.iOS) {
expect(find.byType(CupertinoButton), findsNWidgets(4));
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Look Up'), findsOneWidget);
expect(find.text('Search Web'), findsOneWidget);
expect(find.text('Share...'), findsOneWidget);
} else {
expect(find.byType(CupertinoButton), findsNWidgets(1));
expect(find.text('Copy'), findsOneWidget);
}
}
// Check that the Material text selection toolbar is the expected one.
// TODO(bleroux): Try to merge this into text_selection_toolbar_utils.dart
// (for instance by adding a 'readOnly' flag).
void expectMaterialSelectionToolbar() {
if (defaultTargetPlatform == TargetPlatform.android) {
expect(find.byType(TextButton), findsNWidgets(3));
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Share'), findsOneWidget);
expect(find.text('Select all'), findsOneWidget);
} else {
expect(find.byType(TextButton), findsNWidgets(2));
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Select all'), findsOneWidget);
}
}
List<TextSelectionPoint> globalize(Iterable<TextSelectionPoint> points, RenderBox box) { List<TextSelectionPoint> globalize(Iterable<TextSelectionPoint> points, RenderBox box) {
return points.map<TextSelectionPoint>((TextSelectionPoint point) { return points.map<TextSelectionPoint>((TextSelectionPoint point) {
return TextSelectionPoint( return TextSelectionPoint(
...@@ -272,14 +306,14 @@ void main() { ...@@ -272,14 +306,14 @@ void main() {
await gesture.moveTo(newHandlePos); await gesture.moveTo(newHandlePos);
await tester.pump(); await tester.pump();
// Unmount the SelectableText during handle drag // Unmount the SelectableText during handle drag.
setter(() { setter(() {
isShow = false; isShow = false;
}); });
await tester.pump(); await tester.pump();
await gesture.moveTo(newHandlePos1); await gesture.moveTo(newHandlePos1);
await tester.pump(); // Do not crash here await tester.pump(); // Do not crash here.
await gesture.up(); await gesture.up();
await tester.pump(); await tester.pump();
...@@ -288,7 +322,7 @@ void main() { ...@@ -288,7 +322,7 @@ void main() {
testWidgetsWithLeakTracking('has expected defaults', (WidgetTester tester) async { testWidgetsWithLeakTracking('has expected defaults', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
boilerplate( boilerplate(
child: const SelectableText('selectable text'), child: const SelectableText('selectable text'),
), ),
); );
...@@ -308,29 +342,29 @@ void main() { ...@@ -308,29 +342,29 @@ void main() {
child: Directionality( child: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: SelectableText.rich( child: SelectableText.rich(
TextSpan( TextSpan(
text: 'First line!', text: 'First line!',
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
fontFamily: 'Roboto',
),
children: <TextSpan>[
TextSpan(
text: 'Second line!\n',
style: TextStyle(
fontSize: 30,
fontFamily: 'Roboto', fontFamily: 'Roboto',
),
children: <TextSpan>[
TextSpan(
text: 'Second line!\n',
style: TextStyle(
fontSize: 30,
fontFamily: 'Roboto',
),
), ),
TextSpan( ),
text: 'Third line!\n', TextSpan(
style: TextStyle( text: 'Third line!\n',
fontSize: 14, style: TextStyle(
fontFamily: 'Roboto', fontSize: 14,
), fontFamily: 'Roboto',
), ),
], ),
), ],
),
), ),
), ),
), ),
...@@ -353,33 +387,33 @@ void main() { ...@@ -353,33 +387,33 @@ void main() {
child: Directionality( child: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: SelectableText.rich( child: SelectableText.rich(
TextSpan( TextSpan(
text: 'First line!', text: 'First line!',
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
fontFamily: 'Roboto', fontFamily: 'Roboto',
), ),
children: <InlineSpan>[ children: <InlineSpan>[
WidgetSpan( WidgetSpan(
child: SizedBox( child: SizedBox(
width: 120, width: 120,
height: 50, height: 50,
child: Card( child: Card(
child: Center( child: Center(
child: Text('Hello World!'), child: Text('Hello World!'),
),
),
), ),
),
TextSpan(
text: 'Third line!\n',
style: TextStyle(
fontSize: 14,
fontFamily: 'Roboto',
), ),
), ),
], ),
), TextSpan(
text: 'Third line!\n',
style: TextStyle(
fontSize: 14,
fontFamily: 'Roboto',
),
),
],
),
), ),
), ),
), ),
...@@ -389,9 +423,9 @@ void main() { ...@@ -389,9 +423,9 @@ void main() {
testWidgetsWithLeakTracking('no text keyboard when widget is focused', (WidgetTester tester) async { testWidgetsWithLeakTracking('no text keyboard when widget is focused', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
child: const SelectableText('selectable text'), child: const SelectableText('selectable text'),
), ),
); );
await tester.tap(find.byType(SelectableText)); await tester.tap(find.byType(SelectableText));
await tester.idle(); await tester.idle();
...@@ -421,9 +455,9 @@ void main() { ...@@ -421,9 +455,9 @@ void main() {
testWidgetsWithLeakTracking('Selectable Text has adaptive size', (WidgetTester tester) async { testWidgetsWithLeakTracking('Selectable Text has adaptive size', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
boilerplate( boilerplate(
child: const SelectableText('s'), child: const SelectableText('s'),
), ),
); );
RenderBox findSelectableTextBox() => tester.renderObject(find.byType(SelectableText)); RenderBox findSelectableTextBox() => tester.renderObject(find.byType(SelectableText));
...@@ -432,9 +466,9 @@ void main() { ...@@ -432,9 +466,9 @@ void main() {
expect(textBox.size, const Size(17.0, 14.0)); expect(textBox.size, const Size(17.0, 14.0));
await tester.pumpWidget( await tester.pumpWidget(
boilerplate( boilerplate(
child: const SelectableText('very very long'), child: const SelectableText('very very long'),
), ),
); );
final RenderBox longtextBox = findSelectableTextBox(); final RenderBox longtextBox = findSelectableTextBox();
...@@ -575,9 +609,9 @@ void main() { ...@@ -575,9 +609,9 @@ void main() {
testWidgetsWithLeakTracking('Caret position is updated on tap', (WidgetTester tester) async { testWidgetsWithLeakTracking('Caret position is updated on tap', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
child: const SelectableText('abc def ghi'), child: const SelectableText('abc def ghi'),
), ),
); );
final EditableText editableText = tester.widget(find.byType(EditableText)); final EditableText editableText = tester.widget(find.byType(EditableText));
expect(editableText.controller.selection.baseOffset, -1); expect(editableText.controller.selection.baseOffset, -1);
...@@ -595,12 +629,12 @@ void main() { ...@@ -595,12 +629,12 @@ void main() {
testWidgetsWithLeakTracking('enableInteractiveSelection = false, tap', (WidgetTester tester) async { testWidgetsWithLeakTracking('enableInteractiveSelection = false, tap', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
child: const SelectableText( child: const SelectableText(
'abc def ghi', 'abc def ghi',
enableInteractiveSelection: false, enableInteractiveSelection: false,
),
), ),
),
); );
final EditableText editableText = tester.widget(find.byType(EditableText)); final EditableText editableText = tester.widget(find.byType(EditableText));
expect(editableText.controller.selection.baseOffset, -1); expect(editableText.controller.selection.baseOffset, -1);
...@@ -618,12 +652,12 @@ void main() { ...@@ -618,12 +652,12 @@ void main() {
testWidgetsWithLeakTracking('enableInteractiveSelection = false, long-press', (WidgetTester tester) async { testWidgetsWithLeakTracking('enableInteractiveSelection = false, long-press', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
child: const SelectableText( child: const SelectableText(
'abc def ghi', 'abc def ghi',
enableInteractiveSelection: false, enableInteractiveSelection: false,
),
), ),
),
); );
final EditableText editableText = tester.widget(find.byType(EditableText)); final EditableText editableText = tester.widget(find.byType(EditableText));
expect(editableText.controller.selection.baseOffset, -1); expect(editableText.controller.selection.baseOffset, -1);
...@@ -643,9 +677,9 @@ void main() { ...@@ -643,9 +677,9 @@ void main() {
testWidgetsWithLeakTracking('Can long press to select', (WidgetTester tester) async { testWidgetsWithLeakTracking('Can long press to select', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
child: const SelectableText('abc def ghi'), child: const SelectableText('abc def ghi'),
), ),
); );
final EditableText editableText = tester.widget(find.byType(EditableText)); final EditableText editableText = tester.widget(find.byType(EditableText));
...@@ -672,9 +706,9 @@ void main() { ...@@ -672,9 +706,9 @@ void main() {
testWidgetsWithLeakTracking("Slight movements in longpress don't hide/show handles", (WidgetTester tester) async { testWidgetsWithLeakTracking("Slight movements in longpress don't hide/show handles", (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
child: const SelectableText('abc def ghi'), child: const SelectableText('abc def ghi'),
), ),
); );
// Long press the 'e' to select 'def', but don't release the gesture. // Long press the 'e' to select 'def', but don't release the gesture.
final Offset ePos = textOffsetToPosition(tester, 5); final Offset ePos = textOffsetToPosition(tester, 5);
...@@ -682,13 +716,13 @@ void main() { ...@@ -682,13 +716,13 @@ void main() {
await tester.pump(const Duration(seconds: 2)); await tester.pump(const Duration(seconds: 2));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Handles are shown // Handles are shown.
final Finder fadeFinder = find.byType(FadeTransition); final Finder fadeFinder = find.byType(FadeTransition);
expect(fadeFinder, findsNWidgets(2)); // 2 handles, 1 toolbar expect(fadeFinder, findsNWidgets(2)); // 2 handles, 1 toolbar
FadeTransition handle = tester.widget(fadeFinder.at(0)); FadeTransition handle = tester.widget(fadeFinder.at(0));
expect(handle.opacity.value, equals(1.0)); expect(handle.opacity.value, equals(1.0));
// Move the gesture very slightly // Move the gesture very slightly.
await gesture.moveBy(const Offset(1.0, 1.0)); await gesture.moveBy(const Offset(1.0, 1.0));
await tester.pump(SelectionOverlay.fadeDuration * 0.5); await tester.pump(SelectionOverlay.fadeDuration * 0.5);
handle = tester.widget(fadeFinder.at(0)); handle = tester.widget(fadeFinder.at(0));
...@@ -699,9 +733,9 @@ void main() { ...@@ -699,9 +733,9 @@ void main() {
testWidgetsWithLeakTracking('Mouse long press is just like a tap', (WidgetTester tester) async { testWidgetsWithLeakTracking('Mouse long press is just like a tap', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
child: const SelectableText('abc def ghi'), child: const SelectableText('abc def ghi'),
), ),
); );
final EditableText editableText = tester.widget(find.byType(EditableText)); final EditableText editableText = tester.widget(find.byType(EditableText));
...@@ -721,12 +755,12 @@ void main() { ...@@ -721,12 +755,12 @@ void main() {
testWidgetsWithLeakTracking('selectable text basic', (WidgetTester tester) async { testWidgetsWithLeakTracking('selectable text basic', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
child: const SelectableText('selectable'), child: const SelectableText('selectable'),
), ),
); );
final EditableText editableTextWidget = tester.widget(find.byType(EditableText)); final EditableText editableTextWidget = tester.widget(find.byType(EditableText));
// selectable text cannot open keyboard. // Selectable text cannot open keyboard.
await tester.showKeyboard(find.byType(SelectableText)); await tester.showKeyboard(find.byType(SelectableText));
expect(tester.testTextInput.hasAnyClients, false); expect(tester.testTextInput.hasAnyClients, false);
await skipPastScrollingAnimation(tester); await skipPastScrollingAnimation(tester);
...@@ -1138,14 +1172,14 @@ void main() { ...@@ -1138,14 +1172,14 @@ void main() {
expect(findTextBox(), equals(textBox)); expect(findTextBox(), equals(textBox));
expect(textBox.size.height, emptyInputSize.height); expect(textBox.size.height, emptyInputSize.height);
// maxLines: 3 makes the SelectableText 3 lines tall // maxLines: 3 makes the SelectableText 3 lines tall.
await tester.pumpWidget(selectableTextBuilder(maxLines: 3)); await tester.pumpWidget(selectableTextBuilder(maxLines: 3));
expect(findTextBox(), equals(textBox)); expect(findTextBox(), equals(textBox));
expect(textBox.size.height, greaterThan(emptyInputSize.height)); expect(textBox.size.height, greaterThan(emptyInputSize.height));
final Size threeLineInputSize = textBox.size; final Size threeLineInputSize = textBox.size;
// Filling with 3 lines of text stays the same size // Filling with 3 lines of text stays the same size.
await tester.pumpWidget(selectableTextBuilder(text: kThreeLines, maxLines: 3)); await tester.pumpWidget(selectableTextBuilder(text: kThreeLines, maxLines: 3));
expect(findTextBox(), equals(textBox)); expect(findTextBox(), equals(textBox));
expect(textBox.size.height, threeLineInputSize.height); expect(textBox.size.height, threeLineInputSize.height);
...@@ -1155,14 +1189,14 @@ void main() { ...@@ -1155,14 +1189,14 @@ void main() {
expect(findTextBox(), equals(textBox)); expect(findTextBox(), equals(textBox));
expect(textBox.size.height, threeLineInputSize.height); expect(textBox.size.height, threeLineInputSize.height);
// But now it will... but it will max at four // But now it will... but it will max at four.
await tester.pumpWidget(selectableTextBuilder(text: kMoreThanFourLines, maxLines: 4)); await tester.pumpWidget(selectableTextBuilder(text: kMoreThanFourLines, maxLines: 4));
expect(findTextBox(), equals(textBox)); expect(findTextBox(), equals(textBox));
expect(textBox.size.height, greaterThan(threeLineInputSize.height)); expect(textBox.size.height, greaterThan(threeLineInputSize.height));
final Size fourLineInputSize = textBox.size; final Size fourLineInputSize = textBox.size;
// Now it won't max out until the end // Now it won't max out until the end.
await tester.pumpWidget(selectableTextBuilder(maxLines: null)); await tester.pumpWidget(selectableTextBuilder(maxLines: null));
expect(findTextBox(), equals(textBox)); expect(findTextBox(), equals(textBox));
expect(textBox.size, equals(emptyInputSize)); expect(textBox.size, equals(emptyInputSize));
...@@ -1386,7 +1420,7 @@ void main() { ...@@ -1386,7 +1420,7 @@ void main() {
final RenderBox textBox = findTextBox(); final RenderBox textBox = findTextBox();
final Size emptyInputSize = textBox.size; final Size emptyInputSize = textBox.size;
// Even if the text is a one liner, minimum height of SelectableText will determined by minLines // Even if the text is a one liner, minimum height of SelectableText will determined by minLines.
await tester.pumpWidget(selectableTextBuilder(text: 'No wrapping here.', minLines: 2, maxLines: 3)); await tester.pumpWidget(selectableTextBuilder(text: 'No wrapping here.', minLines: 2, maxLines: 3));
expect(findTextBox(), equals(textBox)); expect(findTextBox(), equals(textBox));
expect(textBox.size.height, emptyInputSize.height * 2); expect(textBox.size.height, emptyInputSize.height * 2);
...@@ -1473,7 +1507,7 @@ void main() { ...@@ -1473,7 +1507,7 @@ void main() {
firstFieldFocus.nextFocus(); firstFieldFocus.nextFocus();
await tester.pump(); await tester.pump();
// expecting focus to skip straight to the second field // Expecting focus to skip straight to the second field.
expect(firstFieldFocus.hasFocus, isFalse); expect(firstFieldFocus.hasFocus, isFalse);
expect(lastFieldFocus.hasFocus, isTrue); expect(lastFieldFocus.hasFocus, isTrue);
}); });
...@@ -1789,7 +1823,7 @@ void main() { ...@@ -1789,7 +1823,7 @@ void main() {
controller.selection = const TextSelection.collapsed(offset: 0); controller.selection = const TextSelection.collapsed(offset: 0);
await tester.pump(); await tester.pump();
// Select the first 5 characters // Select the first 5 characters.
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift); await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
for (int i = 0; i < 5; i += 1) { for (int i = 0; i < 5; i += 1) {
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
...@@ -1797,7 +1831,7 @@ void main() { ...@@ -1797,7 +1831,7 @@ void main() {
} }
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift); await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
// Copy them // Copy them.
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight); await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight);
await tester.sendKeyDownEvent(LogicalKeyboardKey.keyC); await tester.sendKeyDownEvent(LogicalKeyboardKey.keyC);
await tester.sendKeyUpEvent(LogicalKeyboardKey.keyC); await tester.sendKeyUpEvent(LogicalKeyboardKey.keyC);
...@@ -1835,7 +1869,7 @@ void main() { ...@@ -1835,7 +1869,7 @@ void main() {
await tester.tap(find.byType(SelectableText)); await tester.tap(find.byType(SelectableText));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Select All // Select All.
await tester.sendKeyDownEvent(LogicalKeyboardKey.control); await tester.sendKeyDownEvent(LogicalKeyboardKey.control);
await tester.sendKeyEvent(LogicalKeyboardKey.keyA); await tester.sendKeyEvent(LogicalKeyboardKey.keyA);
await tester.sendKeyUpEvent(LogicalKeyboardKey.control); await tester.sendKeyUpEvent(LogicalKeyboardKey.control);
...@@ -1881,7 +1915,7 @@ void main() { ...@@ -1881,7 +1915,7 @@ void main() {
controller.selection = const TextSelection.collapsed(offset: 0); controller.selection = const TextSelection.collapsed(offset: 0);
await tester.pump(); await tester.pump();
// Select the first 5 characters // Select the first 5 characters.
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift); await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
for (int i = 0; i < 5; i += 1) { for (int i = 0; i < 5; i += 1) {
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
...@@ -2055,11 +2089,11 @@ void main() { ...@@ -2055,11 +2089,11 @@ void main() {
testWidgetsWithLeakTracking('Caret works when maxLines is null', (WidgetTester tester) async { testWidgetsWithLeakTracking('Caret works when maxLines is null', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
child: const SelectableText( child: const SelectableText(
'x', 'x',
),
), ),
),
); );
final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first); final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first);
...@@ -2549,7 +2583,7 @@ void main() { ...@@ -2549,7 +2583,7 @@ void main() {
), ),
); );
// Test show on screen. // Test shows on screen.
expect(controller.offset, 0.0); expect(controller.offset, 0.0);
tester.binding.pipelineOwner.semanticsOwner!.performAction(8, SemanticsAction.showOnScreen); tester.binding.pipelineOwner.semanticsOwner!.performAction(8, SemanticsAction.showOnScreen);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -2575,7 +2609,7 @@ void main() { ...@@ -2575,7 +2609,7 @@ void main() {
final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first); final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first);
final TextEditingController controller = editableTextWidget.controller; final TextEditingController controller = editableTextWidget.controller;
// Focus the selectable text // Focus the selectable text.
await tester.tap(find.byKey(key)); await tester.tap(find.byKey(key));
await tester.pump(); await tester.pump();
...@@ -2607,7 +2641,7 @@ void main() { ...@@ -2607,7 +2641,7 @@ void main() {
], ],
), ignoreTransform: true, ignoreRect: true)); ), ignoreTransform: true, ignoreRect: true));
// move cursor back once // Move cursor back once.
semanticsOwner.performAction(inputFieldId, SemanticsAction.setSelection, <dynamic, dynamic>{ semanticsOwner.performAction(inputFieldId, SemanticsAction.setSelection, <dynamic, dynamic>{
'base': 4, 'base': 4,
'extent': 4, 'extent': 4,
...@@ -2615,7 +2649,7 @@ void main() { ...@@ -2615,7 +2649,7 @@ void main() {
await tester.pump(); await tester.pump();
expect(controller.selection, const TextSelection.collapsed(offset: 4)); expect(controller.selection, const TextSelection.collapsed(offset: 4));
// move cursor to front // Move cursor to front.
semanticsOwner.performAction(inputFieldId, SemanticsAction.setSelection, <dynamic, dynamic>{ semanticsOwner.performAction(inputFieldId, SemanticsAction.setSelection, <dynamic, dynamic>{
'base': 0, 'base': 0,
'extent': 0, 'extent': 0,
...@@ -2623,7 +2657,7 @@ void main() { ...@@ -2623,7 +2657,7 @@ void main() {
await tester.pump(); await tester.pump();
expect(controller.selection, const TextSelection.collapsed(offset: 0)); expect(controller.selection, const TextSelection.collapsed(offset: 0));
// select all // Select all.
semanticsOwner.performAction(inputFieldId, SemanticsAction.setSelection, <dynamic, dynamic>{ semanticsOwner.performAction(inputFieldId, SemanticsAction.setSelection, <dynamic, dynamic>{
'base': 0, 'base': 0,
'extent': 5, 'extent': 5,
...@@ -2776,7 +2810,7 @@ void main() { ...@@ -2776,7 +2810,7 @@ void main() {
); );
} }
// Empty TextStyle is overridden by theme // Empty TextStyle is overridden by theme.
await tester.pumpWidget(buildFrame(const TextStyle())); await tester.pumpWidget(buildFrame(const TextStyle()));
EditableText editableText = tester.widget(find.byType(EditableText)); EditableText editableText = tester.widget(find.byType(EditableText));
expect(editableText.style.color, defaultStyle.color); expect(editableText.style.color, defaultStyle.color);
...@@ -2786,13 +2820,13 @@ void main() { ...@@ -2786,13 +2820,13 @@ void main() {
expect(editableText.style.locale, defaultStyle.locale); expect(editableText.style.locale, defaultStyle.locale);
expect(editableText.style.wordSpacing, defaultStyle.wordSpacing); expect(editableText.style.wordSpacing, defaultStyle.wordSpacing);
// Properties set on TextStyle override theme // Properties set on TextStyle override theme.
const Color setColor = Colors.red; const Color setColor = Colors.red;
await tester.pumpWidget(buildFrame(const TextStyle(color: setColor))); await tester.pumpWidget(buildFrame(const TextStyle(color: setColor)));
editableText = tester.widget(find.byType(EditableText)); editableText = tester.widget(find.byType(EditableText));
expect(editableText.style.color, setColor); expect(editableText.style.color, setColor);
// inherit: false causes nothing to be merged in from theme // inherit: false causes nothing to be merged in from theme.
await tester.pumpWidget(buildFrame(const TextStyle( await tester.pumpWidget(buildFrame(const TextStyle(
fontSize: 24.0, fontSize: 24.0,
textBaseline: TextBaseline.alphabetic, textBaseline: TextBaseline.alphabetic,
...@@ -2821,7 +2855,7 @@ void main() { ...@@ -2821,7 +2855,7 @@ void main() {
))); )));
expect(tester.takeException(), isNull); expect(tester.takeException(), isNull);
// With inherit not set to false, will pickup required fields from theme // With inherit not set to false, will pickup required fields from theme.
await tester.pumpWidget(buildFrame(const TextStyle( await tester.pumpWidget(buildFrame(const TextStyle(
fontSize: 12.0, fontSize: 12.0,
))); )));
...@@ -2946,7 +2980,6 @@ void main() { ...@@ -2946,7 +2980,6 @@ void main() {
), ),
); );
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
final Offset selectableTextStart = tester.getTopLeft(find.byType(SelectableText)); final Offset selectableTextStart = tester.getTopLeft(find.byType(SelectableText));
// This tap just puts the cursor somewhere different than where the double // This tap just puts the cursor somewhere different than where the double
...@@ -2974,8 +3007,7 @@ void main() { ...@@ -2974,8 +3007,7 @@ void main() {
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
// Selected text shows 1 toolbar buttons on MacOS, 2 on iOS. expectCupertinoSelectionToolbar();
expect(find.byType(CupertinoButton), isTargetPlatformIOS ? findsNWidgets(4) : findsNWidgets(1));
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -3020,8 +3052,7 @@ void main() { ...@@ -3020,8 +3052,7 @@ void main() {
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
// Selected text shows 2 toolbar buttons: copy, select all expectMaterialSelectionToolbar();
expect(find.byType(TextButton), findsNWidgets(2));
}, },
); );
...@@ -3055,7 +3086,7 @@ void main() { ...@@ -3055,7 +3086,7 @@ void main() {
await tester.tapAt(textOffsetToPosition(tester, index)); await tester.tapAt(textOffsetToPosition(tester, index));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
// First tap doesn't change the selection // First tap doesn't change the selection.
expect( expect(
controller.selection, controller.selection,
const TextSelection.collapsed(offset: index), const TextSelection.collapsed(offset: index),
...@@ -3069,8 +3100,7 @@ void main() { ...@@ -3069,8 +3100,7 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
// Selected text shows 2 toolbar buttons: copy, select all expectMaterialSelectionToolbar();
expect(find.byType(TextButton), findsNWidgets(2));
}, },
); );
...@@ -3088,7 +3118,6 @@ void main() { ...@@ -3088,7 +3118,6 @@ void main() {
); );
final Offset selectableTextStart = tester.getTopLeft(find.byType(SelectableText)); final Offset selectableTextStart = tester.getTopLeft(find.byType(SelectableText));
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
await tester.tapAt(selectableTextStart + const Offset(150.0, 5.0)); await tester.tapAt(selectableTextStart + const Offset(150.0, 5.0));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -3105,8 +3134,7 @@ void main() { ...@@ -3105,8 +3134,7 @@ void main() {
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
// Selected text shows 2 toolbar buttons for iOS, 1 for macOS. expectCupertinoSelectionToolbar();
expect(find.byType(CupertinoButton), isTargetPlatformIOS ? findsNWidgets(4) : findsNWidgets(1));
await gesture.up(); await gesture.up();
await tester.pump(); await tester.pump();
...@@ -3117,7 +3145,7 @@ void main() { ...@@ -3117,7 +3145,7 @@ void main() {
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
// The toolbar is still showing. // The toolbar is still showing.
expect(find.byType(CupertinoButton), isTargetPlatformIOS ? findsNWidgets(4) : findsNWidgets(1)); expectCupertinoSelectionToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -3216,7 +3244,6 @@ void main() { ...@@ -3216,7 +3244,6 @@ void main() {
); );
final Offset selectableTextStart = tester.getTopLeft(find.byType(SelectableText)); final Offset selectableTextStart = tester.getTopLeft(find.byType(SelectableText));
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
await tester.longPressAt(selectableTextStart + const Offset(50.0, 5.0)); await tester.longPressAt(selectableTextStart + const Offset(50.0, 5.0));
await tester.pump(); await tester.pump();
...@@ -3224,7 +3251,7 @@ void main() { ...@@ -3224,7 +3251,7 @@ void main() {
final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first); final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first);
final TextEditingController controller = editableTextWidget.controller; final TextEditingController controller = editableTextWidget.controller;
// The longpressed word is selected. // The long pressed word is selected.
expect( expect(
controller.selection, controller.selection,
const TextSelection( const TextSelection(
...@@ -3233,8 +3260,7 @@ void main() { ...@@ -3233,8 +3260,7 @@ void main() {
), ),
); );
// Toolbar shows one button. expectCupertinoSelectionToolbar();
expect(find.byType(CupertinoButton), isTargetPlatformIOS ? findsNWidgets(4) : findsNWidgets(1));
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -3266,20 +3292,19 @@ void main() { ...@@ -3266,20 +3292,19 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
// Collapsed toolbar shows 2 buttons: copy, select all expectMaterialSelectionToolbar();
expect(find.byType(TextButton), findsNWidgets(2));
}, },
); );
testWidgetsWithLeakTracking( testWidgetsWithLeakTracking(
'long press selects word and shows custom toolbar (Android)', 'long press selects word and shows custom toolbar (Cupertino)',
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
child: Center( child: Center(
child: SelectableText('Atwater Peel Sherbrooke Bonaventure', child: SelectableText('Atwater Peel Sherbrooke Bonaventure',
selectionControls: cupertinoTextSelectionControls, selectionControls: cupertinoTextSelectionControls,
), ),
), ),
), ),
...@@ -3294,7 +3319,7 @@ void main() { ...@@ -3294,7 +3319,7 @@ void main() {
final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first); final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first);
final TextEditingController controller = editableTextWidget.controller; final TextEditingController controller = editableTextWidget.controller;
// The longpressed word is selected. // The long pressed word is selected.
expect( expect(
controller.selection, controller.selection,
const TextSelection( const TextSelection(
...@@ -3303,14 +3328,15 @@ void main() { ...@@ -3303,14 +3328,15 @@ void main() {
), ),
); );
// Toolbar shows one button. // Toolbar shows one button (copy).
expect(find.byType(CupertinoButton), findsNWidgets(1)); expect(find.byType(CupertinoButton), findsNWidgets(1));
expect(find.text('Copy'), findsOneWidget);
}, },
variant: TargetPlatformVariant.all(), variant: TargetPlatformVariant.all(),
); );
testWidgetsWithLeakTracking( testWidgetsWithLeakTracking(
'long press selects word and shows custom toolbar (iOS)', 'long press selects word and shows custom toolbar (Material)',
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
...@@ -3318,7 +3344,7 @@ void main() { ...@@ -3318,7 +3344,7 @@ void main() {
home: Material( home: Material(
child: Center( child: Center(
child: SelectableText('Atwater Peel Sherbrooke Bonaventure', child: SelectableText('Atwater Peel Sherbrooke Bonaventure',
selectionControls: materialTextSelectionControls, selectionControls: materialTextSelectionControls,
), ),
), ),
), ),
...@@ -3340,6 +3366,8 @@ void main() { ...@@ -3340,6 +3366,8 @@ void main() {
// Collapsed toolbar shows 2 buttons: copy, select all // Collapsed toolbar shows 2 buttons: copy, select all
expect(find.byType(TextButton), findsNWidgets(2)); expect(find.byType(TextButton), findsNWidgets(2));
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Select all'), findsOneWidget);
}, },
variant: TargetPlatformVariant.all(), variant: TargetPlatformVariant.all(),
); );
...@@ -3476,8 +3504,8 @@ void main() { ...@@ -3476,8 +3504,8 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 23, extentOffset: 0), const TextSelection(baseOffset: 23, extentOffset: 0),
); );
// The toolbar now shows up.
expect(find.byType(TextButton), findsNWidgets(2)); expectMaterialSelectionToolbar();
}, },
variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -3499,11 +3527,10 @@ void main() { ...@@ -3499,11 +3527,10 @@ void main() {
await tester.startGesture(textOffsetToPosition(tester, 18)); await tester.startGesture(textOffsetToPosition(tester, 18));
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first); final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first);
final TextEditingController controller = editableTextWidget.controller; final TextEditingController controller = editableTextWidget.controller;
// The longpressed word is selected. // The long pressed word is selected.
expect( expect(
controller.selection, controller.selection,
const TextSelection( const TextSelection(
...@@ -3570,7 +3597,7 @@ void main() { ...@@ -3570,7 +3597,7 @@ void main() {
), ),
); );
// The toolbar now shows up. // The toolbar now shows up.
expect(find.byType(CupertinoButton), isTargetPlatformIOS ? findsNWidgets(4) : findsNWidgets(1)); expectCupertinoSelectionToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
...@@ -3597,7 +3624,7 @@ void main() { ...@@ -3597,7 +3624,7 @@ void main() {
final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first); final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first);
final TextEditingController controller = editableTextWidget.controller; final TextEditingController controller = editableTextWidget.controller;
// The longpressed word is selected. // The long pressed word is selected.
expect( expect(
controller.selection, controller.selection,
const TextSelection( const TextSelection(
...@@ -3648,7 +3675,7 @@ void main() { ...@@ -3648,7 +3675,7 @@ void main() {
), ),
); );
// The toolbar now shows up. // The toolbar now shows up.
expect(find.byType(CupertinoButton), findsNWidgets(1)); expectCupertinoSelectionToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.macOS }),
); );
...@@ -3735,7 +3762,7 @@ void main() { ...@@ -3735,7 +3762,7 @@ void main() {
extentOffset: 66, extentOffset: 66,
), ),
); );
// The toolbar now shows up. // The toolbar shows up with one button (copy).
expect(find.byType(CupertinoButton), findsNWidgets(1)); expect(find.byType(CupertinoButton), findsNWidgets(1));
lastCharEndpoint = renderEditable.getEndpointsForSelection( lastCharEndpoint = renderEditable.getEndpointsForSelection(
...@@ -3797,8 +3824,7 @@ void main() { ...@@ -3797,8 +3824,7 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
// Long press toolbar. expectCupertinoSelectionToolbar();
expect(find.byType(CupertinoButton), findsNWidgets(4));
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
...@@ -3841,8 +3867,8 @@ void main() { ...@@ -3841,8 +3867,8 @@ void main() {
const TextSelection(baseOffset: 7, extentOffset: 8), const TextSelection(baseOffset: 7, extentOffset: 8),
); );
// Long press toolbar. // The toolbar shows up.
expect(find.byType(CupertinoButton), findsNWidgets(1)); expectCupertinoSelectionToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.macOS }),
); );
...@@ -3861,7 +3887,6 @@ void main() { ...@@ -3861,7 +3887,6 @@ void main() {
); );
final Offset selectableTextStart = tester.getTopLeft(find.byType(SelectableText)); final Offset selectableTextStart = tester.getTopLeft(find.byType(SelectableText));
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
await tester.longPressAt(selectableTextStart + const Offset(50.0, 5.0)); await tester.longPressAt(selectableTextStart + const Offset(50.0, 5.0));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -3892,7 +3917,7 @@ void main() { ...@@ -3892,7 +3917,7 @@ void main() {
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
expect(find.byType(CupertinoButton), isTargetPlatformIOS ? findsNWidgets(4) : findsNWidgets(1)); expectCupertinoSelectionToolbar();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -3909,7 +3934,6 @@ void main() { ...@@ -3909,7 +3934,6 @@ void main() {
), ),
); );
final Offset selectableTextStart = tester.getTopLeft(find.byType(SelectableText)); final Offset selectableTextStart = tester.getTopLeft(find.byType(SelectableText));
final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS;
await tester.tapAt(selectableTextStart + const Offset(50.0, 5.0)); await tester.tapAt(selectableTextStart + const Offset(50.0, 5.0));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -3927,7 +3951,7 @@ void main() { ...@@ -3927,7 +3951,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), isTargetPlatformIOS ? findsNWidgets(4) : findsNWidgets(1)); expectCupertinoSelectionToolbar();
// Double tap selecting the same word somewhere else is fine. // Double tap selecting the same word somewhere else is fine.
await tester.pumpAndSettle(kDoubleTapTimeout); await tester.pumpAndSettle(kDoubleTapTimeout);
...@@ -3944,7 +3968,7 @@ void main() { ...@@ -3944,7 +3968,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 0, extentOffset: 7), const TextSelection(baseOffset: 0, extentOffset: 7),
); );
expect(find.byType(CupertinoButton), isTargetPlatformIOS ? findsNWidgets(4) : findsNWidgets(1)); expectCupertinoSelectionToolbar();
// Hide the toolbar so it doesn't interfere with taps on the text. // Hide the toolbar so it doesn't interfere with taps on the text.
final EditableTextState editableTextState = final EditableTextState editableTextState =
...@@ -3966,7 +3990,7 @@ void main() { ...@@ -3966,7 +3990,7 @@ void main() {
controller.selection, controller.selection,
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
expect(find.byType(CupertinoButton), isTargetPlatformIOS ? findsNWidgets(4) : findsNWidgets(1)); expectCupertinoSelectionToolbar();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
...@@ -4045,7 +4069,7 @@ void main() { ...@@ -4045,7 +4069,7 @@ void main() {
await gesture.up(); await gesture.up();
await tester.pump(); await tester.pump();
expect(find.byType(CupertinoButton), findsNWidgets(4)); expectCupertinoSelectionToolbar();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
testWidgetsWithLeakTracking('tap on non-force-press-supported devices work', (WidgetTester tester) async { testWidgetsWithLeakTracking('tap on non-force-press-supported devices work', (WidgetTester tester) async {
...@@ -4111,7 +4135,7 @@ void main() { ...@@ -4111,7 +4135,7 @@ void main() {
testWidgetsWithLeakTracking('SelectableText implements debugFillProperties', (WidgetTester tester) async { testWidgetsWithLeakTracking('SelectableText implements debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
// Not checking controller, inputFormatters, focusNode // Not checking controller, inputFormatters, focusNode.
const SelectableText( const SelectableText(
'something', 'something',
style: TextStyle(color: Color(0xff00ff00)), style: TextStyle(color: Color(0xff00ff00)),
......
// Copyright 2014 The Flutter 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/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/platform.dart';
import 'package:flutter_test/flutter_test.dart';
import '../widgets/editable_text_utils.dart';
Finder findCupertinoOverflowNextButton() {
return find.byWidgetPredicate((Widget widget) {
return widget is CustomPaint && '${widget.painter?.runtimeType}' == '_RightCupertinoChevronPainter';
});
}
Finder findCupertinoOverflowBackButton() {
return find.byWidgetPredicate((Widget widget) {
return widget is CustomPaint && '${widget.painter?.runtimeType}' == '_LeftCupertinoChevronPainter';
});
}
Future<void> tapCupertinoOverflowNextButton(WidgetTester tester) async{
await tester.tapAt(tester.getCenter(findCupertinoOverflowNextButton()));
await tester.pumpAndSettle();
}
void expectNoCupertinoToolbar() {
expect(find.byType(CupertinoButton), findsNothing);
}
// Check that the Cupertino text selection toolbars show the expected buttons
// when the content is partially selected.
void expectCupertinoToolbarForPartialSelection() {
if (isContextMenuProvidedByPlatform) {
expectNoCupertinoToolbar();
return;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
expect(find.byType(CupertinoButton), findsNWidgets(5));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Share...'), findsOneWidget);
expect(find.text('Select All'), findsOneWidget);
case TargetPlatform.iOS:
expect(find.byType(CupertinoButton), findsNWidgets(6));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Share...'), findsOneWidget);
expect(find.text('Look Up'), findsOneWidget);
expect(find.text('Search Web'), findsOneWidget);
case TargetPlatform.macOS:
expect(find.byType(CupertinoButton), findsNWidgets(3));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(find.byType(CupertinoButton), findsNWidgets(4));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Select All'), findsOneWidget);
}
}
// Check that the Cupertino text selection toolbar shows the expected buttons
// when the content is fully selected.
void expectCupertinoToolbarForFullSelection() {
if (isContextMenuProvidedByPlatform) {
expectNoCupertinoToolbar();
return;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
expect(find.byType(CupertinoButton), findsNWidgets(4));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Share...'), findsOneWidget);
case TargetPlatform.iOS:
expect(find.byType(CupertinoButton), findsNWidgets(6));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Share...'), findsOneWidget);
expect(find.text('Look Up'), findsOneWidget);
expect(find.text('Search Web'), findsOneWidget);
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.macOS:
case TargetPlatform.windows:
expect(find.byType(CupertinoButton), findsNWidgets(3));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
}
}
// Check that the Cupertino text selection toolbar is correct for a collapsed selection.
void expectCupertinoToolbarForCollapsedSelection() {
if (isContextMenuProvidedByPlatform) {
expectNoCupertinoToolbar();
return;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
expect(find.byType(CupertinoButton), findsNWidgets(4));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Share...'), findsOneWidget);
case TargetPlatform.iOS:
expect(find.byType(CupertinoButton), findsNWidgets(2));
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Select All'), findsOneWidget);
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
case TargetPlatform.macOS:
expect(find.byType(CupertinoButton), findsNWidgets(1));
expect(find.text('Paste'), findsOneWidget);
}
}
void expectNoMaterialToolbar() {
expect(find.byType(TextButton), findsNothing);
}
// Check that the Material text selection toolbars show the expected buttons
// when the content is partially selected.
void expectMaterialToolbarForPartialSelection() {
if (isContextMenuProvidedByPlatform) {
expectNoMaterialToolbar();
return;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
expect(find.byType(TextButton), findsNWidgets(5));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Share'), findsOneWidget);
expect(find.text('Select all'), findsOneWidget);
case TargetPlatform.iOS:
case TargetPlatform.macOS:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(find.byType(TextButton), findsNWidgets(4));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Select all'), findsOneWidget);
}
}
// Check that the Material text selection toolbar shows the expected buttons
// when the content is fully selected.
void expectMaterialToolbarForFullSelection() {
if (isContextMenuProvidedByPlatform) {
expectNoMaterialToolbar();
return;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
expect(find.byType(TextButton), findsNWidgets(4));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
expect(find.text('Share'), findsOneWidget);
case TargetPlatform.iOS:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.macOS:
case TargetPlatform.windows:
expect(find.byType(TextButton), findsNWidgets(3));
expect(find.text('Cut'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget);
}
}
Finder findMaterialOverflowNextButton() {
return find.byIcon(Icons.more_vert);
}
Finder findMaterialOverflowBackButton() {
return find.byIcon(Icons.arrow_back);
}
Future<void> tapMaterialOverflowNextButton(WidgetTester tester) async {
await tester.tapAt(tester.getCenter(findMaterialOverflowNextButton()));
await tester.pumpAndSettle();
}
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