diff --git a/packages/flutter/test/cupertino/dialog_test.dart b/packages/flutter/test/cupertino/dialog_test.dart index 7ae06059ca..4ac91602d8 100644 --- a/packages/flutter/test/cupertino/dialog_test.dart +++ b/packages/flutter/test/cupertino/dialog_test.dart @@ -15,11 +15,12 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import '../widgets/semantics_tester.dart'; void main() { - testWidgets('Alert dialog control test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Alert dialog control test', (WidgetTester tester) async { bool didDelete = false; await tester.pumpWidget( @@ -58,7 +59,7 @@ void main() { expect(find.text('Delete'), findsNothing); }); - testWidgets('Dialog not barrier dismissible by default', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Dialog not barrier dismissible by default', (WidgetTester tester) async { await tester.pumpWidget(createAppWithCenteredButton(const Text('Go'))); final BuildContext context = tester.element(find.text('Go')); @@ -86,7 +87,7 @@ void main() { }); - testWidgets('Dialog configurable to be barrier dismissible', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Dialog configurable to be barrier dismissible', (WidgetTester tester) async { await tester.pumpWidget(createAppWithCenteredButton(const Text('Go'))); final BuildContext context = tester.element(find.text('Go')); @@ -114,7 +115,7 @@ void main() { expect(find.text('Dialog'), findsNothing); }); - testWidgets('Dialog destructive action style', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Dialog destructive action style', (WidgetTester tester) async { await tester.pumpWidget(boilerplate(const CupertinoDialogAction( isDestructiveAction: true, child: Text('Ok'), @@ -125,7 +126,7 @@ void main() { expect(widget.style.color!.withAlpha(255), CupertinoColors.systemRed.color); }); - testWidgets('Dialog default action style', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Dialog default action style', (WidgetTester tester) async { await tester.pumpWidget( CupertinoTheme( data: const CupertinoThemeData( @@ -142,7 +143,7 @@ void main() { expect(widget.style.color!.withAlpha(255), CupertinoColors.systemGreen.color); }); - testWidgets('Dialog dark theme', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Dialog dark theme', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: MediaQuery( @@ -178,7 +179,7 @@ void main() { ); }); - testWidgets('Has semantic annotations', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Has semantic annotations', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget(const MaterialApp(home: Material( child: CupertinoAlertDialog( @@ -250,7 +251,7 @@ void main() { semantics.dispose(); }); - testWidgets('Dialog default action style', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Dialog default action style', (WidgetTester tester) async { await tester.pumpWidget(boilerplate(const CupertinoDialogAction( isDefaultAction: true, child: Text('Ok'), @@ -261,7 +262,7 @@ void main() { expect(widget.style.fontWeight, equals(FontWeight.w600)); }); - testWidgets('Dialog default and destructive action styles', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Dialog default and destructive action styles', (WidgetTester tester) async { await tester.pumpWidget(boilerplate(const CupertinoDialogAction( isDefaultAction: true, isDestructiveAction: true, @@ -274,7 +275,7 @@ void main() { expect(widget.style.fontWeight, equals(FontWeight.w600)); }); - testWidgets('Dialog disabled action style', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Dialog disabled action style', (WidgetTester tester) async { await tester.pumpWidget(boilerplate(const CupertinoDialogAction( child: Text('Ok'), ))); @@ -285,7 +286,7 @@ void main() { expect(widget.style.color!.opacity, lessThanOrEqualTo(128 / 255)); }); - testWidgets('Dialog enabled action style', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Dialog enabled action style', (WidgetTester tester) async { await tester.pumpWidget(boilerplate(CupertinoDialogAction( child: const Text('Ok'), onPressed: () {}, @@ -296,8 +297,9 @@ void main() { expect(widget.style.color!.opacity, equals(1.0)); }); - testWidgets('Message is scrollable, has correct padding with large text sizes', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Message is scrollable, has correct padding with large text sizes', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { @@ -352,8 +354,9 @@ void main() { expect(tester.getSize(find.widgetWithText(CupertinoDialogAction, 'OK')), equals(const Size(310.0, 98.0))); }); - testWidgets('Dialog respects small constraints.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Dialog respects small constraints.', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { @@ -397,8 +400,9 @@ void main() { ); }); - testWidgets('Button list is scrollable, has correct position with large text sizes.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Button list is scrollable, has correct position with large text sizes.', (WidgetTester tester) async { final ScrollController actionScrollController = ScrollController(); + addTearDown(actionScrollController.dispose); await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { @@ -457,9 +461,10 @@ void main() { expect(tester.getSize(find.widgetWithText(CupertinoDialogAction, 'Cancel')).height, equals(148.0)); }); - testWidgets('Title Section is empty, Button section is not empty.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Title Section is empty, Button section is not empty.', (WidgetTester tester) async { const double textScaleFactor = 1.0; final ScrollController actionScrollController = ScrollController(); + addTearDown(actionScrollController.dispose); await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { @@ -510,9 +515,10 @@ void main() { ); }); - testWidgets('Button section is empty, Title section is not empty.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Button section is empty, Title section is not empty.', (WidgetTester tester) async { const double textScaleFactor = 1.0; final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { @@ -551,8 +557,9 @@ void main() { ); }); - testWidgets('Actions section height for 1 button is height of button.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Actions section height for 1 button is height of button.', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { @@ -580,8 +587,9 @@ void main() { expect(okButtonBox.size.height, actionsSectionBox.size.height); }); - testWidgets('Actions section height for 2 side-by-side buttons is height of tallest button.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Actions section height for 2 side-by-side buttons is height of tallest button.', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); late double dividerWidth; // Will be set when the dialog builder runs. Needs a BuildContext. await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( @@ -625,8 +633,9 @@ void main() { ); }); - testWidgets('Actions section height for 2 stacked buttons with enough room is height of both buttons.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Actions section height for 2 stacked buttons with enough room is height of both buttons.', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); late double dividerThickness; // Will be set when the dialog builder runs. Needs a BuildContext. await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( @@ -667,8 +676,9 @@ void main() { ); }); - testWidgets('Actions section height for 2 stacked buttons without enough room and regular font is 1.5 buttons tall.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Actions section height for 2 stacked buttons without enough room and regular font is 1.5 buttons tall.', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { @@ -701,8 +711,9 @@ void main() { ); }); - testWidgets('Actions section height for 2 stacked buttons without enough room and large accessibility font is 50% of dialog height.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Actions section height for 2 stacked buttons without enough room and large accessibility font is 50% of dialog height.', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { @@ -741,8 +752,9 @@ void main() { ); }); - testWidgets('Actions section height for 3 buttons without enough room is 1.5 buttons tall.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Actions section height for 3 buttons without enough room is 1.5 buttons tall.', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { @@ -785,8 +797,9 @@ void main() { ); }); - testWidgets('Actions section overscroll is painted white.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Actions section overscroll is painted white.', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { @@ -833,8 +846,9 @@ void main() { )); }); - testWidgets('Pressed button changes appearance and dividers disappear.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Pressed button changes appearance and dividers disappear.', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); late double dividerThickness; // Will be set when the dialog builder runs. Needs a BuildContext. await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( @@ -946,7 +960,7 @@ void main() { await gesture.up(); }); - testWidgets('ScaleTransition animation for showCupertinoDialog()', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ScaleTransition animation for showCupertinoDialog()', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: Center( @@ -1024,7 +1038,7 @@ void main() { expect(find.byType(Transform), findsNothing); }); - testWidgets('FadeTransition animation for showCupertinoDialog()', (WidgetTester tester) async { + testWidgetsWithLeakTracking('FadeTransition animation for showCupertinoDialog()', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: Center( @@ -1117,7 +1131,7 @@ void main() { expect(transition.opacity.value, moreOrLessEquals(0.0, epsilon: 0.001)); }); - testWidgets('Actions are accessible by key', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Actions are accessible by key', (WidgetTester tester) async { await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { @@ -1147,7 +1161,7 @@ void main() { expect(find.byKey(const Key('option_3')), findsNothing); }); - testWidgets('Dialog widget insets by MediaQuery viewInsets', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Dialog widget insets by MediaQuery viewInsets', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( home: MediaQuery( @@ -1177,7 +1191,7 @@ void main() { expect(tester.getRect(find.byType(Placeholder)), placeholderRectWithoutInsets.translate(10, 10)); }); - testWidgets('Material2 - Default cupertino dialog golden', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Material2 - Default cupertino dialog golden', (WidgetTester tester) async { await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( useMaterial3: false, @@ -1208,7 +1222,7 @@ void main() { ); }); - testWidgets('Material3 - Default cupertino dialog golden', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Material3 - Default cupertino dialog golden', (WidgetTester tester) async { await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( useMaterial3: true, @@ -1239,7 +1253,7 @@ void main() { ); }); - testWidgets('showCupertinoDialog - custom barrierLabel', (WidgetTester tester) async { + testWidgetsWithLeakTracking('showCupertinoDialog - custom barrierLabel', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget( @@ -1279,7 +1293,7 @@ void main() { semantics.dispose(); }); - testWidgets('CupertinoDialogRoute is state restorable', (WidgetTester tester) async { + testWidgetsWithLeakTracking('CupertinoDialogRoute is state restorable', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( restorationScopeId: 'app', @@ -1309,10 +1323,11 @@ void main() { expect(find.byType(CupertinoAlertDialog), findsOneWidget); }, skip: isBrowser); // https://github.com/flutter/flutter/issues/33615 - testWidgets('Conflicting scrollbars are not applied by ScrollBehavior to CupertinoAlertDialog', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Conflicting scrollbars are not applied by ScrollBehavior to CupertinoAlertDialog', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/83819 const double textScaleFactor = 1.0; final ScrollController actionScrollController = ScrollController(); + addTearDown(actionScrollController.dispose); await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { @@ -1348,7 +1363,7 @@ void main() { expect(find.byType(CupertinoScrollbar), findsNWidgets(2)); }, variant: TargetPlatformVariant.all()); - testWidgets('CupertinoAlertDialog scrollbars controllers should be different', (WidgetTester tester) async { + testWidgetsWithLeakTracking('CupertinoAlertDialog scrollbars controllers should be different', (WidgetTester tester) async { // https://github.com/flutter/flutter/pull/81278 await tester.pumpWidget( const MaterialApp( @@ -1375,7 +1390,7 @@ void main() { }); group('showCupertinoDialog avoids overlapping display features', () { - testWidgets('positioning using anchorPoint', (WidgetTester tester) async { + testWidgetsWithLeakTracking('positioning using anchorPoint', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( builder: (BuildContext context, Widget? child) { @@ -1413,7 +1428,7 @@ void main() { expect(tester.getBottomRight(find.byType(Placeholder)), const Offset(800.0, 600.0)); }); - testWidgets('positioning using Directionality', (WidgetTester tester) async { + testWidgetsWithLeakTracking('positioning using Directionality', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( builder: (BuildContext context, Widget? child) { @@ -1453,7 +1468,7 @@ void main() { expect(tester.getBottomRight(find.byType(Placeholder)), const Offset(800.0, 600.0)); }); - testWidgets('default positioning', (WidgetTester tester) async { + testWidgetsWithLeakTracking('default positioning', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( builder: (BuildContext context, Widget? child) { @@ -1491,7 +1506,7 @@ void main() { }); }); - testWidgets('Hovering over Cupertino alert dialog action updates cursor to clickable on Web', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hovering over Cupertino alert dialog action updates cursor to clickable on Web', (WidgetTester tester) async { await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { diff --git a/packages/flutter/test/cupertino/magnifier_test.dart b/packages/flutter/test/cupertino/magnifier_test.dart index ffa160d035..87c22f1b96 100644 --- a/packages/flutter/test/cupertino/magnifier_test.dart +++ b/packages/flutter/test/cupertino/magnifier_test.dart @@ -8,6 +8,7 @@ library; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { final Offset basicOffset = Offset(CupertinoMagnifier.kDefaultSize.width / 2, @@ -48,7 +49,7 @@ void main() { animatedPositioned.left ?? 0, animatedPositioned.top ?? 0); } - testWidgets('should be at gesture position if does not violate any positioning rules', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should be at gesture position if does not violate any positioning rules', (WidgetTester tester) async { final Key fakeTextFieldKey = UniqueKey(); final Key outerKey = UniqueKey(); @@ -87,6 +88,7 @@ void main() { globalGesturePosition: fakeTextFieldRect.center, ), ); + addTearDown(magnifier.dispose); await showCupertinoMagnifier(context, tester, magnifier); @@ -98,7 +100,7 @@ void main() { ); }); - testWidgets('should never horizontally be outside of Screen Padding', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should never horizontally be outside of Screen Padding', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( color: Color.fromARGB(7, 0, 129, 90), @@ -108,19 +110,21 @@ void main() { final BuildContext context = tester.firstElement(find.byType(Placeholder)); + final ValueNotifier magnifierInfo = ValueNotifier( + MagnifierInfo( + currentLineBoundaries: reasonableTextField, + fieldBounds: reasonableTextField, + caretRect: reasonableTextField, + // The tap position is far out of the right side of the app. + globalGesturePosition: + Offset(MediaQuery.sizeOf(context).width + 100, 0), + ), + ); + addTearDown(magnifierInfo.dispose); await showCupertinoMagnifier( context, tester, - ValueNotifier( - MagnifierInfo( - currentLineBoundaries: reasonableTextField, - fieldBounds: reasonableTextField, - caretRect: reasonableTextField, - // The tap position is far out of the right side of the app. - globalGesturePosition: - Offset(MediaQuery.sizeOf(context).width + 100, 0), - ), - ), + magnifierInfo, ); // Should be less than the right edge, since we have padding. @@ -128,7 +132,7 @@ void main() { lessThan(MediaQuery.sizeOf(context).width)); }); - testWidgets('should have some vertical drag', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should have some vertical drag', (WidgetTester tester) async { final double dragPositionBelowTextField = reasonableTextField.center.dy + 30; await tester.pumpWidget( @@ -141,20 +145,23 @@ void main() { final BuildContext context = tester.firstElement(find.byType(Placeholder)); + final ValueNotifier magnifierInfo = + ValueNotifier( + MagnifierInfo( + currentLineBoundaries: reasonableTextField, + fieldBounds: reasonableTextField, + caretRect: reasonableTextField, + // The tap position is dragBelow units below the text field. + globalGesturePosition: Offset( + MediaQuery.sizeOf(context).width / 2, + dragPositionBelowTextField), + ), + ); + addTearDown(magnifierInfo.dispose); await showCupertinoMagnifier( context, tester, - ValueNotifier( - MagnifierInfo( - currentLineBoundaries: reasonableTextField, - fieldBounds: reasonableTextField, - caretRect: reasonableTextField, - // The tap position is dragBelow units below the text field. - globalGesturePosition: Offset( - MediaQuery.sizeOf(context).width / 2, - dragPositionBelowTextField), - ), - ), + magnifierInfo, ); // The magnifier Y should be greater than the text field, since we "dragged" it down. @@ -166,7 +173,7 @@ void main() { }); group('status', () { - testWidgets('should hide if gesture is far below the text field', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should hide if gesture is far below the text field', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( color: Color.fromARGB(7, 0, 129, 90), @@ -177,7 +184,7 @@ void main() { final BuildContext context = tester.firstElement(find.byType(Placeholder)); - final ValueNotifier magnifierinfo = + final ValueNotifier magnifierInfo = ValueNotifier( MagnifierInfo( currentLineBoundaries: reasonableTextField, @@ -188,16 +195,17 @@ void main() { MediaQuery.sizeOf(context).width / 2, reasonableTextField.top), ), ); + addTearDown(magnifierInfo.dispose); // Show the magnifier initially, so that we get it in a not hidden state. - await showCupertinoMagnifier(context, tester, magnifierinfo); + await showCupertinoMagnifier(context, tester, magnifierInfo); // Move the gesture to one that should hide it. - magnifierinfo.value = MagnifierInfo( + magnifierInfo.value = MagnifierInfo( currentLineBoundaries: reasonableTextField, fieldBounds: reasonableTextField, caretRect: reasonableTextField, - globalGesturePosition: magnifierinfo.value.globalGesturePosition + const Offset(0, 100), + globalGesturePosition: magnifierInfo.value.globalGesturePosition + const Offset(0, 100), ); await tester.pumpAndSettle(); @@ -205,7 +213,7 @@ void main() { expect(magnifierController.overlayEntry, isNotNull); }); - testWidgets('should re-show if gesture moves back up', + testWidgetsWithLeakTracking('should re-show if gesture moves back up', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( @@ -227,6 +235,7 @@ void main() { globalGesturePosition: Offset(MediaQuery.sizeOf(context).width / 2, reasonableTextField.top), ), ); + addTearDown(magnifierInfo.dispose); // Show the magnifier initially, so that we get it in a not hidden state. await showCupertinoMagnifier(context, tester, magnifierInfo); diff --git a/packages/flutter/test/cupertino/nav_bar_transition_test.dart b/packages/flutter/test/cupertino/nav_bar_transition_test.dart index 662fcb5300..97205e3e1c 100644 --- a/packages/flutter/test/cupertino/nav_bar_transition_test.dart +++ b/packages/flutter/test/cupertino/nav_bar_transition_test.dart @@ -11,6 +11,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; Future startTransitionBetween( WidgetTester tester, { @@ -131,7 +132,7 @@ void checkOpacity(WidgetTester tester, Finder finder, double opacity) { } void main() { - testWidgets('Bottom middle moves between middle and back label', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Bottom middle moves between middle and back label', (WidgetTester tester) async { await startTransitionBetween(tester, fromTitle: 'Page 1'); // Be mid-transition. @@ -159,7 +160,7 @@ void main() { ); }); - testWidgets('Bottom middle moves between middle and back label RTL', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Bottom middle moves between middle and back label RTL', (WidgetTester tester) async { await startTransitionBetween( tester, fromTitle: 'Page 1', @@ -186,7 +187,7 @@ void main() { ); }); - testWidgets('Bottom middle never changes size during the animation', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Bottom middle never changes size during the animation', (WidgetTester tester) async { await tester.binding.setSurfaceSize(const Size(1080.0 / 2.75, 600)); addTearDown(() async { await tester.binding.setSurfaceSize(const Size(800.0, 600.0)); @@ -207,7 +208,7 @@ void main() { } }); - testWidgets('Bottom middle and top back label transitions their font', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Bottom middle and top back label transitions their font', (WidgetTester tester) async { await startTransitionBetween(tester, fromTitle: 'Page 1'); // Be mid-transition. @@ -251,7 +252,7 @@ void main() { checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.5292819738388062); }); - testWidgets('Font transitions respect themes', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Font transitions respect themes', (WidgetTester tester) async { await startTransitionBetween( tester, fromTitle: 'Page 1', @@ -299,7 +300,7 @@ void main() { checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.5292819738388062); }); - testWidgets('Fullscreen dialogs do not create heroes', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Fullscreen dialogs do not create heroes', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Placeholder(), @@ -333,7 +334,7 @@ void main() { expect(() => flying(tester, find.text('Page 2')), throwsAssertionError); }); - testWidgets('Turning off transition works', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Turning off transition works', (WidgetTester tester) async { await startTransitionBetween( tester, from: const CupertinoNavigationBar( @@ -357,7 +358,7 @@ void main() { expect(() => flying(tester, find.text('Page 2')), throwsAssertionError); }); - testWidgets('Popping mid-transition is symmetrical', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Popping mid-transition is symmetrical', (WidgetTester tester) async { await startTransitionBetween(tester, fromTitle: 'Page 1'); // Be mid-transition. @@ -405,7 +406,7 @@ void main() { checkColorAndPositionAt50ms(); }); - testWidgets('Popping mid-transition is symmetrical RTL', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Popping mid-transition is symmetrical RTL', (WidgetTester tester) async { await startTransitionBetween( tester, fromTitle: 'Page 1', @@ -456,7 +457,7 @@ void main() { checkColorAndPositionAt50ms(); }); - testWidgets('There should be no global keys in the hero flight', (WidgetTester tester) async { + testWidgetsWithLeakTracking('There should be no global keys in the hero flight', (WidgetTester tester) async { await startTransitionBetween(tester, fromTitle: 'Page 1'); // Be mid-transition. @@ -471,7 +472,7 @@ void main() { ); }); - testWidgets('DartPerformanceMode is latency mid-animation', (WidgetTester tester) async { + testWidgetsWithLeakTracking('DartPerformanceMode is latency mid-animation', (WidgetTester tester) async { DartPerformanceMode? mode; // before the animation starts, no requests are active. @@ -491,7 +492,7 @@ void main() { expect(mode, isNull); }); - testWidgets('Multiple nav bars tags do not conflict if in different navigators', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Multiple nav bars tags do not conflict if in different navigators', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: CupertinoTabScaffold( @@ -566,7 +567,7 @@ void main() { expect(find.text('Tab 1 Page 2', skipOffstage: false), findsNothing); }); - testWidgets('Transition box grows to large title size', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Transition box grows to large title size', (WidgetTester tester) async { await startTransitionBetween( tester, fromTitle: 'Page 1', @@ -590,7 +591,7 @@ void main() { checkBackgroundBoxHeight(tester, 84.33018499612808); }); - testWidgets('Large transition box shrinks to standard nav bar size', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Large transition box shrinks to standard nav bar size', (WidgetTester tester) async { await startTransitionBetween( tester, from: const CupertinoSliverNavigationBar(), @@ -614,7 +615,7 @@ void main() { checkBackgroundBoxHeight(tester, 55.66981500387192); }); - testWidgets('Hero flight removed at the end of page transition', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hero flight removed at the end of page transition', (WidgetTester tester) async { await startTransitionBetween(tester, fromTitle: 'Page 1'); await tester.pump(const Duration(milliseconds: 50)); @@ -629,7 +630,7 @@ void main() { expect(() => flying(tester, find.text('Page 1')), throwsAssertionError); }); - testWidgets('Exact widget is reused to build inside the transition', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Exact widget is reused to build inside the transition', (WidgetTester tester) async { const Widget userMiddle = Placeholder(); await startTransitionBetween( tester, @@ -645,7 +646,7 @@ void main() { expect(flying(tester, find.byWidget(userMiddle)), findsOneWidget); }); - testWidgets('Middle is not shown if alwaysShowMiddle is false and the nav bar is expanded', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Middle is not shown if alwaysShowMiddle is false and the nav bar is expanded', (WidgetTester tester) async { const Widget userMiddle = Placeholder(); await startTransitionBetween( tester, @@ -662,9 +663,10 @@ void main() { expect(flying(tester, find.byWidget(userMiddle)), findsNothing); }); - testWidgets('Middle is shown if alwaysShowMiddle is false but the nav bar is collapsed', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Middle is shown if alwaysShowMiddle is false but the nav bar is collapsed', (WidgetTester tester) async { const Widget userMiddle = Placeholder(); final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( CupertinoApp( @@ -710,7 +712,7 @@ void main() { expect(flying(tester, find.byWidget(userMiddle)), findsOneWidget); }); - testWidgets('First appearance of back chevron fades in from the right', (WidgetTester tester) async { + testWidgetsWithLeakTracking('First appearance of back chevron fades in from the right', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: scaffoldForNavBar(null), @@ -749,7 +751,7 @@ void main() { )); }); - testWidgets('First appearance of back chevron fades in from the left in RTL', (WidgetTester tester) async { + testWidgetsWithLeakTracking('First appearance of back chevron fades in from the left in RTL', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( builder: (BuildContext context, Widget? navigator) { @@ -801,7 +803,7 @@ void main() { ); }); - testWidgets('Back chevron fades out and in when both pages have it', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Back chevron fades out and in when both pages have it', (WidgetTester tester) async { await startTransitionBetween(tester, fromTitle: 'Page 1'); await tester.pump(const Duration(milliseconds: 50)); @@ -827,7 +829,7 @@ void main() { expect(tester.getTopLeft(backChevrons.last), const Offset(14.0, 7.0)); }); - testWidgets('Bottom middle just fades if top page has a custom leading', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Bottom middle just fades if top page has a custom leading', (WidgetTester tester) async { await startTransitionBetween( tester, fromTitle: 'Page 1', @@ -858,7 +860,7 @@ void main() { ); }); - testWidgets('Bottom leading fades in place', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Bottom leading fades in place', (WidgetTester tester) async { await startTransitionBetween( tester, from: const CupertinoSliverNavigationBar(leading: Text('custom')), @@ -884,7 +886,7 @@ void main() { ); }); - testWidgets('Bottom trailing fades in place', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Bottom trailing fades in place', (WidgetTester tester) async { await startTransitionBetween( tester, from: const CupertinoSliverNavigationBar(trailing: Text('custom')), @@ -916,7 +918,7 @@ void main() { ); }); - testWidgets('Bottom back label fades and slides to the left', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Bottom back label fades and slides to the left', (WidgetTester tester) async { await startTransitionBetween( tester, fromTitle: 'Page 1', @@ -958,7 +960,7 @@ void main() { ); }); - testWidgets('Bottom back label fades and slides to the right in RTL', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Bottom back label fades and slides to the right in RTL', (WidgetTester tester) async { await startTransitionBetween( tester, fromTitle: 'Page 1', @@ -1002,7 +1004,7 @@ void main() { ); }); - testWidgets('Bottom large title moves to top back label', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Bottom large title moves to top back label', (WidgetTester tester) async { await startTransitionBetween( tester, from: const CupertinoSliverNavigationBar(), @@ -1054,7 +1056,7 @@ void main() { ); }); - testWidgets('Long title turns into the word back mid transition', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Long title turns into the word back mid transition', (WidgetTester tester) async { await startTransitionBetween( tester, from: const CupertinoSliverNavigationBar(), @@ -1104,7 +1106,7 @@ void main() { ); }); - testWidgets('Bottom large title and top back label transitions their font', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Bottom large title and top back label transitions their font', (WidgetTester tester) async { await startTransitionBetween( tester, from: const CupertinoSliverNavigationBar(), @@ -1143,7 +1145,7 @@ void main() { expect(topBackLabel.text.style!.letterSpacing, moreOrLessEquals(-0.2259759941697121)); }); - testWidgets('Top middle fades in and slides in from the right', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Top middle fades in and slides in from the right', (WidgetTester tester) async { await startTransitionBetween( tester, toTitle: 'Page 2', @@ -1174,7 +1176,7 @@ void main() { ); }); - testWidgets('Top middle never changes size during the animation', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Top middle never changes size during the animation', (WidgetTester tester) async { await tester.binding.setSurfaceSize(const Size(1080.0 / 2.75, 600)); addTearDown(() async { await tester.binding.setSurfaceSize(const Size(800.0, 600.0)); @@ -1198,7 +1200,7 @@ void main() { } }); - testWidgets('Top middle fades in and slides in from the left in RTL', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Top middle fades in and slides in from the left in RTL', (WidgetTester tester) async { await startTransitionBetween( tester, toTitle: 'Page 2', @@ -1230,7 +1232,7 @@ void main() { ); }); - testWidgets('Top large title fades in and slides in from the right', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Top large title fades in and slides in from the right', (WidgetTester tester) async { await startTransitionBetween( tester, to: const CupertinoSliverNavigationBar(), @@ -1256,7 +1258,7 @@ void main() { ); }); - testWidgets('Top large title fades in and slides in from the left in RTL', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Top large title fades in and slides in from the left in RTL', (WidgetTester tester) async { await startTransitionBetween( tester, to: const CupertinoSliverNavigationBar(), @@ -1283,7 +1285,7 @@ void main() { ); }); - testWidgets('Components are not unnecessarily rebuilt during transitions', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Components are not unnecessarily rebuilt during transitions', (WidgetTester tester) async { int bottomBuildTimes = 0; int topBuildTimes = 0; await startTransitionBetween( @@ -1328,7 +1330,7 @@ void main() { expect(topBuildTimes, 3); }); - testWidgets('Back swipe gesture transitions', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Back swipe gesture transitions', (WidgetTester tester) async { await startTransitionBetween( tester, fromTitle: 'Page 1', @@ -1391,7 +1393,7 @@ void main() { expect(find.text('Page 1'), findsOneWidget); }); - testWidgets('textScaleFactor is set to 1.0 on transition', (WidgetTester tester) async { + testWidgetsWithLeakTracking('textScaleFactor is set to 1.0 on transition', (WidgetTester tester) async { await startTransitionBetween(tester, fromTitle: 'Page 1', textScale: 99); await tester.pump(const Duration(milliseconds: 50)); @@ -1399,7 +1401,7 @@ void main() { expect(tester.firstWidget(flying(tester, find.byType(RichText))).textScaleFactor, 1); }); - testWidgets('Back swipe gesture cancels properly with transition', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Back swipe gesture cancels properly with transition', (WidgetTester tester) async { await startTransitionBetween( tester, fromTitle: 'Page 1', diff --git a/packages/flutter/test/cupertino/radio_test.dart b/packages/flutter/test/cupertino/radio_test.dart index 0a7d3460c3..da62e4c6ba 100644 --- a/packages/flutter/test/cupertino/radio_test.dart +++ b/packages/flutter/test/cupertino/radio_test.dart @@ -7,11 +7,12 @@ import 'package:flutter/foundation.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/semantics_tester.dart'; void main() { - testWidgets('Radio control test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Radio control test', (WidgetTester tester) async { final Key key = UniqueKey(); final List log = []; @@ -63,7 +64,7 @@ void main() { expect(log, isEmpty); }); - testWidgets('Radio can be toggled when toggleable is set', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Radio can be toggled when toggleable is set', (WidgetTester tester) async { final Key key = UniqueKey(); final List log = []; @@ -118,7 +119,7 @@ void main() { expect(log, equals([1])); }); - testWidgets('Radio selected semantics - platform adaptive', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Radio selected semantics - platform adaptive', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget(CupertinoApp( @@ -153,7 +154,7 @@ void main() { semantics.dispose(); }, variant: TargetPlatformVariant.all()); - testWidgets('Radio semantics', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Radio semantics', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget(CupertinoApp( @@ -241,7 +242,7 @@ void main() { semantics.dispose(); }); - testWidgets('has semantic events', (WidgetTester tester) async { + testWidgetsWithLeakTracking('has semantic events', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final Key key = UniqueKey(); dynamic semanticEvent; @@ -278,13 +279,14 @@ void main() { tester.binding.defaultBinaryMessenger.setMockDecodedMessageHandler(SystemChannels.accessibility, null); }); - testWidgets('Radio can be controlled by keyboard shortcuts', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Radio can be controlled by keyboard shortcuts', (WidgetTester tester) async { tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; int? groupValue = 1; const Key radioKey0 = Key('radio0'); const Key radioKey1 = Key('radio1'); const Key radioKey2 = Key('radio2'); final FocusNode focusNode2 = FocusNode(debugLabel: 'radio2'); + addTearDown(focusNode2.dispose); Widget buildApp({bool enabled = true}) { return CupertinoApp( home: Center( @@ -350,7 +352,7 @@ void main() { expect(groupValue, equals(2)); }); - testWidgets('Show a checkmark when useCheckmarkStyle is true', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Show a checkmark when useCheckmarkStyle is true', (WidgetTester tester) async { await tester.pumpWidget(CupertinoApp( home: Center( child: CupertinoRadio( @@ -405,7 +407,7 @@ void main() { ); }); - testWidgets('Do not crash when widget disappears while pointer is down', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Do not crash when widget disappears while pointer is down', (WidgetTester tester) async { final Key key = UniqueKey(); Widget buildRadio(bool show) { diff --git a/packages/flutter/test/cupertino/scrollbar_test.dart b/packages/flutter/test/cupertino/scrollbar_test.dart index e8a1277ef7..702a3ca8c8 100644 --- a/packages/flutter/test/cupertino/scrollbar_test.dart +++ b/packages/flutter/test/cupertino/scrollbar_test.dart @@ -8,6 +8,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; const CupertinoDynamicColor _kScrollbarColor = CupertinoDynamicColor.withBrightness( color: Color(0x59000000), @@ -20,7 +21,7 @@ void main() { const Duration kScrollbarResizeDuration = Duration(milliseconds: 100); const Duration kLongPressDuration = Duration(milliseconds: 100); - testWidgets('Scrollbar never goes away until finger lift', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar never goes away until finger lift', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -58,7 +59,7 @@ void main() { )); }); - testWidgets('Scrollbar dark mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar dark mode', (WidgetTester tester) async { Brightness brightness = Brightness.light; late StateSetter setState; await tester.pumpWidget( @@ -95,8 +96,9 @@ void main() { )); }); - testWidgets('Scrollbar thumb can be dragged with long press', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar thumb can be dragged with long press', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -167,8 +169,9 @@ void main() { await tester.pump(kScrollbarFadeDuration); }); - testWidgets('Scrollbar thumb can be dragged with long press - reverse', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar thumb can be dragged with long press - reverse', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -242,7 +245,7 @@ void main() { await tester.pump(kScrollbarFadeDuration); }); - testWidgets('Scrollbar changes thickness and radius when dragged', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar changes thickness and radius when dragged', (WidgetTester tester) async { const double thickness = 20; const double thicknessWhileDragging = 40; const double radius = 10; @@ -253,6 +256,7 @@ void main() { final Size screenSize = tester.view.physicalSize / tester.view.devicePixelRatio; final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -339,7 +343,7 @@ void main() { await tester.pump(kScrollbarFadeDuration); }); - testWidgets('When thumbVisibility is true, must pass a controller or find PrimaryScrollController', (WidgetTester tester) async { + testWidgetsWithLeakTracking('When thumbVisibility is true, must pass a controller or find PrimaryScrollController', (WidgetTester tester) async { Widget viewWithScroll() { return const Directionality( textDirection: TextDirection.ltr, @@ -364,8 +368,9 @@ void main() { }, ); - testWidgets('When thumbVisibility is true, must pass a controller or find PrimaryScrollController that is attached to a scroll view', (WidgetTester tester) async { + testWidgetsWithLeakTracking('When thumbVisibility is true, must pass a controller or find PrimaryScrollController that is attached to a scroll view', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); Widget viewWithScroll() { return Directionality( textDirection: TextDirection.ltr, @@ -398,7 +403,7 @@ void main() { }, ); - testWidgets('When thumbVisibility is true, must pass a controller or find PrimaryScrollController', (WidgetTester tester) async { + testWidgetsWithLeakTracking('When thumbVisibility is true, must pass a controller or find PrimaryScrollController', (WidgetTester tester) async { Widget viewWithScroll() { return const Directionality( textDirection: TextDirection.ltr, @@ -423,8 +428,9 @@ void main() { }, ); - testWidgets('When thumbVisibility is true, must pass a controller or find PrimaryScrollController that is attached to a scroll view', (WidgetTester tester) async { + testWidgetsWithLeakTracking('When thumbVisibility is true, must pass a controller or find PrimaryScrollController that is attached to a scroll view', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); Widget viewWithScroll() { return Directionality( textDirection: TextDirection.ltr, @@ -457,8 +463,9 @@ void main() { }, ); - testWidgets('On first render with thumbVisibility: true, the thumb shows with PrimaryScrollController', (WidgetTester tester) async { + testWidgetsWithLeakTracking('On first render with thumbVisibility: true, the thumb shows with PrimaryScrollController', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); Widget viewWithScroll() { return Directionality( textDirection: TextDirection.ltr, @@ -491,8 +498,9 @@ void main() { }, ); - testWidgets('On first render with thumbVisibility: true, the thumb shows', (WidgetTester tester) async { + testWidgetsWithLeakTracking('On first render with thumbVisibility: true, the thumb shows', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); Widget viewWithScroll() { return Directionality( textDirection: TextDirection.ltr, @@ -524,8 +532,9 @@ void main() { expect(find.byType(CupertinoScrollbar), paints..rrect()); }); - testWidgets('On first render with thumbVisibility: true, the thumb shows with PrimaryScrollController', (WidgetTester tester) async { + testWidgetsWithLeakTracking('On first render with thumbVisibility: true, the thumb shows with PrimaryScrollController', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); Widget viewWithScroll() { return Directionality( textDirection: TextDirection.ltr, @@ -558,8 +567,9 @@ void main() { }, ); - testWidgets('On first render with thumbVisibility: true, the thumb shows', (WidgetTester tester) async { + testWidgetsWithLeakTracking('On first render with thumbVisibility: true, the thumb shows', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); Widget viewWithScroll() { return Directionality( textDirection: TextDirection.ltr, @@ -591,8 +601,9 @@ void main() { expect(find.byType(CupertinoScrollbar), paints..rrect()); }); - testWidgets('On first render with thumbVisibility: false, the thumb is hidden', (WidgetTester tester) async { + testWidgetsWithLeakTracking('On first render with thumbVisibility: false, the thumb is hidden', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); Widget viewWithScroll() { return Directionality( textDirection: TextDirection.ltr, @@ -619,8 +630,9 @@ void main() { expect(find.byType(CupertinoScrollbar), isNot(paints..rect())); }); - testWidgets('With thumbVisibility: true, fling a scroll. While it is still scrolling, set thumbVisibility: false. The thumb should not fade out until the scrolling stops.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('With thumbVisibility: true, fling a scroll. While it is still scrolling, set thumbVisibility: false. The thumb should not fade out until the scrolling stops.', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); bool thumbVisibility = true; Widget viewWithScroll() { return StatefulBuilder( @@ -676,10 +688,11 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'With thumbVisibility: false, set thumbVisibility: true. The thumb should be always shown directly', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); bool thumbVisibility = false; Widget viewWithScroll() { return StatefulBuilder( @@ -730,11 +743,12 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'With thumbVisibility: false, fling a scroll. While it is still scrolling, set thumbVisibility: true. ' 'The thumb should not fade even after the scrolling stops', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); bool thumbVisibility = false; Widget viewWithScroll() { return StatefulBuilder( @@ -797,11 +811,12 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'Toggling thumbVisibility while not scrolling fades the thumb in/out. ' 'This works even when you have never scrolled at all yet', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); bool thumbVisibility = true; Widget viewWithScroll() { return StatefulBuilder( @@ -852,8 +867,9 @@ void main() { }, ); - testWidgets('Scrollbar thumb can be dragged with long press - horizontal axis', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar thumb can be dragged with long press - horizontal axis', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -926,8 +942,9 @@ void main() { await tester.pump(kScrollbarFadeDuration); }); - testWidgets('Scrollbar thumb can be dragged with long press - horizontal axis, reverse', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar thumb can be dragged with long press - horizontal axis, reverse', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1001,8 +1018,9 @@ void main() { await tester.pump(kScrollbarFadeDuration); }); - testWidgets('Tapping the track area pages the Scroll View', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Tapping the track area pages the Scroll View', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1060,8 +1078,9 @@ void main() { ); }); - testWidgets('Throw if interactive with the bar when no position attached', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Throw if interactive with the bar when no position attached', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( @@ -1104,9 +1123,11 @@ void main() { FlutterError.onError = handler; }); - testWidgets('Interactive scrollbars should have a valid scroll controller', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Interactive scrollbars should have a valid scroll controller', (WidgetTester tester) async { final ScrollController primaryScrollController = ScrollController(); + addTearDown(primaryScrollController.dispose); final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( @@ -1145,9 +1166,10 @@ void main() { ); }); - testWidgets('Simultaneous dragging and pointer scrolling does not cause a crash', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Simultaneous dragging and pointer scrolling does not cause a crash', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/70105 final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( CupertinoApp( home: PrimaryScrollController( @@ -1256,8 +1278,9 @@ void main() { ); }); - testWidgets('CupertinoScrollbar scrollOrientation works correctly', (WidgetTester tester) async { + testWidgetsWithLeakTracking('CupertinoScrollbar scrollOrientation works correctly', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( CupertinoApp( diff --git a/packages/flutter/test/cupertino/sliding_segmented_control_test.dart b/packages/flutter/test/cupertino/sliding_segmented_control_test.dart index 08bc092057..3aece48a8a 100644 --- a/packages/flutter/test/cupertino/sliding_segmented_control_test.dart +++ b/packages/flutter/test/cupertino/sliding_segmented_control_test.dart @@ -9,6 +9,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import '../widgets/semantics_tester.dart'; @@ -93,7 +94,7 @@ void main() { groupValue = 0; }); - testWidgets('Need at least 2 children', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Need at least 2 children', (WidgetTester tester) async { groupValue = null; await expectLater( () => tester.pumpWidget( @@ -146,7 +147,7 @@ void main() { ); }); - testWidgets('Padding works', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Padding works', (WidgetTester tester) async { const Key key = Key('Container'); const Map children = { @@ -223,7 +224,7 @@ void main() { await verifyPadding(padding: const EdgeInsets.fromLTRB(1, 3, 5, 7)); }); - testWidgets('Tap changes toggle state', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Tap changes toggle state', (WidgetTester tester) async { const Map children = { 0: Text('Child 1'), 1: Text('Child 2'), @@ -255,7 +256,7 @@ void main() { expect(groupValue, 1); }); - testWidgets( + testWidgetsWithLeakTracking( 'Segmented controls respect theme', (WidgetTester tester) async { const Map children = { @@ -293,7 +294,7 @@ void main() { }, ); - testWidgets('SegmentedControl dark mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('SegmentedControl dark mode', (WidgetTester tester) async { const Map children = { 0: Text('Child 1'), 1: Icon(IconData(1)), @@ -345,7 +346,7 @@ void main() { expect(decorationDark.color!.value, CupertinoColors.systemRed.darkColor.value); }); - testWidgets( + testWidgetsWithLeakTracking( 'Children can be non-Text or Icon widgets (in this case, ' 'a Container or Placeholder widget)', (WidgetTester tester) async { @@ -369,13 +370,13 @@ void main() { }, ); - testWidgets('Passed in value is child initially selected', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Passed in value is child initially selected', (WidgetTester tester) async { await tester.pumpWidget(setupSimpleSegmentedControl()); expect(getHighlightedIndex(tester), 0); }); - testWidgets('Null input for value results in no child initially selected', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Null input for value results in no child initially selected', (WidgetTester tester) async { const Map children = { 0: Text('Child 1'), 1: Text('Child 2'), @@ -401,7 +402,7 @@ void main() { expect(getHighlightedIndex(tester), null); }); - testWidgets('Long press not-selected child interactions', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Long press not-selected child interactions', (WidgetTester tester) async { const Map children = { 0: Text('Child 1'), 1: Text('Child 2'), @@ -465,7 +466,7 @@ void main() { expect(getChildOpacityByName('Child 2'), 0.2); }); - testWidgets('Long press does not change the opacity of currently-selected child', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Long press does not change the opacity of currently-selected child', (WidgetTester tester) async { double getChildOpacityByName(String childName) { return tester.renderObject( find.ancestor(matching: find.byType(AnimatedOpacity), of: find.text(childName)), @@ -482,7 +483,7 @@ void main() { expect(getChildOpacityByName('Child 1'), 1); }); - testWidgets('Height of segmented control is determined by tallest widget', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Height of segmented control is determined by tallest widget', (WidgetTester tester) async { final Map children = { 0: Container(constraints: const BoxConstraints.tightFor(height: 100.0)), 1: Container(constraints: const BoxConstraints.tightFor(height: 400.0)), @@ -512,7 +513,7 @@ void main() { ); }); - testWidgets('Width of each segmented control segment is determined by widest widget', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Width of each segmented control segment is determined by widest widget', (WidgetTester tester) async { final Map children = { 0: Container(constraints: const BoxConstraints.tightFor(width: 50.0)), 1: Container(constraints: const BoxConstraints.tightFor(width: 100.0)), @@ -543,7 +544,7 @@ void main() { expect(childWidth, 200.0 + 9.25 * 2); }); - testWidgets('Width is finite in unbounded space', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Width is finite in unbounded space', (WidgetTester tester) async { const Map children = { 0: SizedBox(width: 50), 1: SizedBox(width: 70), @@ -576,7 +577,7 @@ void main() { ); }); - testWidgets('Directionality test - RTL should reverse order of widgets', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Directionality test - RTL should reverse order of widgets', (WidgetTester tester) async { const Map children = { 0: Text('Child 1'), 1: Text('Child 2'), @@ -603,7 +604,7 @@ void main() { expect(tester.getTopRight(find.text('Child 1')).dx > tester.getTopRight(find.text('Child 2')).dx, isTrue); }); - testWidgets('Correct initial selection and toggling behavior - RTL', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Correct initial selection and toggling behavior - RTL', (WidgetTester tester) async { const Map children = { 0: Text('Child 1'), 1: Text('Child 2'), @@ -640,7 +641,7 @@ void main() { expect(getHighlightedIndex(tester), 0); }); - testWidgets('Segmented control semantics', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Segmented control semantics', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); const Map children = { 0: Text('Child 1'), @@ -733,7 +734,7 @@ void main() { semantics.dispose(); }); - testWidgets('Non-centered taps work on smaller widgets', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Non-centered taps work on smaller widgets', (WidgetTester tester) async { final Map children = {}; children[0] = const Text('Child 1'); children[1] = const SizedBox(); @@ -761,7 +762,7 @@ void main() { expect(groupValue, 1); }); - testWidgets('Hit-tests report accurate local position in segments', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hit-tests report accurate local position in segments', (WidgetTester tester) async { final Map children = {}; late TapDownDetails tapDownDetails; children[0] = GestureDetector( @@ -793,7 +794,7 @@ void main() { expect(tapDownDetails.globalPosition, segment0GlobalOffset + const Offset(7, 11)); }); - testWidgets('Thumb animation is correct when the selected segment changes', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Thumb animation is correct when the selected segment changes', (WidgetTester tester) async { await tester.pumpWidget(setupSimpleSegmentedControl()); final Rect initialRect = currentUnscaledThumbRect(tester, useGlobalCoordinate: true); @@ -874,7 +875,7 @@ void main() { expect(currentThumbScale(tester), moreOrLessEquals(1, epsilon: 0.01)); }); - testWidgets( + testWidgetsWithLeakTracking( 'Thumb does not go out of bounds in animation', (WidgetTester tester) async { const Map children = { @@ -931,7 +932,7 @@ void main() { }, ); - testWidgets('Transition is triggered while a transition is already occurring', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Transition is triggered while a transition is already occurring', (WidgetTester tester) async { const Map children = { 0: Text('A'), 1: Text('B'), @@ -981,7 +982,7 @@ void main() { ); }); - testWidgets('Insert segment while animation is running', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Insert segment while animation is running', (WidgetTester tester) async { final Map children = SplayTreeMap((int a, int b) => a - b); children[0] = const Text('A'); @@ -1027,7 +1028,7 @@ void main() { ); }); - testWidgets('change selection programmatically when dragging', (WidgetTester tester) async { + testWidgetsWithLeakTracking('change selection programmatically when dragging', (WidgetTester tester) async { const Map children = { 0: Text('A'), 1: Text('B'), @@ -1086,7 +1087,7 @@ void main() { expect(callbackCalled, isFalse); }); - testWidgets('Disallow new gesture when dragging', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Disallow new gesture when dragging', (WidgetTester tester) async { const Map children = { 0: Text('A'), 1: Text('B'), @@ -1141,7 +1142,7 @@ void main() { expect(callbackCalled, isFalse); }); - testWidgets('gesture outlives the widget', (WidgetTester tester) async { + testWidgetsWithLeakTracking('gesture outlives the widget', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/63338. const Map children = { 0: Text('A'), @@ -1179,7 +1180,7 @@ void main() { expect(tester.takeException(), isNull); }); - testWidgets('computeDryLayout is pure', (WidgetTester tester) async { + testWidgetsWithLeakTracking('computeDryLayout is pure', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/73362. const Map children = { 0: Text('A'), @@ -1213,7 +1214,7 @@ void main() { expect(tester.takeException(), isNull); }); - testWidgets('Has consistent size, independent of groupValue', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Has consistent size, independent of groupValue', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/62063. const Map children = { 0: Text('A'), @@ -1247,12 +1248,13 @@ void main() { } }); - testWidgets('ScrollView + SlidingSegmentedControl interaction', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ScrollView + SlidingSegmentedControl interaction', (WidgetTester tester) async { const Map children = { 0: Text('Child 1'), 1: Text('Child 2'), }; final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( @@ -1333,7 +1335,7 @@ void main() { expect(groupValue, 1); }); - testWidgets('Hovering over Cupertino sliding segmented control updates cursor to clickable on Web', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hovering over Cupertino sliding segmented control updates cursor to clickable on Web', (WidgetTester tester) async { const Map children = { 0: Text('A'), 1: Text('BB'), diff --git a/packages/flutter/test/cupertino/spell_check_suggestions_toolbar_test.dart b/packages/flutter/test/cupertino/spell_check_suggestions_toolbar_test.dart index dec2165a6c..fa88e2859d 100644 --- a/packages/flutter/test/cupertino/spell_check_suggestions_toolbar_test.dart +++ b/packages/flutter/test/cupertino/spell_check_suggestions_toolbar_test.dart @@ -7,11 +7,12 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - testWidgets('more than three suggestions throws an error', (WidgetTester tester) async { + testWidgetsWithLeakTracking('more than three suggestions throws an error', (WidgetTester tester) async { Future pumpToolbar(List suggestions) async { await tester.pumpWidget( CupertinoApp( @@ -61,10 +62,14 @@ void main() { expect(labels, isNot(contains('yeller'))); }); - testWidgets('buildButtonItems builds a disabled "No Replacements Found" button when no suggestions', (WidgetTester tester) async { + testWidgetsWithLeakTracking('buildButtonItems builds a disabled "No Replacements Found" button when no suggestions', (WidgetTester tester) async { + final TextEditingController controller = TextEditingController(); + addTearDown(controller.dispose); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); await tester.pumpWidget( CupertinoApp( - home: _FakeEditableText(), + home: _FakeEditableText(focusNode, controller), ), ); final _FakeEditableTextState editableTextState = @@ -80,9 +85,11 @@ void main() { } class _FakeEditableText extends EditableText { - _FakeEditableText() : super( - controller: TextEditingController(), - focusNode: FocusNode(), + /// The parameters focusNode and controller are needed here so the can be + /// safely disposed after the test is completed. + _FakeEditableText(FocusNode focusNode, TextEditingController controller) : super( + controller: controller, + focusNode: focusNode, backgroundCursorColor: CupertinoColors.white, cursorColor: CupertinoColors.white, style: const TextStyle(), diff --git a/packages/flutter/test/cupertino/switch_test.dart b/packages/flutter/test/cupertino/switch_test.dart index e6b484bda2..cf4962c037 100644 --- a/packages/flutter/test/cupertino/switch_test.dart +++ b/packages/flutter/test/cupertino/switch_test.dart @@ -14,9 +14,10 @@ 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'; void main() { - testWidgets('Switch can toggle on tap', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch can toggle on tap', (WidgetTester tester) async { final Key switchKey = UniqueKey(); bool value = false; await tester.pumpWidget( @@ -46,7 +47,7 @@ void main() { expect(value, isTrue); }); - testWidgets('CupertinoSwitch can be toggled by keyboard shortcuts', (WidgetTester tester) async { + testWidgetsWithLeakTracking('CupertinoSwitch can be toggled by keyboard shortcuts', (WidgetTester tester) async { bool value = true; Widget buildApp({bool enabled = true}) { return CupertinoApp( @@ -79,7 +80,7 @@ void main() { expect(value, isTrue); }); - testWidgets('Switch emits light haptic vibration on tap', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch emits light haptic vibration on tap', (WidgetTester tester) async { final Key switchKey = UniqueKey(); bool value = false; @@ -119,7 +120,7 @@ void main() { expect(log.single, isMethodCall('HapticFeedback.vibrate', arguments: 'HapticFeedbackType.lightImpact')); }, variant: TargetPlatformVariant.only(TargetPlatform.iOS)); - testWidgets('Using other widgets that rebuild the switch will not cause vibrations', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Using other widgets that rebuild the switch will not cause vibrations', (WidgetTester tester) async { final Key switchKey = UniqueKey(); final Key switchKey2 = UniqueKey(); bool value = false; @@ -190,7 +191,7 @@ void main() { expect(log[3], isMethodCall('HapticFeedback.vibrate', arguments: 'HapticFeedbackType.lightImpact')); }, variant: TargetPlatformVariant.only(TargetPlatform.iOS)); - testWidgets('Haptic vibration triggers on drag', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Haptic vibration triggers on drag', (WidgetTester tester) async { bool value = false; final List log = []; @@ -228,7 +229,7 @@ void main() { expect(log[0], isMethodCall('HapticFeedback.vibrate', arguments: 'HapticFeedbackType.lightImpact')); }, variant: TargetPlatformVariant.only(TargetPlatform.iOS)); - testWidgets('No haptic vibration triggers from a programmatic value change', (WidgetTester tester) async { + testWidgetsWithLeakTracking('No haptic vibration triggers from a programmatic value change', (WidgetTester tester) async { final Key switchKey = UniqueKey(); bool value = false; @@ -280,7 +281,7 @@ void main() { expect(log, hasLength(0)); }, variant: TargetPlatformVariant.only(TargetPlatform.iOS)); - testWidgets('Switch can drag (LTR)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch can drag (LTR)', (WidgetTester tester) async { bool value = false; await tester.pumpWidget( @@ -324,7 +325,7 @@ void main() { expect(value, isFalse); }); - testWidgets('Switch can drag with dragStartBehavior', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch can drag with dragStartBehavior', (WidgetTester tester) async { bool value = false; await tester.pumpWidget( @@ -410,7 +411,7 @@ void main() { await tester.pump(); }); - testWidgets('Switch can drag (RTL)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch can drag (RTL)', (WidgetTester tester) async { bool value = false; await tester.pumpWidget( @@ -455,7 +456,7 @@ void main() { expect(value, isFalse); }); - testWidgets('can veto switch dragging result', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can veto switch dragging result', (WidgetTester tester) async { bool value = false; await tester.pumpWidget( @@ -527,7 +528,7 @@ void main() { expect(position.value, 1.0); }); - testWidgets('Switch is translucent when disabled', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch is translucent when disabled', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -545,7 +546,7 @@ void main() { expect(tester.widget(find.byType(Opacity).first).opacity, 0.5); }); - testWidgets('Switch is using track color when set', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch is using track color when set', (WidgetTester tester) async { const Color trackColor = Color(0xFF00FF00); await tester.pumpWidget( @@ -567,7 +568,7 @@ void main() { expect(find.byType(CupertinoSwitch), paints..rrect(color: trackColor)); }); - testWidgets('Switch is using default thumb color', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch is using default thumb color', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -593,7 +594,7 @@ void main() { ); }); - testWidgets('Switch is using thumb color when set', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch is using thumb color when set', (WidgetTester tester) async { const Color thumbColor = Color(0xFF000000); await tester.pumpWidget( const Directionality( @@ -621,7 +622,7 @@ void main() { ); }); - testWidgets('Switch is opaque when enabled', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch is opaque when enabled', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -639,7 +640,7 @@ void main() { expect(tester.widget(find.byType(Opacity).first).opacity, 1.0); }); - testWidgets('Switch turns translucent after becoming disabled', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch turns translucent after becoming disabled', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -670,7 +671,7 @@ void main() { expect(tester.widget(find.byType(Opacity).first).opacity, 0.5); }); - testWidgets('Switch turns opaque after becoming enabled', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch turns opaque after becoming enabled', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -701,7 +702,7 @@ void main() { expect(tester.widget(find.byType(Opacity).first).opacity, 1.0); }); - testWidgets('Switch renders correctly before, during, and after being tapped', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch renders correctly before, during, and after being tapped', (WidgetTester tester) async { final Key switchKey = UniqueKey(); bool value = false; await tester.pumpWidget( @@ -779,7 +780,7 @@ void main() { style: PaintingStyle.stroke, ); - testWidgets('Switch renders switch labels correctly before, during, and after being tapped', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch renders switch labels correctly before, during, and after being tapped', (WidgetTester tester) async { final Key switchKey = UniqueKey(); bool value = false; await tester.pumpWidget( @@ -829,7 +830,7 @@ void main() { expect(switchRenderObject, offLabelPaintPattern(alpha: 0)); }); - testWidgets('Switch renders switch labels correctly before, during, and after being tapped in high contrast', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch renders switch labels correctly before, during, and after being tapped in high contrast', (WidgetTester tester) async { final Key switchKey = UniqueKey(); bool value = false; await tester.pumpWidget( @@ -882,7 +883,7 @@ void main() { expect(switchRenderObject, offLabelPaintPattern(highContrast: true, alpha: 0)); }); - testWidgets('Switch renders switch labels correctly before, during, and after being tapped with direction rtl', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch renders switch labels correctly before, during, and after being tapped with direction rtl', (WidgetTester tester) async { final Key switchKey = UniqueKey(); bool value = false; await tester.pumpWidget( @@ -932,7 +933,7 @@ void main() { expect(switchRenderObject, offLabelPaintPattern(isRtl: true, alpha: 0)); }); - testWidgets('Switch renders correctly in dark mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch renders correctly in dark mode', (WidgetTester tester) async { final Key switchKey = UniqueKey(); bool value = false; await tester.pumpWidget( @@ -977,7 +978,7 @@ void main() { ); }); - testWidgets('Switch can apply the ambient theme and be opted out', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switch can apply the ambient theme and be opted out', (WidgetTester tester) async { final Key switchKey = UniqueKey(); bool value = false; await tester.pumpWidget( @@ -1037,7 +1038,7 @@ void main() { ); }); - testWidgets('Hovering over Cupertino switch updates cursor to clickable on Web', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hovering over Cupertino switch updates cursor to clickable on Web', (WidgetTester tester) async { const bool value = false; // Disabled CupertinoSwitch does not update cursor on Web. await tester.pumpWidget( @@ -1093,8 +1094,9 @@ void main() { ); }); - testWidgets('CupertinoSwitch is focusable and has correct focus color', (WidgetTester tester) async { + testWidgetsWithLeakTracking('CupertinoSwitch is focusable and has correct focus color', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(debugLabel: 'CupertinoSwitch'); + addTearDown(focusNode.dispose); tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; bool value = true; const Color focusColor = Color(0xffff0000); @@ -1174,8 +1176,9 @@ void main() { ); }); - testWidgets('CupertinoSwitch.onFocusChange callback', (WidgetTester tester) async { + testWidgetsWithLeakTracking('CupertinoSwitch.onFocusChange callback', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(debugLabel: 'CupertinoSwitch'); + addTearDown(focusNode.dispose); bool focused = false; await tester.pumpWidget( Directionality(