From 06ed849cbcf5b9f3a4c475f557238361625697fc Mon Sep 17 00:00:00 2001 From: Taha Tesser Date: Mon, 18 Mar 2024 14:45:22 +0200 Subject: [PATCH] Update `inherited_theme_test.dart`, `ink_paint_test.dart`, `ink_splash_test.dart`, `opacity_test.dart` for Material 3 (#144013) Updated unit tests for `Tooltip` to have M2 and M3 versions. More info in #139076 --- .../test/material/inherited_theme_test.dart | 54 ++++- .../flutter/test/material/ink_paint_test.dart | 228 +++++++++++++++++- .../test/material/ink_splash_test.dart | 43 +++- .../flutter/test/widgets/opacity_test.dart | 5 +- 4 files changed, 316 insertions(+), 14 deletions(-) diff --git a/packages/flutter/test/material/inherited_theme_test.dart b/packages/flutter/test/material/inherited_theme_test.dart index 6f58948a794..a36a6fec1c5 100644 --- a/packages/flutter/test/material/inherited_theme_test.dart +++ b/packages/flutter/test/material/inherited_theme_test.dart @@ -91,7 +91,7 @@ void main() { expect(containerColor(), isNot(primaryColor)); }); - testWidgets('PopupMenuTheme.wrap()', (WidgetTester tester) async { + testWidgets('Material2 - PopupMenuTheme.wrap()', (WidgetTester tester) async { const double menuFontSize = 24; const Color menuTextColor = Color(0xFF0000FF); @@ -145,6 +145,58 @@ void main() { await tester.pumpAndSettle(); // menu route animation }); + testWidgets('Material3 - PopupMenuTheme.wrap()', (WidgetTester tester) async { + const TextStyle textStyle = TextStyle(fontSize: 24.0, color: Color(0xFF0000FF)); + + Widget buildFrame() { + return MaterialApp( + home: Scaffold( + body: PopupMenuTheme( + data: const PopupMenuThemeData( + // The menu route's elevation, shape, and color are defined by the + // current context, so they're not affected by ThemeData.captureAll(). + labelTextStyle: MaterialStatePropertyAll(textStyle), + ), + child: Center( + child: PopupMenuButton( + // The appearance of the menu items' text is defined by the + // PopupMenuTheme defined above. Popup menus use + // InheritedTheme.captureAll() by default. + child: const Text('show popupmenu'), + onSelected: (int result) { }, + itemBuilder: (BuildContext context) { + return const >[ + PopupMenuItem(value: 1, child: Text('One')), + PopupMenuItem(value: 2, child: Text('Two')), + ]; + }, + ), + ), + ), + ), + ); + } + + TextStyle itemTextStyle(String text) { + return tester.widget( + find.descendant(of: find.text(text), matching: find.byType(RichText)), + ).text.style!; + } + + await tester.pumpWidget(buildFrame()); + + await tester.tap(find.text('show popupmenu')); + await tester.pumpAndSettle(); // menu route animation + expect(itemTextStyle('One').fontSize, textStyle.fontSize); + expect(itemTextStyle('One').color, textStyle.color); + expect(itemTextStyle('Two').fontSize, textStyle.fontSize); + expect(itemTextStyle('Two').color, textStyle.color); + + // Dismiss the menu + await tester.tap(find.text('One')); + await tester.pumpAndSettle(); // menu route animation + }); + testWidgets('BannerTheme.wrap()', (WidgetTester tester) async { const Color bannerBackgroundColor = Color(0xFF0000FF); const double bannerFontSize = 48; diff --git a/packages/flutter/test/material/ink_paint_test.dart b/packages/flutter/test/material/ink_paint_test.dart index 2cc31710850..be55444f488 100644 --- a/packages/flutter/test/material/ink_paint_test.dart +++ b/packages/flutter/test/material/ink_paint_test.dart @@ -2,6 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This file is run as part of a reduced test set in CI on Mac and Windows +// machines. +@Tags(['reduced-test-set']) +library; + +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -51,8 +57,7 @@ void main() { expect(tester.getSize(find.byType(Ink)), const Size(800, height)); }); - testWidgets('The InkWell widget renders an ink splash', (WidgetTester tester) async { - const Color highlightColor = Color(0xAAFF0000); + testWidgets('Material2 - InkWell widget renders an ink splash', (WidgetTester tester) async { const Color splashColor = Color(0xAA0000FF); const BorderRadius borderRadius = BorderRadius.all(Radius.circular(6.0)); @@ -66,7 +71,6 @@ void main() { height: 60.0, child: InkWell( borderRadius: borderRadius, - highlightColor: highlightColor, splashColor: splashColor, onTap: () { }, ), @@ -91,15 +95,77 @@ void main() { ..clipRRect(rrect: RRect.fromLTRBR(0.0, 0.0, 200.0, 60.0, const Radius.circular(6.0))) ..circle(x: 100.0, y: 30.0, radius: 21.0, color: splashColor) ..restore() - ..rrect( - rrect: RRect.fromLTRBR(300.0, 270.0, 500.0, 330.0, const Radius.circular(6.0)), - color: highlightColor, - ), ); await gesture.up(); }); + testWidgets('Material3 - InkWell widget renders an ink splash', (WidgetTester tester) async { + const Key inkWellKey = Key('InkWell'); + const Color splashColor = Color(0xAA0000FF); + const BorderRadius borderRadius = BorderRadius.all(Radius.circular(6.0)); + + await tester.pumpWidget( + MaterialApp( + home: Material( + child: Center( + child: SizedBox( + width: 200.0, + height: 60.0, + child: InkWell( + key: inkWellKey, + borderRadius: borderRadius, + splashColor: splashColor, + onTap: () { }, + ), + ), + ), + ), + ), + ); + + final Offset center = tester.getCenter(find.byType(InkWell)); + final TestGesture gesture = await tester.startGesture(center); + await tester.pump(); // start gesture + await tester.pump(const Duration(milliseconds: 200)); // wait for splash to be well under way + + final RenderBox box = Material.of(tester.element(find.byType(InkWell))) as RenderBox; + if (kIsWeb && isCanvasKit) { + expect( + box, + paints + ..save() + ..translate(x: 0.0, y: 0.0) + ..clipRect() + ..save() + ..translate(x: 300.0, y: 270.0) + ..clipRRect(rrect: RRect.fromLTRBR(0.0, 0.0, 200.0, 60.0, const Radius.circular(6.0))) + ..circle() + ..restore() + ); + } else { + expect( + box, + paints + ..translate(x: 0.0, y: 0.0) + ..save() + ..translate(x: 300.0, y: 270.0) + ..clipRRect(rrect: RRect.fromLTRBR(0.0, 0.0, 200.0, 60.0, const Radius.circular(6.0))) + ..rect(rect: const Rect.fromLTRB(0.0, 0.0, 200, 60)) + ..restore() + ); + } + + // Material 3 uses the InkSparkle which uses a shader, so we can't capture + // the effect with paint methods. Use a golden test instead. + await expectLater( + find.byKey(inkWellKey), + matchesGoldenFile('m3_ink_well.renders.ink_splash.png'), + ); + + await gesture.up(); + }, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933 + testWidgets('The InkWell widget renders an ink ripple', (WidgetTester tester) async { const Color highlightColor = Color(0xAAFF0000); const Color splashColor = Color(0xB40000FF); @@ -186,7 +252,7 @@ void main() { expect(box, ripplePattern(inkWellCenter - tapDownOffset, 105.0, 0)); }); - testWidgets('Does the Ink widget render anything', (WidgetTester tester) async { + testWidgets('Material2 - Does the Ink widget render anything', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( theme: ThemeData(useMaterial3: false), @@ -269,6 +335,94 @@ void main() { await gesture.up(); }); + testWidgets('Material3 - Does the Ink widget render anything', (WidgetTester tester) async { + const Key inkWellKey = Key('InkWell'); + await tester.pumpWidget( + MaterialApp( + home: Material( + child: Center( + child: Ink( + color: Colors.blue, + width: 200.0, + height: 200.0, + child: InkWell( + key: inkWellKey, + splashColor: Colors.green, + onTap: () { }, + ), + ), + ), + ), + ), + ); + + final Offset center = tester.getCenter(find.byType(InkWell)); + final TestGesture gesture = await tester.startGesture(center); + await tester.pump(); // start gesture + await tester.pump(const Duration(milliseconds: 200)); // wait for splash to be well under way + + final RenderBox box = Material.of(tester.element(find.byType(InkWell)))as RenderBox; + expect(box, paints..rect(rect: const Rect.fromLTRB(300.0, 200.0, 500.0, 400.0), color: Color(Colors.blue.value))); + + // Material 3 uses the InkSparkle which uses a shader, so we can't capture + // the effect with paint methods. Use a golden test instead. + await expectLater( + find.byKey(inkWellKey), + matchesGoldenFile('m3_ink.renders.anything.0.png'), + ); + + await tester.pumpWidget( + MaterialApp( + home: Material( + child: Center( + child: Ink( + color: Colors.red, + width: 200.0, + height: 200.0, + child: InkWell( + key: inkWellKey, + splashColor: Colors.green, + onTap: () { }, + ), + ), + ), + ), + ), + ); + + expect(Material.of(tester.element(find.byType(InkWell))), same(box)); + + expect(box, paints..rect(rect: const Rect.fromLTRB(300.0, 200.0, 500.0, 400.0), color: Color(Colors.red.value))); + + // Material 3 uses the InkSparkle which uses a shader, so we can't capture + // the effect with paint methods. Use a golden test instead. + await expectLater( + find.byKey(inkWellKey), + matchesGoldenFile('m3_ink.renders.anything.1.png'), + ); + + await tester.pumpWidget( + MaterialApp( + home: Material( + child: Center( + child: InkWell( // This is at a different depth in the tree so it's now a new InkWell. + key: inkWellKey, + splashColor: Colors.green, + onTap: () { }, + ), + ), + ), + ), + ); + + expect(Material.of(tester.element(find.byType(InkWell))), same(box)); + + expect(box, isNot(paints..rect())); + expect(box, isNot(paints..rect())); + + await gesture.up(); + }); + testWidgets('The InkWell widget renders an SelectAction or ActivateAction-induced ink ripple', (WidgetTester tester) async { const Color highlightColor = Color(0xAAFF0000); const Color splashColor = Color(0xB40000FF); @@ -517,7 +671,7 @@ void main() { expect(tester.takeException(), isNull); }); - testWidgets('Custom rectCallback renders an ink splash from its center', (WidgetTester tester) async { + testWidgets('Material2 - Custom rectCallback renders an ink splash from its center', (WidgetTester tester) async { const Color splashColor = Color(0xff00ff00); Widget buildWidget({InteractiveInkFeatureFactory? splashFactory}) { @@ -572,6 +726,62 @@ void main() { ); }); + testWidgets('Material3 - Custom rectCallback renders an ink splash from its center', (WidgetTester tester) async { + const Key inkWResponseKey = Key('InkResponse'); + const Color splashColor = Color(0xff00ff00); + + Widget buildWidget({InteractiveInkFeatureFactory? splashFactory}) { + return MaterialApp( + home: Material( + child: Center( + child: SizedBox( + width: 100.0, + height: 200.0, + child: InkResponse( + key: inkWResponseKey, + splashColor: splashColor, + containedInkWell: true, + highlightShape: BoxShape.rectangle, + splashFactory: splashFactory, + onTap: () { }, + ), + ), + ), + ), + ); + } + + await tester.pumpWidget(buildWidget()); + + final Offset center = tester.getCenter(find.byType(SizedBox)); + TestGesture gesture = await tester.startGesture(center); + await tester.pump(); // start gesture + await tester.pumpAndSettle(); // Finish rendering ink splash. + + // Material 3 uses the InkSparkle which uses a shader, so we can't capture + // the effect with paint methods. Use a golden test instead. + await expectLater( + find.byKey(inkWResponseKey), + matchesGoldenFile('m3_ink_response.renders.ink_splash_from_its_center.0.png'), + ); + + await gesture.up(); + + await tester.pumpWidget(buildWidget(splashFactory: _InkRippleFactory())); + await tester.pumpAndSettle(); // Finish rendering ink splash. + + gesture = await tester.startGesture(center); + await tester.pump(); // start gesture + await tester.pumpAndSettle(); // Finish rendering ink splash. + + // Material 3 uses the InkSparkle which uses a shader, so we can't capture + // the effect with paint methods. Use a golden test instead. + await expectLater( + find.byKey(inkWResponseKey), + matchesGoldenFile('m3_ink_response.renders.ink_splash_from_its_center.1.png'), + ); + }); + testWidgets('Ink with isVisible=false does not paint', (WidgetTester tester) async { const Color testColor = Color(0xffff1234); Widget inkWidget({required bool isVisible}) { diff --git a/packages/flutter/test/material/ink_splash_test.dart b/packages/flutter/test/material/ink_splash_test.dart index daafbf76c2b..372ea3a07cb 100644 --- a/packages/flutter/test/material/ink_splash_test.dart +++ b/packages/flutter/test/material/ink_splash_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -58,7 +59,7 @@ void main() { expect(tester.takeException(), isNull); }); - testWidgets('InkWell with NoSplash splashFactory paints nothing', (WidgetTester tester) async { + testWidgets('Material2 - InkWell with NoSplash splashFactory paints nothing', (WidgetTester tester) async { Widget buildFrame({ InteractiveInkFeatureFactory? splashFactory }) { return MaterialApp( theme: ThemeData(useMaterial3: false), @@ -99,6 +100,46 @@ void main() { } }); + testWidgets('Material3 - InkWell with NoSplash splashFactory paints nothing', (WidgetTester tester) async { + Widget buildFrame({ InteractiveInkFeatureFactory? splashFactory }) { + return MaterialApp( + home: Scaffold( + body: Center( + child: Material( + child: InkWell( + splashFactory: splashFactory, + onTap: () { }, + child: const Text('test'), + ), + ), + ), + ), + ); + } + + // NoSplash.splashFactory, one rect is drawn for the highlight. + await tester.pumpWidget(buildFrame(splashFactory: NoSplash.splashFactory)); + { + final TestGesture gesture = await tester.startGesture(tester.getCenter(find.text('test'))); + final MaterialInkController material = Material.of(tester.element(find.text('test'))); + await tester.pump(const Duration(milliseconds: 200)); + expect(material, paintsExactlyCountTimes(#drawRect, 1)); + await gesture.up(); + await tester.pumpAndSettle(); + } + + // Default splashFactory (from Theme.of().splashFactory), two rects are drawn for the splash and highlight. + await tester.pumpWidget(buildFrame()); + { + final TestGesture gesture = await tester.startGesture(tester.getCenter(find.text('test'))); + final MaterialInkController material = Material.of(tester.element(find.text('test'))); + await tester.pump(const Duration(milliseconds: 200)); + expect(material, paintsExactlyCountTimes(#drawRect, (kIsWeb && isCanvasKit ? 1 : 2))); + await gesture.up(); + await tester.pumpAndSettle(); + } + }, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933 + // Regression test for https://github.com/flutter/flutter/issues/136441. testWidgets('PageView item can dispose when widget with NoSplash.splashFactory is tapped', (WidgetTester tester) async { final PageController controller = PageController(); diff --git a/packages/flutter/test/widgets/opacity_test.dart b/packages/flutter/test/widgets/opacity_test.dart index e8571771dd0..c1f9305bb09 100644 --- a/packages/flutter/test/widgets/opacity_test.dart +++ b/packages/flutter/test/widgets/opacity_test.dart @@ -156,7 +156,6 @@ void main() { testWidgets('offset is correctly handled in Opacity', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( - theme: ThemeData(useMaterial3: false), home: Scaffold( body: SingleChildScrollView( child: RepaintBoundary( @@ -168,8 +167,8 @@ void main() { child: Padding( padding: const EdgeInsets.all(5.0), child: Container( - color: Colors.blue, - height: 50, + color: Colors.blue, + height: 50, ), ), );