From 089db4651803d57bd7fdb72fbceef1cb83f999ea Mon Sep 17 00:00:00 2001 From: Renzo Olivares Date: Mon, 29 Jun 2026 23:42:54 +0000 Subject: [PATCH 1/7] [cupertino_ui] Update text selection toolbar utils helper Add missing helper functions from the Flutter framework to support text field tests. TAG=agy CONV=eaf9ac41-f14c-4263-8856-55084f46e5db --- .../test/text_selection_toolbar_utils.dart | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/packages/cupertino_ui/test/text_selection_toolbar_utils.dart b/packages/cupertino_ui/test/text_selection_toolbar_utils.dart index c17218c20946..8f84e4cf8717 100644 --- a/packages/cupertino_ui/test/text_selection_toolbar_utils.dart +++ b/packages/cupertino_ui/test/text_selection_toolbar_utils.dart @@ -3,8 +3,12 @@ // found in the LICENSE file. import 'package:cupertino_ui/cupertino_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/foundation/platform.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'editable_text_utils.dart'; + Finder findCupertinoOverflowNextButton() { return find.byWidgetPredicate((Widget widget) { return widget is CustomPaint && @@ -23,3 +27,181 @@ Future 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.byKey(StandardComponentType.moreButton.key); +} + +Finder findMaterialOverflowBackButton() { + return find.byKey(StandardComponentType.backButton.key); +} + +Future tapMaterialOverflowNextButton(WidgetTester tester) async { + await tester.tapAt(tester.getCenter(findMaterialOverflowNextButton())); + await tester.pumpAndSettle(); +} From 4cf176bbbd32bdba2a46aff0d62b3b3a57de4f24 Mon Sep 17 00:00:00 2001 From: Renzo Olivares Date: Mon, 29 Jun 2026 23:42:54 +0000 Subject: [PATCH 2/7] [cupertino_ui] Migrate and enable text_field_test.dart Remove @Skip and move the file from temporarily_disabled_tests/ to test/. TAG=agy CONV=eaf9ac41-f14c-4263-8856-55084f46e5db --- .../text_field_test.dart | 235 ++++++------------ 1 file changed, 76 insertions(+), 159 deletions(-) rename packages/cupertino_ui/{temporarily_disabled_tests => test}/text_field_test.dart (98%) diff --git a/packages/cupertino_ui/temporarily_disabled_tests/text_field_test.dart b/packages/cupertino_ui/test/text_field_test.dart similarity index 98% rename from packages/cupertino_ui/temporarily_disabled_tests/text_field_test.dart rename to packages/cupertino_ui/test/text_field_test.dart index a97496688f18..a4091e8abd8c 100644 --- a/packages/cupertino_ui/temporarily_disabled_tests/text_field_test.dart +++ b/packages/cupertino_ui/test/text_field_test.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@Skip( - 'This file is skipped due to a cross-import that needs to be fixed. Tracked in https://github.com/flutter/flutter/issues/177028.', -) // reduced-test-set: // This file is run as part of a reduced test set in CI on Mac and Windows // machines. @@ -26,11 +23,10 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../widgets/clipboard_utils.dart'; -import '../widgets/semantics_tester.dart'; -import '../widgets/text_selection_toolbar_utils.dart'; +import 'clipboard_utils.dart'; import 'editable_text_utils.dart'; import 'live_text_utils.dart'; +import 'text_selection_toolbar_utils.dart'; class MockTextSelectionControls extends TextSelectionControls { @override @@ -537,65 +533,36 @@ void main() { testWidgets('Activates the text field when receives semantics focus on desktops', ( WidgetTester tester, ) async { - final semantics = SemanticsTester(tester); + final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!; final focusNode = FocusNode(); addTearDown(focusNode.dispose); await tester.pumpWidget(CupertinoApp(home: CupertinoTextField(focusNode: focusNode))); + + final SemanticsNode node = tester.getSemantics(find.byType(CupertinoTextField)); expect( - semantics, - hasSemantics( - TestSemantics.root( - children: [ - TestSemantics( - id: 1, - textDirection: TextDirection.ltr, - children: [ - TestSemantics( - id: 2, - children: [ - TestSemantics( - id: 3, - flags: [SemanticsFlag.scopesRoute], - children: [ - TestSemantics( - id: 4, - inputType: ui.SemanticsInputType.text, - flags: [ - SemanticsFlag.isTextField, - SemanticsFlag.isFocusable, - SemanticsFlag.hasEnabledState, - SemanticsFlag.isEnabled, - ], - actions: [ - SemanticsAction.tap, - SemanticsAction.focus, - SemanticsAction.didGainAccessibilityFocus, - SemanticsAction.didLoseAccessibilityFocus, - ], - textDirection: TextDirection.ltr, - ), - ], - ), - ], - ), - ], - ), - ], - ), - ignoreRect: true, - ignoreTransform: true, + node, + isSemantics( + inputType: ui.SemanticsInputType.text, + isTextField: true, + isFocusable: true, + hasEnabledState: true, + isEnabled: true, + hasTapAction: true, + hasFocusAction: true, + hasDidGainAccessibilityFocusAction: true, + hasDidLoseAccessibilityFocusAction: true, ), ); expect(focusNode.hasFocus, isFalse); - semanticsOwner.performAction(4, SemanticsAction.didGainAccessibilityFocus); + semanticsOwner.performAction(node.id, SemanticsAction.didGainAccessibilityFocus); await tester.pumpAndSettle(); expect(focusNode.hasFocus, isTrue); - semanticsOwner.performAction(4, SemanticsAction.didLoseAccessibilityFocus); + semanticsOwner.performAction(node.id, SemanticsAction.didLoseAccessibilityFocus); await tester.pumpAndSettle(); expect(focusNode.hasFocus, isFalse); - semantics.dispose(); + handle.dispose(); }, variant: TargetPlatformVariant.desktop()); testWidgets('takes available space horizontally and takes intrinsic space vertically no-strut', ( @@ -2353,20 +2320,18 @@ void main() { }); testWidgets('Readonly text field does not have tap action', (WidgetTester tester) async { - final semantics = SemanticsTester(tester); + final SemanticsHandle handle = tester.ensureSemantics(); await tester.pumpWidget( const CupertinoApp(home: Center(child: CupertinoTextField(maxLength: 10, readOnly: true))), ); expect( - semantics, - isNot( - includesNodeWith(actions: [SemanticsAction.tap, SemanticsAction.focus]), - ), + tester.getSemantics(find.byType(CupertinoTextField)), + isSemantics(hasTapAction: false, hasFocusAction: true), ); - semantics.dispose(); + handle.dispose(); }); testWidgets( @@ -10472,136 +10437,84 @@ void main() { testWidgets('when enabled listens to onFocus events and gains focus', ( WidgetTester tester, ) async { - final semantics = SemanticsTester(tester); + final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!; final focusNode = FocusNode(); addTearDown(focusNode.dispose); await tester.pumpWidget(CupertinoApp(home: CupertinoTextField(focusNode: focusNode))); + + final SemanticsNode node = tester.getSemantics(find.byType(CupertinoTextField)); + final bool isDesktop = + defaultTargetPlatform == TargetPlatform.linux || + defaultTargetPlatform == TargetPlatform.windows || + defaultTargetPlatform == TargetPlatform.macOS; + expect( - semantics, - hasSemantics( - TestSemantics.root( - children: [ - TestSemantics( - id: 1, - children: [ - TestSemantics( - id: 2, - children: [ - TestSemantics( - id: 3, - flags: [SemanticsFlag.scopesRoute], - children: [ - TestSemantics( - id: 4, - inputType: ui.SemanticsInputType.text, - flags: [ - SemanticsFlag.isTextField, - SemanticsFlag.isFocusable, - SemanticsFlag.hasEnabledState, - SemanticsFlag.isEnabled, - ], - actions: [ - SemanticsAction.tap, - SemanticsAction.focus, - if (defaultTargetPlatform == TargetPlatform.linux || - defaultTargetPlatform == TargetPlatform.windows || - defaultTargetPlatform == TargetPlatform.macOS) ...[ - SemanticsAction.didGainAccessibilityFocus, - SemanticsAction.didLoseAccessibilityFocus, - ], - // TODO(gspencergoog): also test for the presence of SemanticsAction.focus when - // this iOS issue is addressed: https://github.com/flutter/flutter/issues/150030 - ], - ), - ], - ), - ], - ), - ], - ), - ], - ), - ignoreRect: true, - ignoreTransform: true, + node, + isSemantics( + inputType: ui.SemanticsInputType.text, + isTextField: true, + isFocusable: true, + hasEnabledState: true, + isEnabled: true, + hasTapAction: true, + hasFocusAction: true, + hasDidGainAccessibilityFocusAction: isDesktop, + hasDidLoseAccessibilityFocusAction: isDesktop, ), ); expect(focusNode.hasFocus, isFalse); - semanticsOwner.performAction(4, SemanticsAction.focus); + semanticsOwner.performAction(node.id, SemanticsAction.focus); await tester.pumpAndSettle(); expect(focusNode.hasFocus, isTrue); - semantics.dispose(); + handle.dispose(); }, variant: TargetPlatformVariant.all()); testWidgets('when disabled does not listen to onFocus events or gain focus', ( WidgetTester tester, ) async { - final semantics = SemanticsTester(tester); + final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!; final focusNode = FocusNode(); addTearDown(focusNode.dispose); await tester.pumpWidget( CupertinoApp(home: CupertinoTextField(focusNode: focusNode, enabled: false)), ); + + final SemanticsNode node = tester.getSemantics(find.byType(CupertinoTextField)); + final bool isDesktop = + defaultTargetPlatform == TargetPlatform.linux || + defaultTargetPlatform == TargetPlatform.windows || + defaultTargetPlatform == TargetPlatform.macOS; + expect( - semantics, - hasSemantics( - TestSemantics.root( - children: [ - TestSemantics( - id: 1, - textDirection: TextDirection.ltr, - children: [ - TestSemantics( - id: 2, - children: [ - TestSemantics( - id: 3, - flags: [SemanticsFlag.scopesRoute], - children: [ - TestSemantics( - id: 4, - inputType: ui.SemanticsInputType.text, - flags: [ - SemanticsFlag.isTextField, - SemanticsFlag.isFocusable, - SemanticsFlag.hasEnabledState, - SemanticsFlag.isReadOnly, - ], - actions: [ - if (defaultTargetPlatform == TargetPlatform.linux || - defaultTargetPlatform == TargetPlatform.windows || - defaultTargetPlatform == TargetPlatform.macOS) ...[ - SemanticsAction.didGainAccessibilityFocus, - SemanticsAction.didLoseAccessibilityFocus, - ], - ], - ), - ], - ), - ], - ), - ], - ), - ], - ), - ignoreRect: true, - ignoreTransform: true, + node, + isSemantics( + inputType: ui.SemanticsInputType.text, + isTextField: true, + isFocusable: true, + hasEnabledState: true, + isEnabled: false, + isReadOnly: true, + hasTapAction: false, + hasFocusAction: false, + hasDidGainAccessibilityFocusAction: isDesktop, + hasDidLoseAccessibilityFocusAction: isDesktop, ), ); expect(focusNode.hasFocus, isFalse); - semanticsOwner.performAction(4, SemanticsAction.focus); + semanticsOwner.performAction(node.id, SemanticsAction.focus); await tester.pumpAndSettle(); expect(focusNode.hasFocus, isFalse); - semantics.dispose(); + handle.dispose(); }, variant: TargetPlatformVariant.all()); testWidgets('when receives SemanticsAction.focus while already focused, shows keyboard', ( WidgetTester tester, ) async { - final semantics = SemanticsTester(tester); + final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!; final focusNode = FocusNode(); addTearDown(focusNode.dispose); @@ -10609,20 +10522,22 @@ void main() { focusNode.requestFocus(); await tester.pumpAndSettle(); + final SemanticsNode node = tester.getSemantics(find.byType(CupertinoTextField)); + tester.testTextInput.log.clear(); expect(focusNode.hasFocus, isTrue); - semanticsOwner.performAction(4, SemanticsAction.focus); + semanticsOwner.performAction(node.id, SemanticsAction.focus); await tester.pumpAndSettle(); expect(focusNode.hasFocus, isTrue); expect(tester.testTextInput.log.single.method, 'TextInput.show'); - semantics.dispose(); + handle.dispose(); }, variant: TargetPlatformVariant.all()); testWidgets( 'when receives SemanticsAction.focus while focused but read-only, does not show keyboard', (WidgetTester tester) async { - final semantics = SemanticsTester(tester); + final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!; final focusNode = FocusNode(); addTearDown(focusNode.dispose); @@ -10632,14 +10547,16 @@ void main() { focusNode.requestFocus(); await tester.pumpAndSettle(); + final SemanticsNode node = tester.getSemantics(find.byType(CupertinoTextField)); + tester.testTextInput.log.clear(); expect(focusNode.hasFocus, isTrue); - semanticsOwner.performAction(4, SemanticsAction.focus); + semanticsOwner.performAction(node.id, SemanticsAction.focus); await tester.pumpAndSettle(); expect(focusNode.hasFocus, isTrue); expect(tester.testTextInput.log, isEmpty); - semantics.dispose(); + handle.dispose(); }, variant: TargetPlatformVariant.all(), ); From d14c4cbe7495755442ba59a8028dadf1760ea446 Mon Sep 17 00:00:00 2001 From: Renzo Olivares Date: Mon, 29 Jun 2026 23:49:26 +0000 Subject: [PATCH 3/7] Revert "[cupertino_ui] Update text selection toolbar utils helper" This reverts commit 089db4651803d57bd7fdb72fbceef1cb83f999ea. --- .../test/text_selection_toolbar_utils.dart | 182 ------------------ 1 file changed, 182 deletions(-) diff --git a/packages/cupertino_ui/test/text_selection_toolbar_utils.dart b/packages/cupertino_ui/test/text_selection_toolbar_utils.dart index 8f84e4cf8717..c17218c20946 100644 --- a/packages/cupertino_ui/test/text_selection_toolbar_utils.dart +++ b/packages/cupertino_ui/test/text_selection_toolbar_utils.dart @@ -3,12 +3,8 @@ // found in the LICENSE file. import 'package:cupertino_ui/cupertino_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/src/foundation/platform.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'editable_text_utils.dart'; - Finder findCupertinoOverflowNextButton() { return find.byWidgetPredicate((Widget widget) { return widget is CustomPaint && @@ -27,181 +23,3 @@ Future 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.byKey(StandardComponentType.moreButton.key); -} - -Finder findMaterialOverflowBackButton() { - return find.byKey(StandardComponentType.backButton.key); -} - -Future tapMaterialOverflowNextButton(WidgetTester tester) async { - await tester.tapAt(tester.getCenter(findMaterialOverflowNextButton())); - await tester.pumpAndSettle(); -} From 2e114b6e5cd4126b4197ae1b178ca49bac18cbf6 Mon Sep 17 00:00:00 2001 From: Renzo Olivares Date: Mon, 29 Jun 2026 23:59:37 +0000 Subject: [PATCH 4/7] Update toolbar utils --- .../test/text_selection_toolbar_utils.dart | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/packages/cupertino_ui/test/text_selection_toolbar_utils.dart b/packages/cupertino_ui/test/text_selection_toolbar_utils.dart index c17218c20946..7a78f4aa3c23 100644 --- a/packages/cupertino_ui/test/text_selection_toolbar_utils.dart +++ b/packages/cupertino_ui/test/text_selection_toolbar_utils.dart @@ -3,8 +3,11 @@ // found in the LICENSE file. import 'package:cupertino_ui/cupertino_ui.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'editable_text_utils.dart'; + Finder findCupertinoOverflowNextButton() { return find.byWidgetPredicate((Widget widget) { return widget is CustomPaint && @@ -23,3 +26,108 @@ Future 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); + } +} From 54a2e5d7805dbf3c189ec1c5d724bb899cc76155 Mon Sep 17 00:00:00 2001 From: Renzo Olivares Date: Tue, 30 Jun 2026 00:27:04 +0000 Subject: [PATCH 5/7] updates --- packages/cupertino_ui/test/text_field_test.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/cupertino_ui/test/text_field_test.dart b/packages/cupertino_ui/test/text_field_test.dart index a4091e8abd8c..a4e9a43c3a2a 100644 --- a/packages/cupertino_ui/test/text_field_test.dart +++ b/packages/cupertino_ui/test/text_field_test.dart @@ -552,6 +552,7 @@ void main() { hasFocusAction: true, hasDidGainAccessibilityFocusAction: true, hasDidLoseAccessibilityFocusAction: true, + textDirection: TextDirection.ltr, ), ); @@ -10461,6 +10462,8 @@ void main() { hasFocusAction: true, hasDidGainAccessibilityFocusAction: isDesktop, hasDidLoseAccessibilityFocusAction: isDesktop, + // TODO(gspencergoog): also test for the presence of SemanticsAction.focus when + // this iOS issue is addressed: https://github.com/flutter/flutter/issues/150030 ), ); From 39130d42019b2ed823879b76b60cbf115db322c1 Mon Sep 17 00:00:00 2001 From: Renzo Olivares Date: Tue, 30 Jun 2026 21:12:12 +0000 Subject: [PATCH 6/7] Address reviewer feedback --- .../cupertino_ui/test/text_field_test.dart | 228 ++++++++++++------ .../test/text_selection_toolbar_utils.dart | 105 -------- 2 files changed, 158 insertions(+), 175 deletions(-) diff --git a/packages/cupertino_ui/test/text_field_test.dart b/packages/cupertino_ui/test/text_field_test.dart index a4e9a43c3a2a..ddb0204552fa 100644 --- a/packages/cupertino_ui/test/text_field_test.dart +++ b/packages/cupertino_ui/test/text_field_test.dart @@ -26,7 +26,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'clipboard_utils.dart'; import 'editable_text_utils.dart'; import 'live_text_utils.dart'; -import 'text_selection_toolbar_utils.dart'; class MockTextSelectionControls extends TextSelectionControls { @override @@ -533,7 +532,6 @@ void main() { testWidgets('Activates the text field when receives semantics focus on desktops', ( WidgetTester tester, ) async { - final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!; final focusNode = FocusNode(); addTearDown(focusNode.dispose); @@ -563,7 +561,6 @@ void main() { semanticsOwner.performAction(node.id, SemanticsAction.didLoseAccessibilityFocus); await tester.pumpAndSettle(); expect(focusNode.hasFocus, isFalse); - handle.dispose(); }, variant: TargetPlatformVariant.desktop()); testWidgets('takes available space horizontally and takes intrinsic space vertically no-strut', ( @@ -1945,10 +1942,10 @@ void main() { // Toolbar shows on mobile. if (isTargetPlatformIOS) { - expectCupertinoToolbarForCollapsedSelection(); + _expectCupertinoToolbarForCollapsedSelection(); } else { // After a tap, macOS does not show a selection toolbar for a collapsed selection. - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); } }, variant: const TargetPlatformVariant({ @@ -2001,7 +1998,7 @@ void main() { // the selection has not changed we toggle the toolbar. expect(controller.selection.isCollapsed, true); expect(controller.selection.baseOffset, 35); - expectCupertinoToolbarForCollapsedSelection(); + _expectCupertinoToolbarForCollapsedSelection(); // Tap the 'v' position again to hide the toolbar. await tester.tapAt(vPos); @@ -2019,7 +2016,7 @@ void main() { expect(controller.selection.isCollapsed, true); expect(controller.selection.baseOffset, 46); expect(controller.selection.affinity, TextAffinity.upstream); - expectCupertinoToolbarForCollapsedSelection(); + _expectCupertinoToolbarForCollapsedSelection(); // Tap at the same position to toggle the toolbar. await tester.tapAt(endPos); @@ -2027,7 +2024,7 @@ void main() { expect(controller.selection.isCollapsed, true); expect(controller.selection.baseOffset, 46); expect(controller.selection.affinity, TextAffinity.upstream); - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); // 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, @@ -2037,7 +2034,7 @@ void main() { expect(controller.selection.isCollapsed, true); expect(controller.selection.baseOffset, 46); expect(controller.selection.affinity, TextAffinity.downstream); - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); }, variant: const TargetPlatformVariant({TargetPlatform.iOS})); testWidgets('Tapping on a non-collapsed selection toggles the toolbar and retains the selection', ( @@ -2077,7 +2074,7 @@ void main() { // Second tap selects the word around the cursor. expect(controller.selection, const TextSelection(baseOffset: 24, extentOffset: 35)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); // Tap the selected word to hide the toolbar and retain the selection. await tester.tapAt(vPos); @@ -2090,7 +2087,7 @@ void main() { await tester.pumpAndSettle(); expect(controller.selection, const TextSelection(baseOffset: 24, extentOffset: 35)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); // Tap past the selected word to move the cursor and hide the toolbar. await tester.tapAt(ePos); @@ -2135,7 +2132,7 @@ void main() { expect(controller.selection, const TextSelection(baseOffset: 8, extentOffset: 12)); // The toolbar now shows up. - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); // Tap somewhere else to move the cursor. await tester.tapAt(textOffsetToPosition(tester, index)); @@ -2176,7 +2173,7 @@ void main() { await tester.pumpAndSettle(); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 7)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, @@ -2314,15 +2311,13 @@ void main() { // Toolbar should re-appear after a drag. await gesture.up(); await tester.pump(); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); // Skip the magnifier hide animation, so it can release resources. await tester.pump(const Duration(milliseconds: 150)); }); testWidgets('Readonly text field does not have tap action', (WidgetTester tester) async { - final SemanticsHandle handle = tester.ensureSemantics(); - await tester.pumpWidget( const CupertinoApp(home: Center(child: CupertinoTextField(maxLength: 10, readOnly: true))), ); @@ -2331,8 +2326,6 @@ void main() { tester.getSemantics(find.byType(CupertinoTextField)), isSemantics(hasTapAction: false, hasFocusAction: true), ); - - handle.dispose(); }); testWidgets( @@ -2367,7 +2360,7 @@ void main() { // Second tap selects the word around the cursor. expect(controller.selection, const TextSelection(baseOffset: 8, extentOffset: 12)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, @@ -2396,14 +2389,14 @@ void main() { expect(controller.selection, const TextSelection(baseOffset: 8, extentOffset: 12)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); await gesture.up(); await tester.pump(); // Still selected. expect(controller.selection, const TextSelection(baseOffset: 8, extentOffset: 12)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); }, variant: TargetPlatformVariant.all()); testWidgets( @@ -2658,14 +2651,14 @@ void main() { expect(controller.selection, const TextSelection(baseOffset: 35, extentOffset: 35)); // The selection menu is not present. - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); await gesture.up(); await tester.pump(); // Still nothing selected and no selection menu. expect(controller.selection, const TextSelection(baseOffset: 35, extentOffset: 35)); - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); }); testWidgets('A read-only obscured CupertinoTextField is not selectable', ( @@ -2695,14 +2688,14 @@ void main() { expect(controller.selection, const TextSelection(baseOffset: 35, extentOffset: 35)); // The selection menu is not present. - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); await gesture.up(); await tester.pump(); // Still nothing selected and no selection menu. expect(controller.selection, const TextSelection.collapsed(offset: 35)); - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); }); testWidgets('An obscured CupertinoTextField is selectable by default', ( @@ -2812,7 +2805,7 @@ void main() { const TextSelection(baseOffset: 0, extentOffset: 7, affinity: TextAffinity.upstream), ); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); }, variant: TargetPlatformVariant.all( excluding: {TargetPlatform.iOS, TargetPlatform.macOS}, @@ -2844,7 +2837,7 @@ void main() { const TextSelection.collapsed(offset: 3, affinity: TextAffinity.upstream), ); - expectCupertinoToolbarForCollapsedSelection(); + _expectCupertinoToolbarForCollapsedSelection(); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, @@ -2871,7 +2864,7 @@ void main() { await tester.longPressAt(ePos); await tester.pumpAndSettle(const Duration(milliseconds: 50)); - expectCupertinoToolbarForCollapsedSelection(); + _expectCupertinoToolbarForCollapsedSelection(); expect(controller.selection.isCollapsed, isTrue); expect(controller.selection.baseOffset, 6); @@ -2888,7 +2881,7 @@ void main() { expect(controller.selection.baseOffset, 6); // The toolbar from the long press is now dismissed by the second tap. - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, @@ -2920,7 +2913,7 @@ void main() { const TextSelection(baseOffset: 0, extentOffset: 7, affinity: TextAffinity.upstream), ); // Toolbar only shows up on long press up. - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); await gesture.moveBy(const Offset(100, 0)); await tester.pump(); @@ -2930,7 +2923,7 @@ void main() { controller.selection, const TextSelection(baseOffset: 0, extentOffset: 12, affinity: TextAffinity.upstream), ); - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); await gesture.moveBy(const Offset(200, 0)); await tester.pump(); @@ -2940,7 +2933,7 @@ void main() { controller.selection, const TextSelection(baseOffset: 0, extentOffset: 23, affinity: TextAffinity.upstream), ); - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); await gesture.up(); await tester.pumpAndSettle(); @@ -2952,7 +2945,7 @@ void main() { ); // The toolbar now shows up. - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); }, variant: TargetPlatformVariant.all( excluding: {TargetPlatform.iOS, TargetPlatform.macOS}, @@ -2986,7 +2979,7 @@ void main() { const TextSelection.collapsed(offset: 3, affinity: TextAffinity.upstream), ); // Toolbar only shows up on long press up. - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); await gesture.moveBy(const Offset(50, 0)); await tester.pump(); @@ -2996,7 +2989,7 @@ void main() { controller.selection, const TextSelection.collapsed(offset: 6, affinity: TextAffinity.upstream), ); - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); await gesture.moveBy(const Offset(50, 0)); await tester.pump(); @@ -3006,7 +2999,7 @@ void main() { controller.selection, const TextSelection.collapsed(offset: 9, affinity: TextAffinity.upstream), ); - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); await gesture.up(); await tester.pumpAndSettle(); @@ -3017,7 +3010,7 @@ void main() { const TextSelection.collapsed(offset: 9, affinity: TextAffinity.upstream), ); // The toolbar now shows up. - expectCupertinoToolbarForCollapsedSelection(); + _expectCupertinoToolbarForCollapsedSelection(); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, @@ -3086,7 +3079,7 @@ void main() { ); // The toolbar now shows up. - expectCupertinoToolbarForFullSelection(); + _expectCupertinoToolbarForFullSelection(); lastCharEndpoint = renderEditable.getEndpointsForSelection( const TextSelection.collapsed(offset: 66), // Last character's position. @@ -3179,7 +3172,7 @@ void main() { const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream), ); // The toolbar now shows up. - expectCupertinoToolbarForCollapsedSelection(); + _expectCupertinoToolbarForCollapsedSelection(); lastCharEndpoint = renderEditable.getEndpointsForSelection( const TextSelection.collapsed(offset: 66), // Last character's position. @@ -3234,7 +3227,7 @@ void main() { expect(controller.selection, const TextSelection.collapsed(offset: 6)); // Long press toolbar. - expectCupertinoToolbarForCollapsedSelection(); + _expectCupertinoToolbarForCollapsedSelection(); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, @@ -3270,7 +3263,7 @@ void main() { expect(controller.selection.isCollapsed, isTrue); expect(controller.selection.baseOffset, 3); - expectCupertinoToolbarForCollapsedSelection(); + _expectCupertinoToolbarForCollapsedSelection(); await tester.tapAt(pPos); await tester.pump(const Duration(milliseconds: 50)); @@ -3285,7 +3278,7 @@ void main() { // Double tap selection. expect(controller.selection, const TextSelection(baseOffset: 8, extentOffset: 12)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, @@ -3313,7 +3306,7 @@ void main() { await tester.tapAt(textFieldStart + const Offset(50.0, 5.0)); await tester.pumpAndSettle(); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 7)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); // Double tap selecting the same word somewhere else is fine. await tester.tapAt(textFieldStart + const Offset(100.0, 5.0)); @@ -3327,7 +3320,7 @@ void main() { // tap is not detected as a triple tap. await tester.pumpAndSettle(kDoubleTapTimeout); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 7)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); await tester.tapAt(textFieldStart + const Offset(150.0, 5.0)); await tester.pump(const Duration(milliseconds: 50)); @@ -3340,7 +3333,7 @@ void main() { await tester.tapAt(textFieldStart + const Offset(150.0, 5.0)); await tester.pumpAndSettle(); expect(controller.selection, const TextSelection(baseOffset: 8, extentOffset: 12)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); }, variant: const TargetPlatformVariant({TargetPlatform.iOS})); group('Triple tap/click', () { @@ -3617,7 +3610,7 @@ void main() { await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.pump(); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 7)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.pumpAndSettle(); @@ -3628,19 +3621,19 @@ void main() { // First tap hides the toolbar and moves the selection. expect(controller.selection.isCollapsed, true); expect(controller.selection.baseOffset, 6); - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); // Second tap shows the toolbar and selects the word. await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.pump(); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 7)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); // Third tap shows the toolbar and selects the paragraph. await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.pumpAndSettle(); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 35)); - expectCupertinoToolbarForFullSelection(); + _expectCupertinoToolbarForFullSelection(); await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.pump(const Duration(milliseconds: 50)); @@ -3652,13 +3645,13 @@ void main() { await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.pump(); expect(controller.selection, const TextSelection(baseOffset: 8, extentOffset: 12)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); // Third tap selects the paragraph and shows the toolbar. await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.pumpAndSettle(); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 35)); - expectCupertinoToolbarForFullSelection(); + _expectCupertinoToolbarForFullSelection(); }, variant: const TargetPlatformVariant({ TargetPlatform.android, @@ -3689,7 +3682,7 @@ void main() { await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.pump(); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 7)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.pumpAndSettle(kDoubleTapTimeout); @@ -3706,13 +3699,13 @@ void main() { await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.pump(); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 7)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); // Third tap shows the toolbar and selects the paragraph. await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.pumpAndSettle(kDoubleTapTimeout); expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 36)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); await tester.tapAt(textfieldStart + const Offset(150.0, 25.0)); await tester.pump(const Duration(milliseconds: 50)); @@ -3727,13 +3720,13 @@ void main() { await tester.tapAt(textfieldStart + const Offset(150.0, 25.0)); await tester.pump(); expect(controller.selection, const TextSelection(baseOffset: 44, extentOffset: 50)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); // Third tap selects the paragraph and shows the toolbar. await tester.tapAt(textfieldStart + const Offset(150.0, 25.0)); await tester.pumpAndSettle(); expect(controller.selection, const TextSelection(baseOffset: 36, extentOffset: 66)); - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); }, variant: const TargetPlatformVariant({TargetPlatform.iOS})); testWidgets('triple click chains work', (WidgetTester tester) async { @@ -4586,7 +4579,7 @@ void main() { await gesture.up(); await tester.pumpAndSettle(); // Shows toolbar. - expectCupertinoToolbarForPartialSelection(); + _expectCupertinoToolbarForPartialSelection(); }); testWidgets( @@ -8899,7 +8892,7 @@ void main() { ); // Initially, the menu is not shown and there is no selection. - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); expect(controller.selection, const TextSelection(baseOffset: -1, extentOffset: -1)); final Offset secondBlah = textOffsetToPosition(tester, 8); @@ -8962,7 +8955,7 @@ void main() { ); // Initially, the menu is not shown and there is no selection. - expectNoCupertinoToolbar(); + _expectNoCupertinoToolbar(); expect(controller.selection, const TextSelection(baseOffset: -1, extentOffset: -1)); final Offset firstBlah = textOffsetToPosition(tester, 5); @@ -8975,7 +8968,7 @@ void main() { await tester.tapAt(firstBlah, kind: PointerDeviceKind.mouse, buttons: kSecondaryMouseButton); await tester.pumpAndSettle(); expect(controller.selection, const TextSelection.collapsed(offset: 5)); - expectCupertinoToolbarForCollapsedSelection(); + _expectCupertinoToolbarForCollapsedSelection(); // Press select all. await tester.tap(find.text('Select All'), kind: PointerDeviceKind.mouse); @@ -10438,7 +10431,6 @@ void main() { testWidgets('when enabled listens to onFocus events and gains focus', ( WidgetTester tester, ) async { - final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!; final focusNode = FocusNode(); addTearDown(focusNode.dispose); @@ -10471,13 +10463,11 @@ void main() { semanticsOwner.performAction(node.id, SemanticsAction.focus); await tester.pumpAndSettle(); expect(focusNode.hasFocus, isTrue); - handle.dispose(); }, variant: TargetPlatformVariant.all()); testWidgets('when disabled does not listen to onFocus events or gain focus', ( WidgetTester tester, ) async { - final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!; final focusNode = FocusNode(); addTearDown(focusNode.dispose); @@ -10511,13 +10501,11 @@ void main() { semanticsOwner.performAction(node.id, SemanticsAction.focus); await tester.pumpAndSettle(); expect(focusNode.hasFocus, isFalse); - handle.dispose(); }, variant: TargetPlatformVariant.all()); testWidgets('when receives SemanticsAction.focus while already focused, shows keyboard', ( WidgetTester tester, ) async { - final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!; final focusNode = FocusNode(); addTearDown(focusNode.dispose); @@ -10533,14 +10521,11 @@ void main() { await tester.pumpAndSettle(); expect(focusNode.hasFocus, isTrue); expect(tester.testTextInput.log.single.method, 'TextInput.show'); - - handle.dispose(); }, variant: TargetPlatformVariant.all()); testWidgets( 'when receives SemanticsAction.focus while focused but read-only, does not show keyboard', (WidgetTester tester) async { - final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!; final focusNode = FocusNode(); addTearDown(focusNode.dispose); @@ -10558,8 +10543,6 @@ void main() { await tester.pumpAndSettle(); expect(focusNode.hasFocus, isTrue); expect(tester.testTextInput.log, isEmpty); - - handle.dispose(); }, variant: TargetPlatformVariant.all(), ); @@ -10782,3 +10765,108 @@ void main() { expect(editableText.enableInlinePrediction, true); }); } + +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); + } +} diff --git a/packages/cupertino_ui/test/text_selection_toolbar_utils.dart b/packages/cupertino_ui/test/text_selection_toolbar_utils.dart index 7a78f4aa3c23..b52878468bb5 100644 --- a/packages/cupertino_ui/test/text_selection_toolbar_utils.dart +++ b/packages/cupertino_ui/test/text_selection_toolbar_utils.dart @@ -26,108 +26,3 @@ Future 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); - } -} From efa32cb18f130c77083bedd25a56bca50256fc3f Mon Sep 17 00:00:00 2001 From: Renzo Olivares Date: Tue, 30 Jun 2026 22:34:53 +0000 Subject: [PATCH 7/7] fix analyzer --- packages/cupertino_ui/test/text_selection_toolbar_utils.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/cupertino_ui/test/text_selection_toolbar_utils.dart b/packages/cupertino_ui/test/text_selection_toolbar_utils.dart index b52878468bb5..c17218c20946 100644 --- a/packages/cupertino_ui/test/text_selection_toolbar_utils.dart +++ b/packages/cupertino_ui/test/text_selection_toolbar_utils.dart @@ -3,11 +3,8 @@ // found in the LICENSE file. import 'package:cupertino_ui/cupertino_ui.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'editable_text_utils.dart'; - Finder findCupertinoOverflowNextButton() { return find.byWidgetPredicate((Widget widget) { return widget is CustomPaint &&