Unverified Commit 0ef46388 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Update `ToggleButtons`, `ExpansionPanel`, and `ExpandIcon` tests for Material 3 (#141868)

Updated unit tests for `ToggleButtons`, `ExpansionPanel`, and `ExpandIcon` to have M2 and M3 versions.

More info in #139076
parent 5caa8b89
......@@ -73,9 +73,9 @@ void main() {
expect(iconTheme.data.color, equals(Colors.white60));
});
testWidgets('ExpandIcon disabled', (WidgetTester tester) async {
testWidgets('Material2 - ExpandIcon disabled', (WidgetTester tester) async {
IconTheme iconTheme;
// Light mode test
// Test light mode.
await tester.pumpWidget(wrap(
theme: ThemeData(useMaterial3: false),
child: const ExpandIcon(onPressed: null),
......@@ -85,7 +85,7 @@ void main() {
iconTheme = tester.firstWidget(find.byType(IconTheme).last);
expect(iconTheme.data.color, equals(Colors.black38));
// Dark mode test
// Test dark mode.
await tester.pumpWidget(wrap(
child: const ExpandIcon(onPressed: null),
theme: ThemeData(useMaterial3: false, brightness: Brightness.dark),
......@@ -96,6 +96,37 @@ void main() {
expect(iconTheme.data.color, equals(Colors.white38));
});
testWidgets('Material3 - ExpandIcon disabled', (WidgetTester tester) async {
ThemeData theme = ThemeData();
IconTheme iconTheme;
// Test light mode.
await tester.pumpWidget(wrap(
theme: theme,
child: const ExpandIcon(onPressed: null),
));
await tester.pumpAndSettle();
iconTheme = tester.firstWidget(find.byType(IconTheme).last);
expect(
iconTheme.data.color,
equals(theme.colorScheme.onSurface.withOpacity(0.38)),
);
theme = ThemeData(brightness: Brightness.dark);
// Test dark mode.
await tester.pumpWidget(wrap(
theme: theme,
child: const ExpandIcon(onPressed: null),
));
await tester.pumpAndSettle();
iconTheme = tester.firstWidget(find.byType(IconTheme).last);
expect(
iconTheme.data.color,
equals(theme.colorScheme.onSurface.withOpacity(0.38)),
);
});
testWidgets('ExpandIcon test isExpanded does not trigger callback', (WidgetTester tester) async {
bool expanded = false;
......@@ -173,7 +204,7 @@ void main() {
expect(icon.size, 48);
});
testWidgets('ExpandIcon has correct semantic hints', (WidgetTester tester) async {
testWidgets('Material2 - ExpandIcon has correct semantic hints', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
const DefaultMaterialLocalizations localizations = DefaultMaterialLocalizations();
await tester.pumpWidget(wrap(
......@@ -194,6 +225,7 @@ void main() {
));
await tester.pumpWidget(wrap(
theme: ThemeData(useMaterial3: false),
child: ExpandIcon(
onPressed: (bool _) { },
),
......@@ -210,6 +242,52 @@ void main() {
handle.dispose();
});
testWidgets('Material3 - ExpandIcon has correct semantic hints', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
const DefaultMaterialLocalizations localizations = DefaultMaterialLocalizations();
await tester.pumpWidget(wrap(
child: ExpandIcon(
isExpanded: true,
onPressed: (bool _) { },
),
));
expect(tester.getSemantics(find.byType(ExpandIcon)), matchesSemantics(
onTapHint: localizations.expandedIconTapHint,
children: <Matcher>[
matchesSemantics(
hasTapAction: true,
hasEnabledState: true,
isEnabled: true,
isFocusable: true,
isButton: true,
),
],
));
await tester.pumpWidget(wrap(
child: ExpandIcon(
onPressed: (bool _) { },
),
));
expect(tester.getSemantics(find.byType(ExpandIcon)), matchesSemantics(
onTapHint: localizations.collapsedIconTapHint,
children: <Matcher>[
matchesSemantics(
hasTapAction: true,
hasEnabledState: true,
isEnabled: true,
isFocusable: true,
isButton: true,
),
],
));
handle.dispose();
});
testWidgets('ExpandIcon uses custom icon color and expanded icon color', (WidgetTester tester) async {
bool expanded = false;
IconTheme iconTheme;
......
......@@ -178,7 +178,7 @@ void main() {
expect(box.size.height - oldHeight, greaterThanOrEqualTo(100.0)); // 100 + some margin
});
testWidgets('ExpansionPanelList does not merge header when canTapOnHeader is false', (WidgetTester tester) async {
testWidgets('Material2 - ExpansionPanelList does not merge header when canTapOnHeader is false', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
final Key headerKey = UniqueKey();
await tester.pumpWidget(
......@@ -226,6 +226,58 @@ void main() {
handle.dispose();
});
testWidgets('Material3 - ExpansionPanelList does not merge header when canTapOnHeader is false', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
final Key headerKey = UniqueKey();
await tester.pumpWidget(
MaterialApp(
home: ExpansionPanelListSemanticsTest(headerKey: headerKey),
),
);
// Make sure custom gesture detector widget is clickable.
await tester.tap(find.text('head1'));
await tester.pump();
final ExpansionPanelListSemanticsTestState state =
tester.state(find.byType(ExpansionPanelListSemanticsTest));
expect(state.headerTapped, true);
// Check the expansion icon semantics does not merged with header widget.
final Finder expansionIcon = find.descendant(
of: find.ancestor(
of: find.byKey(headerKey),
matching: find.byType(Row),
),
matching: find.byType(ExpandIcon),
);
expect(tester.getSemantics(expansionIcon), matchesSemantics(
label: 'Expand',
children: <Matcher>[
matchesSemantics(
isButton: true,
hasEnabledState: true,
isEnabled: true,
isFocusable: true,
hasTapAction: true,
),
],
));
// Check custom header widget semantics is preserved.
final Finder headerWidget = find.descendant(
of: find.byKey(headerKey),
matching: find.byType(RichText),
);
expect(tester.getSemantics(headerWidget), matchesSemantics(
label: 'head1',
hasTapAction: true,
));
handle.dispose();
});
testWidgets('Multiple Panel List test', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
......@@ -998,7 +1050,7 @@ void main() {
await tester.pumpAndSettle();
});
testWidgets('Panel header has semantics, canTapOnHeader = false ', (WidgetTester tester) async {
testWidgets('Material2 - Panel header has semantics, canTapOnHeader = false', (WidgetTester tester) async {
const Key expandedKey = Key('expanded');
const Key collapsedKey = Key('collapsed');
const DefaultMaterialLocalizations localizations = DefaultMaterialLocalizations();
......@@ -1083,6 +1135,99 @@ void main() {
handle.dispose();
});
testWidgets('Material3 - Panel header has semantics, canTapOnHeader = false', (WidgetTester tester) async {
const Key expandedKey = Key('expanded');
const Key collapsedKey = Key('collapsed');
const DefaultMaterialLocalizations localizations = DefaultMaterialLocalizations();
final SemanticsHandle handle = tester.ensureSemantics();
final List<ExpansionPanel> demoItems = <ExpansionPanel>[
ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return const Text('Expanded', key: expandedKey);
},
body: const SizedBox(height: 100.0),
isExpanded: true,
),
ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return const Text('Collapsed', key: collapsedKey);
},
body: const SizedBox(height: 100.0),
),
];
final ExpansionPanelList expansionList = ExpansionPanelList(
children: demoItems,
);
await tester.pumpWidget(
MaterialApp(
home: SingleChildScrollView(
child: expansionList,
),
),
);
// Check the semantics of [ExpandIcon] for expanded panel.
final Finder expandedIcon = find.descendant(
of: find.ancestor(
of: find.byKey(expandedKey),
matching: find.byType(Row),
),
matching: find.byType(ExpandIcon),
);
expect(tester.getSemantics(expandedIcon), matchesSemantics(
label: 'Collapse',
onTapHint: localizations.expandedIconTapHint,
children: <Matcher>[
matchesSemantics(
isButton: true,
hasEnabledState: true,
isEnabled: true,
isFocusable: true,
hasTapAction: true,
),
],
));
// Check the semantics of the header widget for expanded panel.
final Finder expandedHeader = find.byKey(expandedKey);
expect(tester.getSemantics(expandedHeader), matchesSemantics(
label: 'Expanded',
));
// Check the semantics of [ExpandIcon] for collapsed panel.
final Finder collapsedIcon = find.descendant(
of: find.ancestor(
of: find.byKey(collapsedKey),
matching: find.byType(Row),
),
matching: find.byType(ExpandIcon),
);
expect(tester.getSemantics(collapsedIcon), matchesSemantics(
label: 'Expand',
onTapHint: localizations.collapsedIconTapHint,
children: <Matcher>[
matchesSemantics(
isButton: true,
hasEnabledState: true,
isEnabled: true,
isFocusable: true,
hasTapAction: true,
),
],
));
// Check the semantics of the header widget for expanded panel.
final Finder collapsedHeader = find.byKey(collapsedKey);
expect(tester.getSemantics(collapsedHeader), matchesSemantics(
label: 'Collapsed',
));
handle.dispose();
});
testWidgets('Panel header has semantics, canTapOnHeader = true', (WidgetTester tester) async {
const Key expandedKey = Key('expanded');
const Key collapsedKey = Key('collapsed');
......
......@@ -7,6 +7,7 @@
@Tags(<String>['reduced-test-set'])
library;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
......@@ -16,13 +17,12 @@ import '../widgets/semantics_tester.dart';
const double _defaultBorderWidth = 1.0;
Widget boilerplate({
bool? useMaterial3,
ThemeData? theme,
MaterialTapTargetSize? tapTargetSize,
required Widget child,
}) {
return Theme(
data: ThemeData(
useMaterial3: useMaterial3,
data: theme ?? ThemeData(
materialTapTargetSize: tapTargetSize,
),
child: Directionality(
......@@ -1273,12 +1273,12 @@ void main() {
}
});
testWidgets('ToggleButtons text baseline alignment', (WidgetTester tester) async {
testWidgets('Material2 - ToggleButtons text baseline alignment', (WidgetTester tester) async {
// The point size of the fonts must be a multiple of 4 until
// https://github.com/flutter/flutter/issues/122066 is resolved.
await tester.pumpWidget(
boilerplate(
useMaterial3: false,
theme: ThemeData(useMaterial3: false),
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
......@@ -1323,6 +1323,55 @@ void main() {
expect(firstToggleButtonDy, textDy - 5.0);
});
testWidgets('Material3 - ToggleButtons text baseline alignment', (WidgetTester tester) async {
// The point size of the fonts must be a multiple of 4 until
// https://github.com/flutter/flutter/issues/122066 is resolved.
await tester.pumpWidget(
boilerplate(
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: <Widget>[
ToggleButtons(
borderWidth: 5.0,
isSelected: const <bool>[false, true],
children: const <Widget>[
Text('First child', style: TextStyle(fontFamily: 'FlutterTest', fontSize: 8.0)),
Text('Second child', style: TextStyle(fontFamily: 'FlutterTest', fontSize: 8.0)),
],
),
ElevatedButton(
onPressed: null,
style: ElevatedButton.styleFrom(textStyle: const TextStyle(
fontFamily: 'FlutterTest',
fontSize: 20.0,
)),
child: const Text('Elevated Button'),
),
const Text('Text', style: TextStyle(fontFamily: 'FlutterTest', fontSize: 28.0)),
],
),
),
);
// The test font extends 0.25 * fontSize below the baseline.
// So the three row elements line up like this:
//
// ToggleButton MaterialButton Text
// ------------------------------------ baseline
// 2.0 5.0 7.0 space below the baseline = 0.25 * fontSize
// ------------------------------------ widget text dy values
final double firstToggleButtonDy = tester.getBottomLeft(find.text('First child')).dy;
final double secondToggleButtonDy = tester.getBottomLeft(find.text('Second child')).dy;
final double elevatedButtonDy = tester.getBottomLeft(find.text('Elevated Button')).dy;
final double textDy = tester.getBottomLeft(find.text('Text')).dy;
expect(firstToggleButtonDy, secondToggleButtonDy);
expect(firstToggleButtonDy, closeTo(elevatedButtonDy - 1.7, 0.1));
expect(firstToggleButtonDy, closeTo(textDy - 9.7, 0.1));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgets('Directionality test', (WidgetTester tester) async {
await tester.pumpWidget(
Material(
......@@ -1564,10 +1613,10 @@ void main() {
},
);
testWidgets('Tap target size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
testWidgets('Material2 - Tap target size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) {
return boilerplate(
useMaterial3: false,
theme: ThemeData(useMaterial3: false),
tapTargetSize: tapTargetSize,
child: ToggleButtons(
key: key,
......@@ -1589,13 +1638,40 @@ void main() {
final Key key2 = UniqueKey();
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.shrinkWrap, key2));
expect(tester.getSize(find.byKey(key2)), const Size(228.0, 34.0));
expect(tester.getSize(find.byKey(key2)), const Size(228.0, 48.0));
});
testWidgets('Material3 - Tap target size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) {
return boilerplate(
tapTargetSize: tapTargetSize,
child: ToggleButtons(
key: key,
constraints: const BoxConstraints(minWidth: 32.0, minHeight: 32.0),
isSelected: const <bool>[false, true, false],
onPressed: (int index) {},
children: const <Widget>[
Text('First'),
Text('Second'),
Text('Third'),
],
),
);
}
final Key key1 = UniqueKey();
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.padded, key1));
expect(tester.getSize(find.byKey(key1)), const Size(232.0, 48.0));
final Key key2 = UniqueKey();
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.shrinkWrap, key2));
expect(tester.getSize(find.byKey(key2)), const Size(232.0, 34.0));
});
testWidgets('Tap target size is configurable', (WidgetTester tester) async {
testWidgets('Material2 - Tap target size is configurable', (WidgetTester tester) async {
Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) {
return boilerplate(
useMaterial3: false,
theme: ThemeData(useMaterial3: false),
child: ToggleButtons(
key: key,
tapTargetSize: tapTargetSize,
......@@ -1620,6 +1696,33 @@ void main() {
expect(tester.getSize(find.byKey(key2)), const Size(228.0, 34.0));
});
testWidgets('Material3 - Tap target size is configurable', (WidgetTester tester) async {
Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) {
return boilerplate(
child: ToggleButtons(
key: key,
tapTargetSize: tapTargetSize,
constraints: const BoxConstraints(minWidth: 32.0, minHeight: 32.0),
isSelected: const <bool>[false, true, false],
onPressed: (int index) {},
children: const <Widget>[
Text('First'),
Text('Second'),
Text('Third'),
],
),
);
}
final Key key1 = UniqueKey();
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.padded, key1));
expect(tester.getSize(find.byKey(key1)), const Size(232.0, 48.0));
final Key key2 = UniqueKey();
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.shrinkWrap, key2));
expect(tester.getSize(find.byKey(key2)), const Size(232.0, 34.0));
});
testWidgets('Tap target size is configurable for vertical axis', (WidgetTester tester) async {
Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) {
return boilerplate(
......@@ -1649,11 +1752,55 @@ void main() {
});
// Regression test for https://github.com/flutter/flutter/issues/73725
testWidgets('Border radius paint test when there is only one button', (WidgetTester tester) async {
testWidgets('Material2 - Border radius paint test when there is only one button', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: false);
await tester.pumpWidget(
boilerplate(
useMaterial3: false,
theme: theme,
child: RepaintBoundary(
child: ToggleButtons(
borderRadius: const BorderRadius.all(Radius.circular(7.0)),
isSelected: const <bool>[true],
onPressed: (int index) {},
children: const <Widget>[
Text('First child'),
],
),
),
),
);
// The only button should be laid out at the center of the screen.
expect(tester.getCenter(find.text('First child')), const Offset(400.0, 300.0));
final List<RenderObject> toggleButtonRenderObject = tester.allRenderObjects.where((RenderObject object) {
return object.runtimeType.toString() == '_SelectToggleButtonRenderObject';
}).toSet().toList();
// The first button paints the left, top and right sides with a path.
expect(
toggleButtonRenderObject[0],
paints
// physical model paints
..path()
// left side, top and right - enabled.
..path(
style: PaintingStyle.stroke,
color: theme.colorScheme.onSurface.withOpacity(0.12),
strokeWidth: _defaultBorderWidth,
),
);
await expectLater(
find.byType(RepaintBoundary),
matchesGoldenFile('m2_toggle_buttons.oneButton.boardsPaint.png'),
);
});
testWidgets('Material3 - Border radius paint test when there is only one button', (WidgetTester tester) async {
final ThemeData theme = ThemeData();
await tester.pumpWidget(
boilerplate(
child: RepaintBoundary(
child: ToggleButtons(
borderRadius: const BorderRadius.all(Radius.circular(7.0)),
......@@ -1690,14 +1837,41 @@ void main() {
await expectLater(
find.byType(RepaintBoundary),
matchesGoldenFile('toggle_buttons.oneButton.boardsPaint.png'),
matchesGoldenFile('m3_toggle_buttons.oneButton.boardsPaint.png'),
);
});
testWidgets('Material2 - Border radius paint test when Radius.x or Radius.y equal 0.0', (WidgetTester tester) async {
await tester.pumpWidget(
boilerplate(
theme: ThemeData(useMaterial3: false),
child: RepaintBoundary(
child: ToggleButtons(
borderRadius: const BorderRadius.only(
topRight: Radius.elliptical(10, 0),
topLeft: Radius.elliptical(0, 10),
bottomRight: Radius.elliptical(0, 10),
bottomLeft: Radius.elliptical(10, 0),
),
isSelected: const <bool>[true],
onPressed: (int index) {},
children: const <Widget>[
Text('First child'),
],
),
),
),
);
await expectLater(
find.byType(RepaintBoundary),
matchesGoldenFile('m2_toggle_buttons.oneButton.boardsPaint2.png'),
);
});
testWidgets('Border radius paint test when Radius.x or Radius.y equal 0.0', (WidgetTester tester) async {
testWidgets('Material3 - Border radius paint test when Radius.x or Radius.y equal 0.0', (WidgetTester tester) async {
await tester.pumpWidget(
boilerplate(
useMaterial3: false,
child: RepaintBoundary(
child: ToggleButtons(
borderRadius: const BorderRadius.only(
......@@ -1718,7 +1892,7 @@ void main() {
await expectLater(
find.byType(RepaintBoundary),
matchesGoldenFile('toggle_buttons.oneButton.boardsPaint2.png'),
matchesGoldenFile('m3_toggle_buttons.oneButton.boardsPaint2.png'),
);
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment