From f4da09626ef0985b30e37cdaeef2da6c2022b006 Mon Sep 17 00:00:00 2001 From: Qun Cheng <36861262+QuncCccccc@users.noreply.github.com> Date: Mon, 17 Jul 2023 01:07:10 -0700 Subject: [PATCH] Update `DropdownMenu`, `SnackBarTheme` and `Stepper` tests for M2/M3 (#130464) Updated unit tests for `DropdownMenu`, `SnackBarTheme` and `Stepper` to have M2 and M3 versions. More info in #127064 --- .../test/material/dropdown_menu_test.dart | 117 +++++++++- .../test/material/snack_bar_theme_test.dart | 45 +++- .../flutter/test/material/stepper_test.dart | 214 +++++++++++++++--- 3 files changed, 331 insertions(+), 45 deletions(-) diff --git a/packages/flutter/test/material/dropdown_menu_test.dart b/packages/flutter/test/material/dropdown_menu_test.dart index 6642efd227..d61d1764a8 100644 --- a/packages/flutter/test/material/dropdown_menu_test.dart +++ b/packages/flutter/test/material/dropdown_menu_test.dart @@ -5,6 +5,7 @@ import 'dart:ui'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; @@ -109,11 +110,10 @@ void main() { expect(updatedMenuMaterial, findsNothing); }); - testWidgets('The width of the text field should always be the same as the menu view', + testWidgets('Material2 - The width of the text field should always be the same as the menu view', (WidgetTester tester) async { final ThemeData themeData = ThemeData(useMaterial3: false); - final bool useMaterial3 = themeData.useMaterial3; await tester.pumpWidget( MaterialApp( theme: themeData, @@ -129,7 +129,7 @@ void main() { final Finder textField = find.byType(TextField); final Size anchorSize = tester.getSize(textField); - expect(anchorSize, useMaterial3 ? const Size(195.0, 60.0) : const Size(180.0, 56.0)); + expect(anchorSize, const Size(180.0, 56.0)); await tester.tap(find.byType(DropdownMenu)); await tester.pumpAndSettle(); @@ -139,7 +139,7 @@ void main() { matching: find.byType(Material), ); final Size menuSize = tester.getSize(menuMaterial); - expect(menuSize, useMaterial3 ? const Size(195.0, 304.0) : const Size(180.0, 304.0)); + expect(menuSize, const Size(180.0, 304.0)); // The text field should have same width as the menu // when the width property is not null. @@ -147,7 +147,64 @@ void main() { final Finder anchor = find.byType(TextField); final Size size = tester.getSize(anchor); - expect(size, useMaterial3 ? const Size(200.0, 60.0) : const Size(200.0, 56.0)); + expect(size, const Size(200.0, 56.0)); + + await tester.tap(anchor); + await tester.pumpAndSettle(); + + final Finder updatedMenu = find.ancestor( + of: find.byType(SingleChildScrollView), + matching: find.byType(Material), + ); + final Size updatedMenuSize = tester.getSize(updatedMenu); + expect(updatedMenuSize, const Size(200.0, 304.0)); + }); + + testWidgets('Material3 - The width of the text field should always be the same as the menu view', + (WidgetTester tester) async { + final ThemeData themeData = ThemeData(useMaterial3: true); + await tester.pumpWidget( + MaterialApp( + theme: themeData, + home: Scaffold( + body: SafeArea( + child: DropdownMenu( + dropdownMenuEntries: menuChildren, + ), + ), + ), + ) + ); + + final Finder textField = find.byType(TextField); + final Size anchorSize = tester.getSize(textField); + if (kIsWeb && !isCanvasKit) { + expect(anchorSize, const Size(195.0, 61.0)); + } else { + expect(anchorSize, const Size(195.0, 60.0)); + } + + await tester.tap(find.byType(DropdownMenu)); + await tester.pumpAndSettle(); + + final Finder menuMaterial = find.ancestor( + of: find.byType(SingleChildScrollView), + matching: find.byType(Material), + ); + final Size menuSize = tester.getSize(menuMaterial); + expect(menuSize, const Size(195.0, 304.0)); + + // The text field should have same width as the menu + // when the width property is not null. + await tester.pumpWidget(buildTest(themeData, menuChildren, width: 200.0)); + + final Finder anchor = find.byType(TextField); + final Size size = tester.getSize(anchor); + if (kIsWeb && !isCanvasKit) { + expect(size, const Size(200.0, 61.0)); + } else { + expect(size, const Size(200.0, 60.0)); + } await tester.tap(anchor); await tester.pumpAndSettle(); @@ -280,10 +337,9 @@ void main() { expect(buttonSize.width, parentWidth - 35.0 - 20.0); }); - testWidgets('The menuHeight property can be used to show a shorter scrollable menu list instead of the complete list', + testWidgets('Material2 - The menuHeight property can be used to show a shorter scrollable menu list instead of the complete list', (WidgetTester tester) async { - final ThemeData themeData = ThemeData(); - final bool material3 = themeData.useMaterial3; + final ThemeData themeData = ThemeData(useMaterial3: false); await tester.pumpWidget(buildTest(themeData, menuChildren)); await tester.tap(find.byType(DropdownMenu)); @@ -303,7 +359,7 @@ void main() { matching: find.byType(Padding), ).first; final Size menuViewSize = tester.getSize(menuView); - expect(menuViewSize, material3 ? const Size(195.0, 304.0) : const Size(180.0, 304.0)); // 304 = 288 + vertical padding(2 * 8) + expect(menuViewSize, const Size(180.0, 304.0)); // 304 = 288 + vertical padding(2 * 8) // Constrains the menu height. await tester.pumpWidget(Container()); @@ -319,9 +375,50 @@ void main() { ).first; final Size updatedMenuSize = tester.getSize(updatedMenu); - expect(updatedMenuSize, material3 ? const Size(195.0, 100.0) : const Size(180.0, 100.0)); + expect(updatedMenuSize, const Size(180.0, 100.0)); }); + testWidgets('Material3 - The menuHeight property can be used to show a shorter scrollable menu list instead of the complete list', + (WidgetTester tester) async { + final ThemeData themeData = ThemeData(useMaterial3: true); + await tester.pumpWidget(buildTest(themeData, menuChildren)); + + await tester.tap(find.byType(DropdownMenu)); + await tester.pumpAndSettle(); + + final Element firstItem = tester.element(find.widgetWithText(MenuItemButton, 'Item 0').last); + final RenderBox firstBox = firstItem.renderObject! as RenderBox; + final Offset topLeft = firstBox.localToGlobal(firstBox.size.topLeft(Offset.zero)); + final Element lastItem = tester.element(find.widgetWithText(MenuItemButton, 'Item 5').last); + final RenderBox lastBox = lastItem.renderObject! as RenderBox; + final Offset bottomRight = lastBox.localToGlobal(lastBox.size.bottomRight(Offset.zero)); + // height = height of MenuItemButton * 6 = 48 * 6 + expect(bottomRight.dy - topLeft.dy, 288.0); + + final Finder menuView = find.ancestor( + of: find.byType(SingleChildScrollView), + matching: find.byType(Padding), + ).first; + final Size menuViewSize = tester.getSize(menuView); + expect(menuViewSize, const Size(195.0, 304.0)); // 304 = 288 + vertical padding(2 * 8) + + // Constrains the menu height. + await tester.pumpWidget(Container()); + await tester.pumpWidget(buildTest(themeData, menuChildren, menuHeight: 100)); + await tester.pumpAndSettle(); + + await tester.tap(find.byType(DropdownMenu)); + await tester.pumpAndSettle(); + + final Finder updatedMenu = find.ancestor( + of: find.byType(SingleChildScrollView), + matching: find.byType(Padding), + ).first; + + final Size updatedMenuSize = tester.getSize(updatedMenu); + expect(updatedMenuSize, const Size(195.0, 100.0)); +}); + testWidgets('The text in the menu button should be aligned with the text of ' 'the text field - LTR', (WidgetTester tester) async { final ThemeData themeData = ThemeData(); diff --git a/packages/flutter/test/material/snack_bar_theme_test.dart b/packages/flutter/test/material/snack_bar_theme_test.dart index be25edd2d5..c84df3b78c 100644 --- a/packages/flutter/test/material/snack_bar_theme_test.dart +++ b/packages/flutter/test/material/snack_bar_theme_test.dart @@ -96,10 +96,43 @@ void main() { ]); }); - testWidgets('Passing no SnackBarThemeData returns defaults', (WidgetTester tester) async { + testWidgets('Material2 - Passing no SnackBarThemeData returns defaults', (WidgetTester tester) async { const String text = 'I am a snack bar.'; - final ThemeData theme = ThemeData(); - final bool material3 = theme.useMaterial3; + await tester.pumpWidget(MaterialApp( + theme: ThemeData(useMaterial3: false), + home: Scaffold( + body: Builder( + builder: (BuildContext context) { + return GestureDetector( + onTap: () { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: const Text(text), + duration: const Duration(seconds: 2), + action: SnackBarAction(label: 'ACTION', onPressed: () {}), + )); + }, + child: const Text('X'), + ); + }, + ), + ), + )); + + await tester.tap(find.text('X')); + await tester.pumpAndSettle(); + + final Material material = _getSnackBarMaterial(tester); + final RenderParagraph content = _getSnackBarTextRenderObject(tester, text); + + expect(content.text.style, Typography.material2018().white.titleMedium); + expect(material.color, const Color(0xFF333333)); + expect(material.elevation, 6.0); + expect(material.shape, null); + }); + + testWidgets('Material3 - Passing no SnackBarThemeData returns defaults', (WidgetTester tester) async { + const String text = 'I am a snack bar.'; + final ThemeData theme = ThemeData(useMaterial3: true); await tester.pumpWidget(MaterialApp( theme: theme, home: Scaffold( @@ -126,10 +159,8 @@ void main() { final Material material = _getSnackBarMaterial(tester); final RenderParagraph content = _getSnackBarTextRenderObject(tester, text); - expect(content.text.style, material3 - ? Typography.material2021().englishLike.bodyMedium?.merge(Typography.material2021().black.bodyMedium).copyWith(color: theme.colorScheme.onInverseSurface, decorationColor: theme.colorScheme.onSurface) - : Typography.material2018().white.titleMedium); - expect(material.color, material3 ? theme.colorScheme.inverseSurface : const Color(0xFF333333)); + expect(content.text.style, Typography.material2021().englishLike.bodyMedium?.merge(Typography.material2021().black.bodyMedium).copyWith(color: theme.colorScheme.onInverseSurface, decorationColor: theme.colorScheme.onSurface)); + expect(material.color, theme.colorScheme.inverseSurface); expect(material.elevation, 6.0); expect(material.shape, null); }); diff --git a/packages/flutter/test/material/stepper_test.dart b/packages/flutter/test/material/stepper_test.dart index d85070b607..e89a8efec1 100644 --- a/packages/flutter/test/material/stepper_test.dart +++ b/packages/flutter/test/material/stepper_test.dart @@ -214,15 +214,13 @@ void main() { expect(find.text('B'), findsOneWidget); }); - testWidgets('Stepper button test', (WidgetTester tester) async { + testWidgets('Material2 - Stepper button test', (WidgetTester tester) async { bool continuePressed = false; bool cancelPressed = false; - final ThemeData theme = ThemeData(); - final bool material3 = theme.useMaterial3; await tester.pumpWidget( MaterialApp( - theme: theme, + theme: ThemeData(useMaterial3: false), home: Material( child: Stepper( type: StepperType.horizontal, @@ -253,8 +251,52 @@ void main() { ), ); - await tester.tap(find.text(material3 ? 'Continue' : 'CONTINUE')); - await tester.tap(find.text(material3 ? 'Cancel' : 'CANCEL')); + await tester.tap(find.text('CONTINUE')); + await tester.tap(find.text('CANCEL')); + + expect(continuePressed, isTrue); + expect(cancelPressed, isTrue); + }); + + testWidgets('Material3 - Stepper button test', (WidgetTester tester) async { + bool continuePressed = false; + bool cancelPressed = false; + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(useMaterial3: true), + home: Material( + child: Stepper( + type: StepperType.horizontal, + onStepContinue: () { + continuePressed = true; + }, + onStepCancel: () { + cancelPressed = true; + }, + steps: const [ + Step( + title: Text('Step 1'), + content: SizedBox( + width: 100.0, + height: 100.0, + ), + ), + Step( + title: Text('Step 2'), + content: SizedBox( + width: 200.0, + height: 200.0, + ), + ), + ], + ), + ), + ), + ); + + await tester.tap(find.text('Continue')); + await tester.tap(find.text('Cancel')); expect(continuePressed, isTrue); expect(cancelPressed, isTrue); @@ -833,7 +875,7 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async expect(tester.takeException(), isNull); }); - testWidgets('Stepper enabled button styles', (WidgetTester tester) async { + testWidgets('Material2 - Stepper enabled button styles', (WidgetTester tester) async { Widget buildFrame(ThemeData theme) { return MaterialApp( theme: theme, @@ -861,15 +903,14 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async const OutlinedBorder buttonShape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2))); - final ThemeData themeLight = ThemeData.light(); - final bool material3Light = themeLight.useMaterial3; + final ThemeData themeLight = ThemeData(useMaterial3: false); await tester.pumpWidget(buildFrame(themeLight)); - final String continueStr = material3Light ? 'Continue' : 'CONTINUE'; - final String cancelStr = material3Light ? 'Cancel' : 'CANCEL'; - final Rect continueButtonRect = material3Light ? const Rect.fromLTRB(24.0, 212.0, 169.0, 260.0) : const Rect.fromLTRB(24.0, 212.0, 168.0, 260.0); - final Rect cancelButtonRect = material3Light ? const Rect.fromLTRB(177.0, 212.0, 294.0, 260.0) : const Rect.fromLTRB(176.0, 212.0, 292.0, 260.0); - expect(buttonMaterial(continueStr).color!.value, material3Light ? themeLight.colorScheme.primary.value : 0xff2196f3); + const String continueStr = 'CONTINUE'; + const String cancelStr = 'CANCEL'; + const Rect continueButtonRect = Rect.fromLTRB(24.0, 212.0, 168.0, 260.0); + const Rect cancelButtonRect = Rect.fromLTRB(176.0, 212.0, 292.0, 260.0); + expect(buttonMaterial(continueStr).color!.value, 0xff2196f3); expect(buttonMaterial(continueStr).textStyle!.color!.value, 0xffffffff); expect(buttonMaterial(continueStr).shape, buttonShape); expect(tester.getRect(find.widgetWithText(TextButton, continueStr)), continueButtonRect); @@ -879,13 +920,12 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async expect(buttonMaterial(cancelStr).shape, buttonShape); expect(tester.getRect(find.widgetWithText(TextButton, cancelStr)), cancelButtonRect); - final ThemeData themeDark = ThemeData.dark(); - final bool material3Dark = themeDark.useMaterial3; + final ThemeData themeDark = ThemeData.dark(useMaterial3: false); await tester.pumpWidget(buildFrame(themeDark)); await tester.pumpAndSettle(); // Complete the theme animation. expect(buttonMaterial(continueStr).color!.value, 0); - expect(buttonMaterial(continueStr).textStyle!.color!.value, material3Dark ? themeDark.colorScheme.onSurface.value : 0xffffffff); + expect(buttonMaterial(continueStr).textStyle!.color!.value, 0xffffffff); expect(buttonMaterial(continueStr).shape, buttonShape); expect(tester.getRect(find.widgetWithText(TextButton, continueStr)), continueButtonRect); @@ -895,7 +935,67 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async expect(tester.getRect(find.widgetWithText(TextButton, cancelStr)), cancelButtonRect); }); - testWidgets('Stepper disabled button styles', (WidgetTester tester) async { + testWidgets('Material3 - Stepper enabled button styles', (WidgetTester tester) async { + Widget buildFrame(ThemeData theme) { + return MaterialApp( + theme: theme, + home: Material( + child: Stepper( + type: StepperType.horizontal, + onStepCancel: () { }, + onStepContinue: () { }, + steps: const [ + Step( + title: Text('step1'), + content: SizedBox(width: 100, height: 100), + ), + ], + ), + ), + ); + } + + Material buttonMaterial(String label) { + return tester.widget( + find.descendant(of: find.widgetWithText(TextButton, label), matching: find.byType(Material)), + ); + } + + const OutlinedBorder buttonShape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2))); + + final ThemeData themeLight = ThemeData(useMaterial3: true); + await tester.pumpWidget(buildFrame(themeLight)); + + const String continueStr = 'Continue'; + const String cancelStr = 'Cancel'; + const Rect continueButtonRect = Rect.fromLTRB(24.0, 212.0, 169.0, 260.0); + const Rect cancelButtonRect = Rect.fromLTRB(177.0, 212.0, 294.0, 260.0); + expect(buttonMaterial(continueStr).color!.value, themeLight.colorScheme.primary.value); + expect(buttonMaterial(continueStr).textStyle!.color!.value, 0xffffffff); + expect(buttonMaterial(continueStr).shape, buttonShape); + expect(tester.getRect(find.widgetWithText(TextButton, continueStr)), continueButtonRect); + + expect(buttonMaterial(cancelStr).color!.value, 0); + expect(buttonMaterial(cancelStr).textStyle!.color!.value, 0x8a000000); + expect(buttonMaterial(cancelStr).shape, buttonShape); + expect(tester.getRect(find.widgetWithText(TextButton, cancelStr)), cancelButtonRect); + + final ThemeData themeDark = ThemeData.dark(useMaterial3: true); + await tester.pumpWidget(buildFrame(themeDark)); + await tester.pumpAndSettle(); // Complete the theme animation. + + expect(buttonMaterial(continueStr).color!.value, 0); + expect(buttonMaterial(continueStr).textStyle!.color!.value, themeDark.colorScheme.onSurface.value); + expect(buttonMaterial(continueStr).shape, buttonShape); + expect(tester.getRect(find.widgetWithText(TextButton, continueStr)), continueButtonRect); + + expect(buttonMaterial(cancelStr).color!.value, 0); + expect(buttonMaterial(cancelStr).textStyle!.color!.value, 0xb3ffffff); + expect(buttonMaterial(cancelStr).shape, buttonShape); + expect(tester.getRect(find.widgetWithText(TextButton, cancelStr)), cancelButtonRect); + }); + + testWidgets('Material2 - Stepper disabled button styles', (WidgetTester tester) async { Widget buildFrame(ThemeData theme) { return MaterialApp( theme: theme, @@ -919,28 +1019,86 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async ); } - final ThemeData themeLight = ThemeData.light(); - final bool material3Light = themeLight.useMaterial3; + final ThemeData themeLight = ThemeData(useMaterial3: false); await tester.pumpWidget(buildFrame(themeLight)); - final String continueStr = material3Light ? 'Continue' : 'CONTINUE'; - final String cancelStr = material3Light ? 'Cancel' : 'CANCEL'; + const String continueStr = 'CONTINUE'; + const String cancelStr = 'CANCEL'; expect(buttonMaterial(continueStr).color!.value, 0); - expect(buttonMaterial(continueStr).textStyle!.color!.value, material3Light ? themeLight.colorScheme.onSurface.withOpacity(0.38).value : 0x61000000); + expect(buttonMaterial(continueStr).textStyle!.color!.value, 0x61000000); expect(buttonMaterial(cancelStr).color!.value, 0); - expect(buttonMaterial(cancelStr).textStyle!.color!.value, material3Light ? themeLight.colorScheme.onSurface.withOpacity(0.38).value : 0x61000000); + expect(buttonMaterial(cancelStr).textStyle!.color!.value, 0x61000000); - final ThemeData themeDark = ThemeData.dark(); - final bool material3Dark = themeDark.useMaterial3; + final ThemeData themeDark = ThemeData.dark(useMaterial3: false); await tester.pumpWidget(buildFrame(themeDark)); await tester.pumpAndSettle(); // Complete the theme animation. expect(buttonMaterial(continueStr).color!.value, 0); - expect(buttonMaterial(continueStr).textStyle!.color!.value, material3Dark ? themeDark.colorScheme.onSurface.withOpacity(0.38).value : 0x61ffffff); + expect(buttonMaterial(continueStr).textStyle!.color!.value, 0x61ffffff); expect(buttonMaterial(cancelStr).color!.value, 0); - expect(buttonMaterial(cancelStr).textStyle!.color!.value, material3Dark ? themeDark.colorScheme.onSurface.withOpacity(0.38).value : 0x61ffffff); + expect(buttonMaterial(cancelStr).textStyle!.color!.value, 0x61ffffff); + }); + + testWidgets('Material3 - Stepper disabled button styles', (WidgetTester tester) async { + Widget buildFrame(ThemeData theme) { + return MaterialApp( + theme: theme, + home: Material( + child: Stepper( + type: StepperType.horizontal, + steps: const [ + Step( + title: Text('step1'), + content: SizedBox(width: 100, height: 100), + ), + ], + ), + ), + ); + } + + Material buttonMaterial(String label) { + return tester.widget( + find.descendant(of: find.widgetWithText(TextButton, label), matching: find.byType(Material)), + ); + } + + final ThemeData themeLight = ThemeData(useMaterial3: true); + final ColorScheme colorsLight = themeLight.colorScheme; + await tester.pumpWidget(buildFrame(themeLight)); + + const String continueStr = 'Continue'; + const String cancelStr = 'Cancel'; + expect(buttonMaterial(continueStr).color!.value, 0); + expect( + buttonMaterial(continueStr).textStyle!.color!.value, + colorsLight.onSurface.withOpacity(0.38).value, + ); + + expect(buttonMaterial(cancelStr).color!.value, 0); + expect( + buttonMaterial(cancelStr).textStyle!.color!.value, + colorsLight.onSurface.withOpacity(0.38).value, + ); + + final ThemeData themeDark = ThemeData.dark(useMaterial3: true); + final ColorScheme colorsDark = themeDark.colorScheme; + await tester.pumpWidget(buildFrame(themeDark)); + await tester.pumpAndSettle(); // Complete the theme animation. + + expect(buttonMaterial(continueStr).color!.value, 0); + expect( + buttonMaterial(continueStr).textStyle!.color!.value, + colorsDark.onSurface.withOpacity(0.38).value, + ); + + expect(buttonMaterial(cancelStr).color!.value, 0); + expect( + buttonMaterial(cancelStr).textStyle!.color!.value, + colorsDark.onSurface.withOpacity(0.38).value, + ); }); testWidgets('Vertical and Horizontal Stepper physics test', (WidgetTester tester) async {