From 783672588b264518042db6933c088f7e107b2180 Mon Sep 17 00:00:00 2001 From: derdilla <82763757+NobodyForNothing@users.noreply.github.com> Date: Mon, 2 Oct 2023 04:41:59 +0200 Subject: [PATCH] Test cover cupertino text field for memory leaks. (#135804) --- .../test/cupertino/text_field_test.dart | 567 ++++++++++++------ 1 file changed, 371 insertions(+), 196 deletions(-) diff --git a/packages/flutter/test/cupertino/text_field_test.dart b/packages/flutter/test/cupertino/text_field_test.dart index fe4a90fc08..5750c548c9 100644 --- a/packages/flutter/test/cupertino/text_field_test.dart +++ b/packages/flutter/test/cupertino/text_field_test.dart @@ -17,6 +17,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import '../widgets/clipboard_utils.dart'; import '../widgets/editable_text_utils.dart' show OverflowWidgetTextEditingController; @@ -208,15 +209,17 @@ void main() { }); - testWidgets( + testWidgetsWithLeakTracking( 'Live Text button shows and hides correctly when LiveTextStatus changes', (WidgetTester tester) async { final LiveTextInputTester liveTextInputTester = LiveTextInputTester(); addTearDown(liveTextInputTester.dispose); final TextEditingController controller = TextEditingController(text: ''); + addTearDown(controller.dispose); const Key key = ValueKey('TextField'); final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); final Widget app = MaterialApp( theme: ThemeData(platform: TargetPlatform.iOS), home: Scaffold( @@ -250,7 +253,7 @@ void main() { }, ); - testWidgets('Look Up shows up on iOS only', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Look Up shows up on iOS only', (WidgetTester tester) async { String? lastLookUp; TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async { @@ -264,6 +267,7 @@ void main() { final TextEditingController controller = TextEditingController( text: 'Test', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -299,7 +303,7 @@ void main() { skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu. ); - testWidgets('Search Web shows up on iOS only', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Search Web shows up on iOS only', (WidgetTester tester) async { String? lastSearch; TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async { @@ -313,6 +317,7 @@ void main() { final TextEditingController controller = TextEditingController( text: 'Test', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -348,7 +353,7 @@ void main() { skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu. ); - testWidgets('Share shows up on iOS only', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Share shows up on iOS only', (WidgetTester tester) async { String? lastShare; TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async { @@ -362,6 +367,7 @@ void main() { final TextEditingController controller = TextEditingController( text: 'Test', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -397,10 +403,11 @@ void main() { skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu. ); - testWidgets('can use the desktop cut/copy/paste buttons on Mac', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can use the desktop cut/copy/paste buttons on Mac', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'blah1 blah2', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -472,11 +479,13 @@ void main() { skip: kIsWeb, // [intended] the web handles this on its own. ); - testWidgets('can get text selection color initially on desktop', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can get text selection color initially on desktop', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); final TextEditingController controller = TextEditingController( text: 'blah1 blah2', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -502,10 +511,11 @@ void main() { ); }); - testWidgets('Activates the text field when receives semantics focus on Mac, Windows', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Activates the text field when receives semantics focus on Mac, Windows', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!; final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); await tester.pumpWidget( CupertinoApp( home: CupertinoTextField(focusNode: focusNode), @@ -552,7 +562,7 @@ void main() { semantics.dispose(); }, variant: const TargetPlatformVariant({ TargetPlatform.macOS, TargetPlatform.windows })); - testWidgets( + testWidgetsWithLeakTracking( 'takes available space horizontally and takes intrinsic space vertically no-strut', (WidgetTester tester) async { await tester.pumpWidget( @@ -573,7 +583,7 @@ void main() { }, ); - testWidgets('sets cursorOpacityAnimates on EditableText correctly', (WidgetTester tester) async { + testWidgetsWithLeakTracking('sets cursorOpacityAnimates on EditableText correctly', (WidgetTester tester) async { // True @@ -598,7 +608,7 @@ void main() { expect(editableText.cursorOpacityAnimates, false); }); - testWidgets( + testWidgetsWithLeakTracking( 'takes available space horizontally and takes intrinsic space vertically', (WidgetTester tester) async { await tester.pumpWidget( @@ -619,7 +629,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'uses DefaultSelectionStyle for selection and cursor colors if provided', (WidgetTester tester) async { const Color selectionColor = Colors.black; @@ -645,11 +655,12 @@ void main() { }, ); - testWidgets('Text field drops selection color when losing focus', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Text field drops selection color when losing focus', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/103341. final Key key1 = UniqueKey(); final Key key2 = UniqueKey(); final TextEditingController controller1 = TextEditingController(); + addTearDown(controller1.dispose); const Color selectionColor = Colors.orange; const Color cursorColor = Colors.red; @@ -702,7 +713,7 @@ void main() { expect(state2.widget.selectionColor, selectionColor); }); - testWidgets( + testWidgetsWithLeakTracking( 'multi-lined text fields are intrinsically taller no-strut', (WidgetTester tester) async { await tester.pumpWidget( @@ -726,7 +737,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'multi-lined text fields are intrinsically taller', (WidgetTester tester) async { await tester.pumpWidget( @@ -747,7 +758,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'strut height override', (WidgetTester tester) async { await tester.pumpWidget( @@ -776,7 +787,7 @@ void main() { skip: isBrowser, // https://github.com/flutter/flutter/issues/32243 ); - testWidgets( + testWidgetsWithLeakTracking( 'strut forces field taller', (WidgetTester tester) async { await tester.pumpWidget( @@ -806,7 +817,7 @@ void main() { skip: isBrowser, // https://github.com/flutter/flutter/issues/32243 ); - testWidgets( + testWidgetsWithLeakTracking( 'default text field has a border', (WidgetTester tester) async { await tester.pumpWidget( @@ -861,7 +872,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'decoration can be overridden', (WidgetTester tester) async { await tester.pumpWidget( @@ -884,14 +895,16 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'text entries are padded by default', (WidgetTester tester) async { + final TextEditingController controller = TextEditingController(text: 'initial'); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoTextField( - controller: TextEditingController(text: 'initial'), + controller: controller, ), ), ), @@ -904,7 +917,7 @@ void main() { }, ); - testWidgets('iOS cursor has offset', (WidgetTester tester) async { + testWidgetsWithLeakTracking('iOS cursor has offset', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: CupertinoTextField(), @@ -915,7 +928,7 @@ void main() { expect(editableText.cursorOffset, const Offset(-2.0 / 3.0, 0)); }); - testWidgets('Cursor radius is 2.0', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Cursor radius is 2.0', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: CupertinoTextField(), @@ -928,7 +941,7 @@ void main() { expect(renderEditable.cursorRadius, const Radius.circular(2.0)); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('Cupertino cursor android golden', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Cupertino cursor android golden', (WidgetTester tester) async { final Widget widget = CupertinoApp( home: Center( child: RepaintBoundary( @@ -955,7 +968,7 @@ void main() { ); }); - testWidgets('Cupertino cursor golden', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Cupertino cursor golden', (WidgetTester tester) async { final Widget widget = CupertinoApp( home: Center( child: RepaintBoundary( @@ -984,10 +997,11 @@ void main() { ); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets( + testWidgetsWithLeakTracking( 'can control text content via controller', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -1011,7 +1025,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'placeholder respects textAlign', (WidgetTester tester) async { await tester.pumpWidget( @@ -1036,7 +1050,7 @@ void main() { }, ); - testWidgets('placeholder dark mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('placeholder dark mode', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( theme: CupertinoThemeData(brightness: Brightness.dark), @@ -1053,7 +1067,7 @@ void main() { expect(placeholder.style!.color!.value, CupertinoColors.placeholderText.darkColor.value); }); - testWidgets( + testWidgetsWithLeakTracking( 'placeholders are lightly colored and disappears once typing starts', (WidgetTester tester) async { await tester.pumpWidget( @@ -1076,7 +1090,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( "placeholderStyle modifies placeholder's style and doesn't affect text's style", (WidgetTester tester) async { await tester.pumpWidget( @@ -1110,17 +1124,20 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'prefix widget is in front of the text', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + final TextEditingController controller = TextEditingController(text: 'input'); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoTextField( focusNode: focusNode, prefix: const Icon(CupertinoIcons.add), - controller: TextEditingController(text: 'input'), + controller: controller, ), ), ), @@ -1140,7 +1157,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'prefix widget respects visibility mode', (WidgetTester tester) async { await tester.pumpWidget( @@ -1177,10 +1194,11 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'suffix widget is after the text', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -1206,7 +1224,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'suffix widget respects visibility mode', (WidgetTester tester) async { await tester.pumpWidget( @@ -1230,7 +1248,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'can customize padding', (WidgetTester tester) async { await tester.pumpWidget( @@ -1250,7 +1268,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'padding is in between prefix and suffix no-strut', (WidgetTester tester) async { await tester.pumpWidget( @@ -1308,7 +1326,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'padding is in between prefix and suffix', (WidgetTester tester) async { await tester.pumpWidget( @@ -1364,10 +1382,11 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'clear button shows with right visibility mode', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -1435,10 +1454,11 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'clear button removes text', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -1464,11 +1484,12 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'tapping clear button also calls onChanged when text not empty', (WidgetTester tester) async { String value = 'text entry'; final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -1494,10 +1515,11 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'clear button yields precedence to suffix', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -1533,7 +1555,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'font style controls intrinsic height no-strut', (WidgetTester tester) async { await tester.pumpWidget( @@ -1572,7 +1594,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'font style controls intrinsic height', (WidgetTester tester) async { await tester.pumpWidget( @@ -1608,7 +1630,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'RTL puts attachments to the right places', (WidgetTester tester) async { await tester.pumpWidget( @@ -1638,7 +1660,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'text fields with no max lines can grow no-strut', (WidgetTester tester) async { await tester.pumpWidget( @@ -1667,7 +1689,7 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'text fields with no max lines can grow', (WidgetTester tester) async { await tester.pumpWidget( @@ -1695,8 +1717,9 @@ void main() { }, ); - testWidgets('cannot enter new lines onto single line TextField', (WidgetTester tester) async { + testWidgetsWithLeakTracking('cannot enter new lines onto single line TextField', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -1713,10 +1736,11 @@ void main() { expect(controller.text, 'abcdef'); }); - testWidgets('toolbar colors change with theme brightness, but nothing else', (WidgetTester tester) async { + testWidgetsWithLeakTracking('toolbar colors change with theme brightness, but nothing else', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: "j'aime la poutine", ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -1775,10 +1799,11 @@ void main() { expect(text.style!.fontWeight, FontWeight.w400); }, skip: isContextMenuProvidedByPlatform); // [intended] only applies to platforms where we supply the context menu. - testWidgets('text field toolbar options correctly changes options on Apple Platforms', (WidgetTester tester) async { + testWidgetsWithLeakTracking('text field toolbar options correctly changes options on Apple Platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Column( @@ -1825,10 +1850,11 @@ void main() { skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu. ); - testWidgets('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( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Column( @@ -1880,8 +1906,9 @@ void main() { skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu. ); - testWidgets('Read only text field', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Read only text field', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(text: 'readonly'); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -1917,7 +1944,7 @@ void main() { expect(find.text('Cut'), findsNothing); }, skip: isContextMenuProvidedByPlatform); // [intended] only applies to platforms where we supply the context menu. - testWidgets('copy paste', (WidgetTester tester) async { + testWidgetsWithLeakTracking('copy paste', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Column( @@ -1970,12 +1997,13 @@ void main() { expect(Visibility.of(placeholder2Element), false); }, skip: isContextMenuProvidedByPlatform); // [intended] only applies to platforms where we supply the context menu. - testWidgets( + testWidgetsWithLeakTracking( 'tap moves cursor to the edge of the word it tapped on', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -2001,12 +2029,13 @@ void main() { expect(find.byType(CupertinoButton), findsNothing); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS })); - testWidgets( + testWidgetsWithLeakTracking( 'slow double tap does not trigger double tap', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); // On iOS/iPadOS, during a tap we select the edge of the word closest to the tap. // On macOS, we select the precise position of the tap. final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS; @@ -2035,12 +2064,13 @@ void main() { expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : isTargetPlatformIOS ? findsNWidgets(2) : findsNothing); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets( + testWidgetsWithLeakTracking( 'Tapping on a collapsed selection toggles the toolbar', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure Angrignon Peel Côte-des-Neigse Atwater Peel Sherbrooke Bonaventure Angrignon Peel Côte-des-Neiges', ); + addTearDown(controller.dispose); // On iOS/iPadOS, during a tap we select the edge of the word closest to the tap. await tester.pumpWidget( CupertinoApp( @@ -2120,12 +2150,13 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.iOS }), ); - testWidgets( + testWidgetsWithLeakTracking( 'Tapping on a non-collapsed selection toggles the toolbar and retains the selection', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); // On iOS/iPadOS, during a tap we select the edge of the word closest to the tap. await tester.pumpWidget( CupertinoApp( @@ -2194,12 +2225,13 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.iOS }), ); - testWidgets( + testWidgetsWithLeakTracking( 'double tap selects word for non-Apple platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -2249,12 +2281,13 @@ void main() { variant: TargetPlatformVariant.all(excluding: { TargetPlatform.iOS, TargetPlatform.macOS}), ); - testWidgets( + testWidgetsWithLeakTracking( 'double tap selects word for Apple platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -2308,12 +2341,13 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS }), ); - testWidgets( + testWidgetsWithLeakTracking( 'double tap does not select word on read-only obscured field', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -2346,10 +2380,11 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'Can double click + drag with a mouse to select word by word', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -2395,10 +2430,11 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'Can double tap + drag to select word by word', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -2450,9 +2486,14 @@ void main() { await tester.pump(); expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(4)); }, + // TODO(derdilla): remove after fixing and leak track other failing tests in + // this file. https://github.com/flutter/flutter/issues/135803 + leakTrackingTestConfig: const LeakTrackingTestConfig(notDisposedAllowList: + {'ValueNotifier<_OverlayEntryWidgetState?>': 1}) ); + // - testWidgets('Readonly text field does not have tap action', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Readonly text field does not have tap action', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget( @@ -2471,12 +2512,13 @@ void main() { semantics.dispose(); }); - testWidgets( + testWidgetsWithLeakTracking( 'double tap selects word and first tap of double tap moves cursor', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); // On iOS/iPadOS, during a tap we select the edge of the word closest to the tap. // On macOS, we select the precise position of the tap. final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS; @@ -2516,12 +2558,13 @@ void main() { expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : (isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3))); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets( + testWidgetsWithLeakTracking( 'double tap hold selects word', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -2575,12 +2618,13 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'tap after a double tap select is not affected', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); // On iOS/iPadOS, during a tap we select the edge of the word closest to the tap. // On macOS, we select the precise position of the tap. final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS; @@ -2620,10 +2664,11 @@ void main() { expect(find.byType(CupertinoButton), findsNothing); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('double tapping a space selects the previous word on iOS', (WidgetTester tester) async { + testWidgetsWithLeakTracking('double tapping a space selects the previous word on iOS', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: ' blah blah \n blah', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -2689,10 +2734,11 @@ void main() { expect(controller.value.selection.extentOffset, 14); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS })); - testWidgets('double tapping a space selects the space on Mac', (WidgetTester tester) async { + testWidgetsWithLeakTracking('double tapping a space selects the space on Mac', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: ' blah blah', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -2743,10 +2789,11 @@ void main() { expect(controller.value.selection.extentOffset, 1); }, variant: const TargetPlatformVariant({ TargetPlatform.macOS })); - testWidgets('double clicking a space selects the space on Mac', (WidgetTester tester) async { + testWidgetsWithLeakTracking('double clicking a space selects the space on Mac', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: ' blah blah', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -2810,12 +2857,13 @@ void main() { expect(controller.value.selection.extentOffset, 1); }, variant: const TargetPlatformVariant({ TargetPlatform.macOS })); - testWidgets( + testWidgetsWithLeakTracking( 'An obscured CupertinoTextField is not selectable when disabled', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -2858,12 +2906,13 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'A read-only obscured CupertinoTextField is not selectable', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -2906,12 +2955,13 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'An obscured CupertinoTextField is selectable by default', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -2953,10 +3003,11 @@ void main() { }, ); - testWidgets('An obscured TextField has correct default context menu', (WidgetTester tester) async { + testWidgetsWithLeakTracking('An obscured TextField has correct default context menu', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -2997,12 +3048,13 @@ void main() { expect(find.text('Cut'), findsNothing); }, skip: isContextMenuProvidedByPlatform); // [intended] only applies to platforms where we supply the context menu. - testWidgets( + testWidgetsWithLeakTracking( 'long press selects the word at the long press position and shows toolbar on non-Apple platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -3030,12 +3082,13 @@ void main() { variant: TargetPlatformVariant.all(excluding: { TargetPlatform.iOS, TargetPlatform.macOS }), ); - testWidgets( + testWidgetsWithLeakTracking( 'long press moves cursor to the exact long press position and shows toolbar on Apple platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -3074,12 +3127,13 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS }), ); - testWidgets( + testWidgetsWithLeakTracking( 'long press tap cannot initiate a double tap', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -3124,12 +3178,13 @@ void main() { expect(find.byType(CupertinoButton), findsNothing); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets( + testWidgetsWithLeakTracking( 'long press drag selects word by word and shows toolbar on lift on non-Apple platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -3188,12 +3243,13 @@ void main() { variant: TargetPlatformVariant.all(excluding: { TargetPlatform.iOS, TargetPlatform.macOS }), ); - testWidgets( + testWidgetsWithLeakTracking( 'long press drag on a focused TextField moves the cursor under the drag and shows toolbar on lift on Apple platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -3263,10 +3319,11 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS }), ); - testWidgets('long press drag can edge scroll on non-Apple platforms', (WidgetTester tester) async { + testWidgetsWithLeakTracking('long press drag can edge scroll on non-Apple platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure Angrignon Peel Côte-des-Neiges', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -3349,10 +3406,11 @@ void main() { expect(firstCharEndpoint[0].point.dx, moreOrLessEquals(-310.30, epsilon: 1)); }, variant: TargetPlatformVariant.all(excluding: { TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('long press drag can edge scroll on Apple platforms', (WidgetTester tester) async { + testWidgetsWithLeakTracking('long press drag can edge scroll on Apple platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure Angrignon Peel Côte-des-Neiges', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -3450,12 +3508,13 @@ void main() { expect(firstCharEndpoint[0].point.dx, moreOrLessEquals(-310.20, epsilon: 0.25)); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets( + testWidgetsWithLeakTracking( 'long tap after a double tap select is not affected', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); // On iOS/iPadOS, during a tap we select the edge of the word closest to the tap. // On macOS, we select the precise position of the tap. final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS; @@ -3502,12 +3561,13 @@ void main() { expect(find.byType(CupertinoButton), findsNWidgets(toolbarButtons)); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets( + testWidgetsWithLeakTracking( 'double tap after a long tap is not affected', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); // On iOS/iPadOS, during a tap we select the edge of the word closest to the tap. // On macOS, we select the precise position of the tap. final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS; @@ -3561,12 +3621,13 @@ void main() { expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : (isTargetPlatformIOS ? findsNWidgets(6) : findsNWidgets(3))); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets( + testWidgetsWithLeakTracking( 'double tap chains work', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -3640,11 +3701,12 @@ void main() { 'all good people\n' // 22 + 16 => 38 'to come to the aid\n' // 38 + 19 => 57 'of their country.'; // 57 + 17 => 74 - testWidgets( + testWidgetsWithLeakTracking( 'Can triple tap to select a paragraph on mobile platforms when tapping at a word edge', (WidgetTester tester) async { // TODO(Renzo-Olivares): Enable, currently broken because selection overlay blocks the TextSelectionGestureDetector. final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); final bool isTargetPlatformApple = defaultTargetPlatform == TargetPlatform.iOS; await tester.pumpWidget( @@ -3700,10 +3762,11 @@ void main() { skip: true, // https://github.com/flutter/flutter/issues/123415 ); - testWidgets( + testWidgetsWithLeakTracking( 'Can triple tap to select a paragraph on mobile platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); final bool isTargetPlatformApple = defaultTargetPlatform == TargetPlatform.iOS; await tester.pumpWidget( @@ -3758,11 +3821,12 @@ void main() { variant: TargetPlatformVariant.mobile(), ); - testWidgets( + testWidgetsWithLeakTracking( 'Triple click at the beginning of a line should not select the previous paragraph', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/132126 final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -3815,11 +3879,12 @@ void main() { variant: TargetPlatformVariant.all(excluding: { TargetPlatform.linux }), ); - testWidgets( + testWidgetsWithLeakTracking( 'Triple click at the end of text should select the previous paragraph', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/132126. final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -3873,12 +3938,13 @@ void main() { variant: TargetPlatformVariant.all(excluding: { TargetPlatform.linux }), ); - testWidgets( + testWidgetsWithLeakTracking( 'triple tap chains work on Non-Apple mobile platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -3963,12 +4029,13 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.android, TargetPlatform.fuchsia }), ); - testWidgets( + testWidgetsWithLeakTracking( 'triple tap chains work on Apple platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure\nThe fox jumped over the fence.', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -4063,12 +4130,13 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.iOS }), ); - testWidgets( + testWidgetsWithLeakTracking( 'triple click chains work', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: testValueA, ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -4180,12 +4248,13 @@ void main() { variant: TargetPlatformVariant.desktop(), ); - testWidgets( + testWidgetsWithLeakTracking( 'triple click after a click on desktop platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: testValueA, ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -4244,12 +4313,13 @@ void main() { variant: TargetPlatformVariant.desktop(), ); - testWidgets( + testWidgetsWithLeakTracking( 'Can triple tap to select all on a single-line textfield on mobile platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: testValueB, ); + addTearDown(controller.dispose); final bool isTargetPlatformApple = defaultTargetPlatform == TargetPlatform.iOS; await tester.pumpWidget( @@ -4299,12 +4369,13 @@ void main() { variant: TargetPlatformVariant.mobile(), ); - testWidgets( + testWidgetsWithLeakTracking( 'Can triple click to select all on a single-line textfield on desktop platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: testValueA, ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -4355,10 +4426,11 @@ void main() { variant: TargetPlatformVariant.desktop(), ); - testWidgets( + testWidgetsWithLeakTracking( 'Can triple click to select a line on Linux', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -4416,10 +4488,11 @@ void main() { variant: TargetPlatformVariant.only(TargetPlatform.linux), ); - testWidgets( + testWidgetsWithLeakTracking( 'Can triple click to select a paragraph', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -4477,10 +4550,11 @@ void main() { variant: TargetPlatformVariant.all(excluding: { TargetPlatform.linux }), ); - testWidgets( + testWidgetsWithLeakTracking( 'Can triple click + drag to select line by line on Linux', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -4579,10 +4653,11 @@ void main() { variant: TargetPlatformVariant.only(TargetPlatform.linux), ); - testWidgets( + testWidgetsWithLeakTracking( 'Can triple click + drag to select paragraph by paragraph', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -4681,12 +4756,13 @@ void main() { variant: TargetPlatformVariant.all(excluding: { TargetPlatform.linux }), ); - testWidgets( + testWidgetsWithLeakTracking( 'Going past triple click retains the selection on Apple platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: testValueA, ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -4768,12 +4844,13 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS }), ); - testWidgets( + testWidgetsWithLeakTracking( 'Tap count resets when going past a triple tap on Android, Fuchsia, and Linux', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: testValueA, ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -4882,12 +4959,13 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux }), ); - testWidgets( + testWidgetsWithLeakTracking( 'Double click and triple click alternate on Windows', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: testValueA, ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -5000,10 +5078,11 @@ void main() { ); }); - testWidgets('force press selects word', (WidgetTester tester) async { + testWidgetsWithLeakTracking('force press selects word', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -5055,10 +5134,11 @@ void main() { expect(find.byType(CupertinoButton), matchToolbarButtons); }); - testWidgets('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 { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); // On iOS/iPadOS, during a tap we select the edge of the word closest to the tap. // On macOS, we select the precise position of the tap. final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS; @@ -5098,10 +5178,11 @@ void main() { expect(find.byType(CupertinoButton), findsNothing); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('Cannot drag one handle past the other', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Cannot drag one handle past the other', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'abc def ghi', ); + addTearDown(controller.dispose); // On iOS/iPadOS, during a tap we select the edge of the word closest to the tap. // On macOS, we select the precise position of the tap. final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS; @@ -5165,11 +5246,12 @@ void main() { expect(controller.selection.extentOffset, 5); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS })); - testWidgets('Dragging between multiple lines keeps the contact point at the same place on the handle on Android', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Dragging between multiple lines keeps the contact point at the same place on the handle on Android', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( // 11 first line, 19 second line, 17 third line = length 49 text: 'a big house\njumped over a mouse\nOne more line yay', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -5363,11 +5445,12 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.android }), ); - testWidgets('Dragging between multiple lines keeps the contact point at the same place on the handle on iOS', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Dragging between multiple lines keeps the contact point at the same place on the handle on iOS', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( // 11 first line, 19 second line, 17 third line = length 49 text: 'a big house\njumped over a mouse\nOne more line yay', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -5552,8 +5635,9 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.iOS }), ); - testWidgets('Selection updates on tap down (Desktop platforms)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Selection updates on tap down (Desktop platforms)', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -5593,8 +5677,9 @@ void main() { variant: TargetPlatformVariant.desktop(), ); - testWidgets('Selection updates on tap up (Mobile platforms)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Selection updates on tap up (Mobile platforms)', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); final bool isTargetPlatformApple = defaultTargetPlatform == TargetPlatform.iOS; await tester.pumpWidget( @@ -5649,8 +5734,9 @@ void main() { variant: TargetPlatformVariant.mobile(), ); - testWidgets('Can select text by dragging with a mouse', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can select text by dragging with a mouse', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -5684,8 +5770,9 @@ void main() { expect(controller.selection.extentOffset, testValue.indexOf('g')); }); - testWidgets('Cursor should not move on a quick touch drag when touch does not begin on previous selection (iOS)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Cursor should not move on a quick touch drag when touch does not begin on previous selection (iOS)', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -5731,6 +5818,7 @@ void main() { testWidgets('Can move cursor when dragging, when tap is on collapsed selection (iOS)', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -5778,6 +5866,7 @@ void main() { testWidgets('Can move cursor when dragging, when tap is on collapsed selection (iOS) - multiline', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -5824,10 +5913,11 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.iOS }), ); - testWidgets('Can move cursor when dragging, when tap is on collapsed selection (iOS) - ListView', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can move cursor when dragging, when tap is on collapsed selection (iOS) - ListView', (WidgetTester tester) async { // This is a regression test for // https://github.com/flutter/flutter/issues/122519 final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -5902,6 +5992,7 @@ void main() { testWidgets('Can move cursor when dragging (Android)', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -5944,10 +6035,11 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.android, TargetPlatform.fuchsia }), ); - testWidgets('Continuous dragging does not cause flickering', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Continuous dragging does not cause flickering', (WidgetTester tester) async { int selectionChangedCount = 0; const String testValue = 'abc def ghi'; final TextEditingController controller = TextEditingController(text: testValue); + addTearDown(controller.dispose); controller.addListener(() { selectionChangedCount++; @@ -5996,10 +6088,11 @@ void main() { expect(controller.selection.extentOffset, 9); }); - testWidgets('Tap does not show handles nor toolbar', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Tap does not show handles nor toolbar', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'abc def ghi', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -6018,10 +6111,11 @@ void main() { expect(editableText.selectionOverlay!.toolbarIsVisible, isFalse); }); - testWidgets('Long press shows toolbar but not handles', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Long press shows toolbar but not handles', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'abc def ghi', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -6042,12 +6136,13 @@ void main() { expect(editableText.selectionOverlay!.toolbarIsVisible, isContextMenuProvidedByPlatform ? isFalse : isTrue); }); - testWidgets( + testWidgetsWithLeakTracking( 'Double tap shows handles and toolbar if selection is not collapsed', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'abc def ghi', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -6071,12 +6166,13 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'Double tap shows toolbar but not handles if selection is collapsed', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'abc def ghi', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -6100,12 +6196,13 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'Mouse long press does not show handles nor toolbar', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'abc def ghi', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -6131,12 +6228,13 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'Mouse double tap does not show handles nor toolbar', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'abc def ghi', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -6182,7 +6280,7 @@ void main() { }, ); - testWidgets('onTap is called upon tap', (WidgetTester tester) async { + testWidgetsWithLeakTracking('onTap is called upon tap', (WidgetTester tester) async { int tapCount = 0; await tester.pumpWidget( CupertinoApp( @@ -6211,7 +6309,7 @@ void main() { expect(tapCount, 2); }); - testWidgets( + testWidgetsWithLeakTracking( 'onTap does not work when the text field is disabled', (WidgetTester tester) async { int tapCount = 0; @@ -6268,8 +6366,9 @@ void main() { }, ); - testWidgets('Focus test when the text field is disabled', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Focus test when the text field is disabled', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -6305,7 +6404,7 @@ void main() { expect(focusNode.hasFocus, false); }); - testWidgets( + testWidgetsWithLeakTracking( 'text field respects theme', (WidgetTester tester) async { await tester.pumpWidget( @@ -6343,13 +6442,14 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'Check the toolbar appears below the TextField when there is not enough space above the TextField to show it', (WidgetTester tester) async { // This is a regression test for // https://github.com/flutter/flutter/issues/29808 const String testValue = 'abc def ghi'; final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -6414,7 +6514,7 @@ void main() { skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu. ); - testWidgets('text field respects keyboardAppearance from theme', (WidgetTester tester) async { + testWidgetsWithLeakTracking('text field respects keyboardAppearance from theme', (WidgetTester tester) async { final List log = []; tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.textInput, (MethodCall methodCall) async { log.add(methodCall); @@ -6438,7 +6538,7 @@ void main() { expect(((setClient.arguments as List).last as Map)['keyboardAppearance'], 'Brightness.dark'); }); - testWidgets('text field can override keyboardAppearance from theme', (WidgetTester tester) async { + testWidgetsWithLeakTracking('text field can override keyboardAppearance from theme', (WidgetTester tester) async { final List log = []; tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.textInput, (MethodCall methodCall) async { log.add(methodCall); @@ -6464,7 +6564,7 @@ void main() { expect(((setClient.arguments as List).last as Map)['keyboardAppearance'], 'Brightness.light'); }); - testWidgets('cursorColor respects theme', (WidgetTester tester) async { + testWidgetsWithLeakTracking('cursorColor respects theme', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: CupertinoTextField(), @@ -6506,7 +6606,7 @@ void main() { expect(renderEditable.cursorColor, const Color(0xFFF44336)); }); - testWidgets('cursor can override color from theme', (WidgetTester tester) async { + testWidgetsWithLeakTracking('cursor can override color from theme', (WidgetTester tester) async { const CupertinoDynamicColor cursorColor = CupertinoDynamicColor.withBrightness( color: Color(0x12345678), darkColor: Color(0x87654321), @@ -6541,9 +6641,10 @@ void main() { expect(editableText.cursorColor.value, 0x87654321); }); - testWidgets('shows selection handles', (WidgetTester tester) async { + testWidgetsWithLeakTracking('shows selection handles', (WidgetTester tester) async { const String testText = 'lorem ipsum'; final TextEditingController controller = TextEditingController(text: testText); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -6573,9 +6674,11 @@ void main() { expect(right.opacity.value, equals(1.0)); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('when CupertinoTextField would be blocked by keyboard, it is shown with enough space for the selection handle', (WidgetTester tester) async { + testWidgetsWithLeakTracking('when CupertinoTextField would be blocked by keyboard, it is shown with enough space for the selection handle', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget(CupertinoApp( theme: const CupertinoThemeData(), @@ -6601,7 +6704,9 @@ void main() { expect(scrollController.offset, 27.0); }); - testWidgets('disabled state golden', (WidgetTester tester) async { + testWidgetsWithLeakTracking('disabled state golden', (WidgetTester tester) async { + final TextEditingController controller = TextEditingController(text: 'lorem'); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: DecoratedBox( @@ -6613,7 +6718,7 @@ void main() { child: RepaintBoundary( key: const ValueKey(1), child: CupertinoTextField( - controller: TextEditingController(text: 'lorem'), + controller: controller, enabled: false, ), ), @@ -6629,13 +6734,16 @@ void main() { ); }); - testWidgets( + testWidgetsWithLeakTracking( 'Can drag the left handle while the right handle remains off-screen', (WidgetTester tester) async { // Text is longer than textfield width. const String testValue = 'aaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbb'; final TextEditingController controller = TextEditingController(text: testValue); + addTearDown(controller.dispose); final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); + await tester.pumpWidget( CupertinoApp( @@ -6712,13 +6820,15 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'Can drag the right handle while the left handle remains off-screen', (WidgetTester tester) async { // Text is longer than textfield width. const String testValue = 'aaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbb'; final TextEditingController controller = TextEditingController(text: testValue); + addTearDown(controller.dispose); final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( CupertinoApp( @@ -6785,7 +6895,7 @@ void main() { ); group('Text selection toolbar', () { - testWidgets('Collapsed selection works', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Collapsed selection works', (WidgetTester tester) async { tester.view.physicalSize = const Size(400, 400); tester.view.devicePixelRatio = 1; addTearDown(tester.view.reset); @@ -6858,6 +6968,7 @@ void main() { // Top Right collapsed selection. The toolbar should flip vertically, and // the arrow should not point exactly to the caret because the caret is // too close to the right. + controller.dispose(); controller = TextEditingController(text: List.filled(200, 'a').join()); await tester.pumpWidget( CupertinoApp( @@ -6918,7 +7029,9 @@ void main() { // Normal centered collapsed selection. The toolbar arrow should point down, and // it should point exactly to the caret. + controller.dispose(); controller = TextEditingController(text: List.filled(200, 'a').join()); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( debugShowCheckedModeBanner: false, @@ -6971,7 +7084,7 @@ void main() { ); }); - testWidgets('selecting multiple words works', (WidgetTester tester) async { + testWidgetsWithLeakTracking('selecting multiple words works', (WidgetTester tester) async { tester.view.physicalSize = const Size(400, 400); tester.view.devicePixelRatio = 1; addTearDown(tester.view.reset); @@ -6983,6 +7096,7 @@ void main() { // Normal multiword collapsed selection. The toolbar arrow should point down, and // it should point exactly to the caret. controller = TextEditingController(text: List.filled(20, 'a').join(' ')); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( debugShowCheckedModeBanner: false, @@ -7040,7 +7154,7 @@ void main() { ); }); - testWidgets('selecting multiline works', (WidgetTester tester) async { + testWidgetsWithLeakTracking('selecting multiline works', (WidgetTester tester) async { tester.view.physicalSize = const Size(400, 400); tester.view.devicePixelRatio = 1; addTearDown(tester.view.reset); @@ -7052,6 +7166,7 @@ void main() { // Normal multiline collapsed selection. The toolbar arrow should point down, and // it should point exactly to the horizontal center of the text field. controller = TextEditingController(text: List.filled(20, 'a a ').join('\n')); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( debugShowCheckedModeBanner: false, @@ -7115,11 +7230,12 @@ void main() { // This is a regression test for // https://github.com/flutter/flutter/issues/37046. - testWidgets('No exceptions when showing selection menu inside of nested Navigators', (WidgetTester tester) async { + testWidgetsWithLeakTracking('No exceptions when showing selection menu inside of nested Navigators', (WidgetTester tester) async { const String testValue = '123456'; final TextEditingController controller = TextEditingController( text: testValue, ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: CupertinoPageScaffold( @@ -7161,10 +7277,11 @@ void main() { expect(tester.takeException(), null); }); - testWidgets('Drag selection hides the selection menu', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Drag selection hides the selection menu', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'blah1 blah2', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -7211,8 +7328,9 @@ void main() { group('textAlignVertical position', () { group('simple case', () { - testWidgets('align top (default)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('align top (default)', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); const Size size = Size(200.0, 200.0); await tester.pumpWidget( CupertinoApp( @@ -7257,8 +7375,9 @@ void main() { expect(tester.getTopLeft(find.byType(EditableText)).dy, moreOrLessEquals(207.0, epsilon: .0001)); }); - testWidgets('align center', (WidgetTester tester) async { + testWidgetsWithLeakTracking('align center', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); const Size size = Size(200.0, 200.0); await tester.pumpWidget( CupertinoApp( @@ -7304,8 +7423,9 @@ void main() { expect(tester.getTopLeft(find.byType(EditableText)).dy, moreOrLessEquals(291.5, epsilon: .0001)); }); - testWidgets('align bottom', (WidgetTester tester) async { + testWidgetsWithLeakTracking('align bottom', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); const Size size = Size(200.0, 200.0); await tester.pumpWidget( CupertinoApp( @@ -7351,8 +7471,9 @@ void main() { expect(tester.getTopLeft(find.byType(EditableText)).dy, moreOrLessEquals(376.0, epsilon: .0001)); }); - testWidgets('align as a double', (WidgetTester tester) async { + testWidgetsWithLeakTracking('align as a double', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); const Size size = Size(200.0, 200.0); await tester.pumpWidget( CupertinoApp( @@ -7400,8 +7521,9 @@ void main() { }); group('tall prefix', () { - testWidgets('align center (default when prefix)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('align center (default when prefix)', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); const Size size = Size(200.0, 200.0); await tester.pumpWidget( CupertinoApp( @@ -7451,8 +7573,9 @@ void main() { expect(tester.getTopLeft(find.byType(EditableText)).dy, moreOrLessEquals(291.5, epsilon: .0001)); }); - testWidgets('align top', (WidgetTester tester) async { + testWidgetsWithLeakTracking('align top', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); const Size size = Size(200.0, 200.0); await tester.pumpWidget( CupertinoApp( @@ -7504,8 +7627,9 @@ void main() { expect(tester.getTopLeft(find.byType(EditableText)).dy, moreOrLessEquals(241.5, epsilon: .0001)); }); - testWidgets('align bottom', (WidgetTester tester) async { + testWidgetsWithLeakTracking('align bottom', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); const Size size = Size(200.0, 200.0); await tester.pumpWidget( CupertinoApp( @@ -7557,8 +7681,9 @@ void main() { expect(tester.getTopLeft(find.byType(EditableText)).dy, moreOrLessEquals(341.5, epsilon: .0001)); }); - testWidgets('align as a double', (WidgetTester tester) async { + testWidgetsWithLeakTracking('align as a double', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); const Size size = Size(200.0, 200.0); await tester.pumpWidget( CupertinoApp( @@ -7610,7 +7735,7 @@ void main() { }); }); - testWidgets( + testWidgetsWithLeakTracking( 'Long press on an autofocused field shows the selection menu', (WidgetTester tester) async { await tester.pumpWidget( @@ -7637,7 +7762,7 @@ void main() { ); }); - testWidgets("Arrow keys don't move input focus", (WidgetTester tester) async { + testWidgetsWithLeakTracking("Arrow keys don't move input focus", (WidgetTester tester) async { final TextEditingController controller1 = TextEditingController(); final TextEditingController controller2 = TextEditingController(); final TextEditingController controller3 = TextEditingController(); @@ -7648,6 +7773,16 @@ void main() { final FocusNode focusNode3 = FocusNode(debugLabel: 'Field 3'); final FocusNode focusNode4 = FocusNode(debugLabel: 'Field 4'); final FocusNode focusNode5 = FocusNode(debugLabel: 'Field 5'); + addTearDown(focusNode1.dispose); + addTearDown(focusNode2.dispose); + addTearDown(focusNode3.dispose); + addTearDown(focusNode4.dispose); + addTearDown(focusNode5.dispose); + addTearDown(controller1.dispose); + addTearDown(controller2.dispose); + addTearDown(controller3.dispose); + addTearDown(controller4.dispose); + addTearDown(controller5.dispose); // Lay out text fields in a "+" formation, and focus the center one. await tester.pumpWidget(CupertinoApp( @@ -7724,7 +7859,7 @@ void main() { expect(focusNode3.hasPrimaryFocus, isTrue); }, variant: KeySimulatorTransitModeVariant.all()); - testWidgets('Scrolling shortcuts are disabled in text fields', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrolling shortcuts are disabled in text fields', (WidgetTester tester) async { bool scrollInvoked = false; await tester.pumpWidget( CupertinoApp( @@ -7758,7 +7893,7 @@ void main() { expect(scrollInvoked, isFalse); }, variant: KeySimulatorTransitModeVariant.all()); - testWidgets('Cupertino text field semantics', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Cupertino text field semantics', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: Center( @@ -7786,7 +7921,7 @@ void main() { ); }); - testWidgets('Disabled Cupertino text field semantics', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Disabled Cupertino text field semantics', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: Center( @@ -7815,10 +7950,11 @@ void main() { ); }); - testWidgets('text selection style 1', (WidgetTester tester) async { + testWidgetsWithLeakTracking('text selection style 1', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure\nhi\nwassssup!', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -7869,10 +8005,11 @@ void main() { skip: kIsWeb, // [intended] the web has its own Select All. ); - testWidgets('text selection style 2', (WidgetTester tester) async { + testWidgetsWithLeakTracking('text selection style 2', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure\nhi\nwassssup!', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -7922,7 +8059,7 @@ void main() { skip: kIsWeb, // [intended] the web has its own Select All. ); - testWidgets('textSelectionControls is passed to EditableText', (WidgetTester tester) async { + testWidgetsWithLeakTracking('textSelectionControls is passed to EditableText', (WidgetTester tester) async { final MockTextSelectionControls selectionControl = MockTextSelectionControls(); await tester.pumpWidget( CupertinoApp( @@ -7938,7 +8075,7 @@ void main() { expect(widget.selectionControls, equals(selectionControl)); }); - testWidgets('Do not add LengthLimiting formatter to the user supplied list', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Do not add LengthLimiting formatter to the user supplied list', (WidgetTester tester) async { final List formatters = []; await tester.pumpWidget( @@ -7971,7 +8108,7 @@ void main() { await tester.pumpAndSettle(); } - testWidgets('using none enforcement.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('using none enforcement.', (WidgetTester tester) async { const MaxLengthEnforcement enforcement = MaxLengthEnforcement.none; await setupWidget(tester, enforcement); @@ -7991,7 +8128,7 @@ void main() { expect(state.currentTextEditingValue.composing, TextRange.empty); }); - testWidgets('using enforced.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('using enforced.', (WidgetTester tester) async { const MaxLengthEnforcement enforcement = MaxLengthEnforcement.enforced; await setupWidget(tester, enforcement); @@ -8015,7 +8152,7 @@ void main() { expect(state.currentTextEditingValue.composing, const TextRange(start: 3, end: 5)); }); - testWidgets('using truncateAfterCompositionEnds.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('using truncateAfterCompositionEnds.', (WidgetTester tester) async { const MaxLengthEnforcement enforcement = MaxLengthEnforcement.truncateAfterCompositionEnds; await setupWidget(tester, enforcement); @@ -8039,7 +8176,7 @@ void main() { expect(state.currentTextEditingValue.composing, TextRange.empty); }); - testWidgets('using default behavior for different platforms.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('using default behavior for different platforms.', (WidgetTester tester) async { await setupWidget(tester, null); final EditableTextState state = tester.state(find.byType(EditableText)); @@ -8072,7 +8209,7 @@ void main() { }); }); - testWidgets('disabled widget changes background color', (WidgetTester tester) async { + testWidgetsWithLeakTracking('disabled widget changes background color', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Center( @@ -8148,7 +8285,7 @@ void main() { }); // Regression test for https://github.com/flutter/flutter/issues/78097. - testWidgets( + testWidgetsWithLeakTracking( 'still gets disabled background color when decoration is null', (WidgetTester tester) async { await tester.pumpWidget( @@ -8172,7 +8309,7 @@ void main() { }, ); - testWidgets('autofill info has placeholder text', (WidgetTester tester) async { + testWidgetsWithLeakTracking('autofill info has placeholder text', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: CupertinoTextField( @@ -8188,7 +8325,7 @@ void main() { ); }); - testWidgets('textDirection is passed to EditableText', (WidgetTester tester) async { + testWidgetsWithLeakTracking('textDirection is passed to EditableText', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Center( @@ -8216,7 +8353,7 @@ void main() { expect(rtlWidget.textDirection, TextDirection.rtl); }); - testWidgets('clipBehavior has expected defaults', (WidgetTester tester) async { + testWidgetsWithLeakTracking('clipBehavior has expected defaults', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: CupertinoTextField( @@ -8228,7 +8365,9 @@ void main() { expect(textField.clipBehavior, Clip.hardEdge); }); - testWidgets('Overflow clipBehavior none golden', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Overflow clipBehavior none golden', (WidgetTester tester) async { + final OverflowWidgetTextEditingController controller = OverflowWidgetTextEditingController(); + addTearDown(controller.dispose); final Widget widget = CupertinoApp( home: RepaintBoundary( key: const ValueKey(1), @@ -8240,7 +8379,7 @@ void main() { // Make sure the input field is not high enough for the WidgetSpan. height: 50, child: CupertinoTextField( - controller: OverflowWidgetTextEditingController(), + controller: controller, clipBehavior: Clip.none, ), ), @@ -8262,10 +8401,11 @@ void main() { ); }); - testWidgets('can shift + tap to select with a keyboard (Apple platforms)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can shift + tap to select with a keyboard (Apple platforms)', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -8305,10 +8445,11 @@ void main() { expect(controller.selection.extentOffset, 4); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('can shift + tap to select with a keyboard (non-Apple platforms)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can shift + tap to select with a keyboard (non-Apple platforms)', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -8350,11 +8491,13 @@ void main() { expect(controller.selection.extentOffset, 4); }, variant: const TargetPlatformVariant({ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows })); - testWidgets('shift tapping an unfocused field', (WidgetTester tester) async { + testWidgetsWithLeakTracking('shift tapping an unfocused field', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -8404,10 +8547,11 @@ void main() { expect(controller.selection.extentOffset, 20); }, variant: TargetPlatformVariant.all()); - testWidgets('can shift + tap + drag to select with a keyboard (Apple platforms)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can shift + tap + drag to select with a keyboard (Apple platforms)', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS; await tester.pumpWidget( CupertinoApp( @@ -8510,10 +8654,11 @@ void main() { expect(controller.selection.extentOffset, 26); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('can shift + tap + drag to select with a keyboard (non-Apple platforms)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can shift + tap + drag to select with a keyboard (non-Apple platforms)', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.fuchsia; await tester.pumpWidget( @@ -8617,10 +8762,11 @@ void main() { expect(controller.selection.extentOffset, 26); }, variant: const TargetPlatformVariant({ TargetPlatform.linux, TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.windows })); - testWidgets('can shift + tap + drag to select with a keyboard, reversed (Apple platforms)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can shift + tap + drag to select with a keyboard, reversed (Apple platforms)', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); final bool isTargetPlatformIOS = defaultTargetPlatform == TargetPlatform.iOS; await tester.pumpWidget( CupertinoApp( @@ -8724,10 +8870,11 @@ void main() { expect(controller.selection.extentOffset, 14); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('can shift + tap + drag to select with a keyboard, reversed (non-Apple platforms)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can shift + tap + drag to select with a keyboard, reversed (non-Apple platforms)', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', ); + addTearDown(controller.dispose); final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.fuchsia; await tester.pumpWidget( @@ -8832,10 +8979,11 @@ void main() { }, variant: const TargetPlatformVariant({ TargetPlatform.linux, TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.windows })); // Regression test for https://github.com/flutter/flutter/issues/101587. - testWidgets('Right clicking menu behavior', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Right clicking menu behavior', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'blah1 blah2', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -8910,10 +9058,12 @@ void main() { ); group('Right click focus', () { - testWidgets('Can right click to focus multiple times', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can right click to focus multiple times', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/pull/103228 final FocusNode focusNode1 = FocusNode(); final FocusNode focusNode2 = FocusNode(); + addTearDown(focusNode1.dispose); + addTearDown(focusNode2.dispose); final UniqueKey key1 = UniqueKey(); final UniqueKey key2 = UniqueKey(); await tester.pumpWidget( @@ -8965,12 +9115,15 @@ void main() { expect(focusNode2.hasFocus, isFalse); }); - testWidgets('Can right click to focus on previously selected word on Apple platforms', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can right click to focus on previously selected word on Apple platforms', (WidgetTester tester) async { final FocusNode focusNode1 = FocusNode(); final FocusNode focusNode2 = FocusNode(); + addTearDown(focusNode1.dispose); + addTearDown(focusNode2.dispose); final TextEditingController controller = TextEditingController( text: 'first second', ); + addTearDown(controller.dispose); final UniqueKey key1 = UniqueKey(); await tester.pumpWidget( CupertinoApp( @@ -9065,8 +9218,9 @@ void main() { }); group('context menu', () { - testWidgets('builds CupertinoAdaptiveTextSelectionToolbar by default', (WidgetTester tester) async { + testWidgetsWithLeakTracking('builds CupertinoAdaptiveTextSelectionToolbar by default', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(text: ''); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Column( @@ -9095,9 +9249,10 @@ void main() { skip: kIsWeb, // [intended] on web the browser handles the context menu. ); - testWidgets('contextMenuBuilder is used in place of the default text selection toolbar', (WidgetTester tester) async { + testWidgetsWithLeakTracking('contextMenuBuilder is used in place of the default text selection toolbar', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); final TextEditingController controller = TextEditingController(text: ''); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Column( @@ -9138,7 +9293,7 @@ void main() { final Widget fakeMagnifier = Container(key: UniqueKey()); group('magnifier builder', () { - testWidgets('should build custom magnifier if given', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should build custom magnifier if given', (WidgetTester tester) async { final Widget customMagnifier = Container( key: UniqueKey(), ); @@ -9153,17 +9308,19 @@ void main() { final BuildContext context = tester.firstElement(find.byType(Placeholder)); + final ValueNotifier magnifierInfo = ValueNotifier(MagnifierInfo.empty); + addTearDown(magnifierInfo.dispose); expect( defaultCupertinoTextField.magnifierConfiguration!.magnifierBuilder( context, MagnifierController(), - ValueNotifier(MagnifierInfo.empty), + magnifierInfo, ), isA().having((Widget widget) => widget.key, 'key', equals(customMagnifier.key))); }); group('defaults', () { - testWidgets('should build CupertinoMagnifier on iOS and Android', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should build CupertinoMagnifier on iOS and Android', (WidgetTester tester) async { await tester.pumpWidget(const CupertinoApp( home: CupertinoTextField(), )); @@ -9171,11 +9328,13 @@ void main() { final BuildContext context = tester.firstElement(find.byType(CupertinoTextField)); final EditableText editableText = tester.widget(find.byType(EditableText)); + final ValueNotifier magnifierInfo = ValueNotifier(MagnifierInfo.empty); + addTearDown(magnifierInfo.dispose); expect( editableText.magnifierConfiguration.magnifierBuilder( context, MagnifierController(), - ValueNotifier(MagnifierInfo.empty), + magnifierInfo, ), isA()); }, @@ -9183,7 +9342,7 @@ void main() { {TargetPlatform.iOS, TargetPlatform.android})); }); - testWidgets('should build nothing on all platforms but iOS and Android', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should build nothing on all platforms but iOS and Android', (WidgetTester tester) async { await tester.pumpWidget(const CupertinoApp( home: CupertinoTextField(), )); @@ -9191,11 +9350,13 @@ void main() { final BuildContext context = tester.firstElement(find.byType(CupertinoTextField)); final EditableText editableText = tester.widget(find.byType(EditableText)); + final ValueNotifier magnifierInfo = ValueNotifier(MagnifierInfo.empty); + addTearDown(magnifierInfo.dispose); expect( editableText.magnifierConfiguration.magnifierBuilder( context, MagnifierController(), - ValueNotifier(MagnifierInfo.empty), + magnifierInfo, ), isNull); }, @@ -9203,9 +9364,10 @@ void main() { excluding: {TargetPlatform.iOS, TargetPlatform.android})); }); - testWidgets('Can drag handles to show, unshow, and update magnifier', + testWidgetsWithLeakTracking('Can drag handles to show, unshow, and update magnifier', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: CupertinoPageScaffold( @@ -9270,8 +9432,9 @@ void main() { expect(find.byKey(fakeMagnifier.key!), findsNothing); }, variant: TargetPlatformVariant.only(TargetPlatform.iOS)); - testWidgets('Can drag to show, unshow, and update magnifier', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can drag to show, unshow, and update magnifier', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -9369,8 +9532,9 @@ void main() { expect(find.byKey(fakeMagnifier.key!), findsNothing); }, variant: const TargetPlatformVariant({ TargetPlatform.android, TargetPlatform.iOS })); - testWidgets('Can long press to show, unshow, and update magnifier on non-Apple platforms', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can long press to show, unshow, and update magnifier on non-Apple platforms', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); final bool isTargetPlatformAndroid = defaultTargetPlatform == TargetPlatform.android; await tester.pumpWidget( CupertinoApp( @@ -9428,8 +9592,9 @@ void main() { expect(find.byKey(fakeMagnifier.key!), findsNothing); }, variant: const TargetPlatformVariant({ TargetPlatform.android })); - testWidgets('Can long press to show, unshow, and update magnifier on iOS', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can long press to show, unshow, and update magnifier on iOS', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); final bool isTargetPlatformAndroid = defaultTargetPlatform == TargetPlatform.android; await tester.pumpWidget( CupertinoApp( @@ -9490,8 +9655,9 @@ void main() { }); group('TapRegion integration', () { - testWidgets('Tapping outside loses focus on desktop', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Tapping outside loses focus on desktop', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(debugLabel: 'Test Node'); + addTearDown(focusNode.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -9516,8 +9682,9 @@ void main() { expect(focusNode.hasPrimaryFocus, isFalse); }, variant: TargetPlatformVariant.desktop()); - testWidgets("Tapping outside doesn't lose focus on mobile", (WidgetTester tester) async { + testWidgetsWithLeakTracking("Tapping outside doesn't lose focus on mobile", (WidgetTester tester) async { final FocusNode focusNode = FocusNode(debugLabel: 'Test Node'); + addTearDown(focusNode.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -9543,12 +9710,14 @@ void main() { expect(focusNode.hasPrimaryFocus, kIsWeb ? isFalse : isTrue); }, variant: TargetPlatformVariant.mobile()); - testWidgets("tapping on toolbar doesn't lose focus", (WidgetTester tester) async { + testWidgetsWithLeakTracking("tapping on toolbar doesn't lose focus", (WidgetTester tester) async { final TextEditingController controller; final EditableTextState state; controller = TextEditingController(text: 'A B C'); + addTearDown(controller.dispose); final FocusNode focusNode = FocusNode(debugLabel: 'Test Node'); + addTearDown(focusNode.dispose); await tester.pumpWidget( CupertinoApp( debugShowCheckedModeBanner: false, @@ -9602,9 +9771,10 @@ void main() { skip: kIsWeb, // [intended] The toolbar isn't rendered by Flutter on the web, it's rendered by the browser. ); - testWidgets("Tapping on border doesn't lose focus", + testWidgetsWithLeakTracking("Tapping on border doesn't lose focus", (WidgetTester tester) async { final FocusNode focusNode = FocusNode(debugLabel: 'Test Node'); + addTearDown(focusNode.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -9631,8 +9801,9 @@ void main() { }, variant: TargetPlatformVariant.all()); }); - testWidgets('Can drag handles to change selection correctly in multiline', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can drag handles to change selection correctly in multiline', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( @@ -9728,7 +9899,7 @@ void main() { } }); - testWidgets('placeholder style overflow works', (WidgetTester tester) async { + testWidgetsWithLeakTracking('placeholder style overflow works', (WidgetTester tester) async { final String placeholder = 'hint text' * 20; const TextStyle placeholderStyle = TextStyle( fontSize: 14.0, @@ -9752,7 +9923,7 @@ void main() { expect(placeholderWidget.style!.overflow, placeholderStyle.overflow); }); - testWidgets('tapping on a misspelled word on iOS hides the handles and shows red selection', (WidgetTester tester) async { + testWidgetsWithLeakTracking('tapping on a misspelled word on iOS hides the handles and shows red selection', (WidgetTester tester) async { tester.binding.platformDispatcher.nativeSpellCheckServiceDefinedTestValue = true; // The default derived color for the iOS text selection highlight. @@ -9760,6 +9931,7 @@ void main() { final TextEditingController controller = TextEditingController( text: 'test test testt', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -9825,10 +9997,11 @@ void main() { skip: kIsWeb, // [intended] ); - testWidgets('text selection toolbar is hidden on tap down', (WidgetTester tester) async { + testWidgetsWithLeakTracking('text selection toolbar is hidden on tap down', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'blah1 blah2', ); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -9870,9 +10043,10 @@ void main() { variant: TargetPlatformVariant.all(excluding: { TargetPlatform.iOS }), ); - testWidgets('Does not shrink in height when enters text when there is large single-line placeholder', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Does not shrink in height when enters text when there is large single-line placeholder', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/133241. final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Align( @@ -9894,8 +10068,9 @@ void main() { expect(rectWithPlaceholder, rectWithText); }); - testWidgets('Does not match the height of a multiline placeholder', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Does not match the height of a multiline placeholder', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Align(