blob: 08efb09f05a84c13d17f8859174cb2757b4d8db5 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import '../widgets/semantics_tester.dart';
const Key avatarA = Key('A');
const Key avatarC = Key('C');
const Key avatarD = Key('D');
Future<void> pumpTestWidget(
WidgetTester tester, {
bool withName = true,
bool withEmail = true,
bool withOnDetailsPressedHandler = true,
Size otherAccountsPictureSize = const Size.square(40.0),
Size currentAccountPictureSize = const Size.square(72.0),
Color? primaryColor,
Color? colorSchemePrimary,
}) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
primaryColor: primaryColor,
colorScheme: const ColorScheme.light().copyWith(primary: colorSchemePrimary),
),
home: MediaQuery(
data: const MediaQueryData(
padding: EdgeInsets.only(
left: 10.0,
top: 20.0,
right: 30.0,
bottom: 40.0,
),
),
child: Material(
child: Center(
child: UserAccountsDrawerHeader(
onDetailsPressed: withOnDetailsPressedHandler ? () { } : null,
currentAccountPictureSize: currentAccountPictureSize,
otherAccountsPicturesSize: otherAccountsPictureSize,
currentAccountPicture: const ExcludeSemantics(
child: CircleAvatar(
key: avatarA,
child: Text('A'),
),
),
otherAccountsPictures: const <Widget>[
CircleAvatar(
child: Text('B'),
),
CircleAvatar(
key: avatarC,
child: Text('C'),
),
CircleAvatar(
key: avatarD,
child: Text('D'),
),
CircleAvatar(
child: Text('E'),
),
],
accountName: withName ? const Text('name') : null,
accountEmail: withEmail ? const Text('email') : null,
),
),
),
),
),
);
}
void main() {
// Find the exact transform which is the descendant of [UserAccountsDrawerHeader].
final Finder findTransform = find.descendant(
of: find.byType(UserAccountsDrawerHeader),
matching: find.byType(Transform),
);
testWidgets('UserAccountsDrawerHeader inherits ColorScheme.primary', (WidgetTester tester) async {
const Color primaryColor = Color(0xff00ff00);
const Color colorSchemePrimary = Color(0xff0000ff);
await pumpTestWidget(tester, primaryColor: primaryColor, colorSchemePrimary: colorSchemePrimary);
final BoxDecoration? boxDecoration = tester.widget<DrawerHeader>(
find.byType(DrawerHeader),
).decoration as BoxDecoration?;
expect(boxDecoration?.color == primaryColor, false);
expect(boxDecoration?.color == colorSchemePrimary, true);
});
testWidgets('UserAccountsDrawerHeader test', (WidgetTester tester) async {
await pumpTestWidget(tester);
expect(find.text('A'), findsOneWidget);
expect(find.text('B'), findsOneWidget);
expect(find.text('C'), findsOneWidget);
expect(find.text('D'), findsOneWidget);
expect(find.text('E'), findsNothing);
expect(find.text('name'), findsOneWidget);
expect(find.text('email'), findsOneWidget);
RenderBox box = tester.renderObject(find.byKey(avatarA));
expect(box.size.width, equals(72.0));
expect(box.size.height, equals(72.0));
box = tester.renderObject(find.byKey(avatarC));
expect(box.size.width, equals(40.0));
expect(box.size.height, equals(40.0));
// Verify height = height + top padding + bottom margin + bottom edge)
box = tester.renderObject(find.byType(UserAccountsDrawerHeader));
expect(box.size.height, equals(160.0 + 20.0 + 8.0 + 1.0));
final Offset topLeft = tester.getTopLeft(find.byType(UserAccountsDrawerHeader));
final Offset topRight = tester.getTopRight(find.byType(UserAccountsDrawerHeader));
final Offset avatarATopLeft = tester.getTopLeft(find.byKey(avatarA));
final Offset avatarDTopRight = tester.getTopRight(find.byKey(avatarD));
final Offset avatarCTopRight = tester.getTopRight(find.byKey(avatarC));
expect(avatarATopLeft.dx - topLeft.dx, equals(16.0 + 10.0)); // left padding
expect(avatarATopLeft.dy - topLeft.dy, equals(16.0 + 20.0)); // add top padding
expect(topRight.dx - avatarDTopRight.dx, equals(16.0 + 30.0)); // right padding
expect(avatarDTopRight.dy - topRight.dy, equals(16.0 + 20.0)); // add top padding
expect(avatarDTopRight.dx - avatarCTopRight.dx, equals(40.0 + 16.0)); // size + space between
});
testWidgets('UserAccountsDrawerHeader change default size test', (WidgetTester tester) async {
const Size currentAccountPictureSize = Size.square(60.0);
const Size otherAccountsPictureSize = Size.square(30.0);
await pumpTestWidget(
tester,
currentAccountPictureSize: currentAccountPictureSize,
otherAccountsPictureSize: otherAccountsPictureSize,
);
final RenderBox currentAccountRenderBox = tester.renderObject(find.byKey(avatarA));
final RenderBox otherAccountRenderBox = tester.renderObject(find.byKey(avatarC));
expect(currentAccountRenderBox.size, currentAccountPictureSize);
expect(otherAccountRenderBox.size, otherAccountsPictureSize);
});
testWidgets('UserAccountsDrawerHeader icon rotation test', (WidgetTester tester) async {
await pumpTestWidget(tester);
Transform transformWidget = tester.firstWidget(findTransform);
// Icon is right side up.
expect(transformWidget.transform.getRotation()[0], 1.0);
expect(transformWidget.transform.getRotation()[4], 1.0);
await tester.tap(find.byType(Icon));
await tester.pump();
await tester.pump(const Duration(milliseconds: 10));
expect(tester.hasRunningAnimations, isTrue);
await tester.pumpAndSettle();
await tester.pump();
transformWidget = tester.firstWidget(findTransform);
// Icon has rotated 180 degrees.
expect(transformWidget.transform.getRotation()[0], -1.0);
expect(transformWidget.transform.getRotation()[4], -1.0);
await tester.tap(find.byType(Icon));
await tester.pump();
await tester.pump(const Duration(milliseconds: 10));
expect(tester.hasRunningAnimations, isTrue);
await tester.pumpAndSettle();
await tester.pump();
transformWidget = tester.firstWidget(findTransform);
// Icon has rotated 180 degrees back to the original position.
expect(transformWidget.transform.getRotation()[0], 1.0);
expect(transformWidget.transform.getRotation()[4], 1.0);
});
// Regression test for https://github.com/flutter/flutter/issues/25801.
testWidgets('UserAccountsDrawerHeader icon does not rotate after setState', (WidgetTester tester) async {
late StateSetter testSetState;
await tester.pumpWidget(MaterialApp(
home: Material(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
testSetState = setState;
return UserAccountsDrawerHeader(
onDetailsPressed: () { },
accountName: const Text('name'),
accountEmail: const Text('email'),
);
},
),
),
));
Transform transformWidget = tester.firstWidget(findTransform);
// Icon is right side up.
expect(transformWidget.transform.getRotation()[0], 1.0);
expect(transformWidget.transform.getRotation()[4], 1.0);
testSetState(() { });
await tester.pump(const Duration(milliseconds: 10));
expect(tester.hasRunningAnimations, isFalse);
expect(await tester.pumpAndSettle(), 1);
transformWidget = tester.firstWidget(findTransform);
// Icon has not rotated.
expect(transformWidget.transform.getRotation()[0], 1.0);
expect(transformWidget.transform.getRotation()[4], 1.0);
});
testWidgets('UserAccountsDrawerHeader icon rotation test speeeeeedy', (WidgetTester tester) async {
await pumpTestWidget(tester);
Transform transformWidget = tester.firstWidget(findTransform);
// Icon is right side up.
expect(transformWidget.transform.getRotation()[0], 1.0);
expect(transformWidget.transform.getRotation()[4], 1.0);
// Icon starts to rotate down.
await tester.tap(find.byType(Icon));
await tester.pump();
await tester.pump(const Duration(milliseconds: 100));
expect(tester.hasRunningAnimations, isTrue);
// Icon starts to rotate up mid animation.
await tester.tap(find.byType(Icon));
await tester.pump();
await tester.pump(const Duration(milliseconds: 100));
expect(tester.hasRunningAnimations, isTrue);
// Icon starts to rotate down again still mid animation.
await tester.tap(find.byType(Icon));
await tester.pump();
await tester.pump(const Duration(milliseconds: 100));
expect(tester.hasRunningAnimations, isTrue);
// Icon starts to rotate up to its original position mid animation.
await tester.tap(find.byType(Icon));
await tester.pump();
await tester.pump(const Duration(milliseconds: 100));
expect(tester.hasRunningAnimations, isTrue);
await tester.pumpAndSettle();
await tester.pump();
transformWidget = tester.firstWidget(findTransform);
// Icon has rotated 180 degrees back to the original position.
expect(transformWidget.transform.getRotation()[0], 1.0);
expect(transformWidget.transform.getRotation()[4], 1.0);
});
testWidgets('UserAccountsDrawerHeader icon color changes', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Material(
child: UserAccountsDrawerHeader(
onDetailsPressed: () {},
accountName: const Text('name'),
accountEmail: const Text('email'),
),
),
));
Icon iconWidget = tester.firstWidget(find.byType(Icon));
// Default icon color is white.
expect(iconWidget.color, Colors.white);
const Color arrowColor = Colors.red;
await tester.pumpWidget(MaterialApp(
home: Material(
child: UserAccountsDrawerHeader(
onDetailsPressed: () { },
accountName: const Text('name'),
accountEmail: const Text('email'),
arrowColor: arrowColor,
),
),
));
iconWidget = tester.firstWidget(find.byType(Icon));
expect(iconWidget.color, arrowColor);
});
testWidgets('UserAccountsDrawerHeader null parameters LTR', (WidgetTester tester) async {
Widget buildFrame({
Widget? currentAccountPicture,
List<Widget>? otherAccountsPictures,
Widget? accountName,
Widget? accountEmail,
VoidCallback? onDetailsPressed,
EdgeInsets? margin,
}) {
return MaterialApp(
home: Material(
child: Center(
child: UserAccountsDrawerHeader(
currentAccountPicture: currentAccountPicture,
otherAccountsPictures: otherAccountsPictures,
accountName: accountName,
accountEmail: accountEmail,
onDetailsPressed: onDetailsPressed,
margin: margin,
),
),
),
);
}
await tester.pumpWidget(buildFrame());
final RenderBox box = tester.renderObject(find.byType(UserAccountsDrawerHeader));
expect(box.size.height, equals(160.0 + 1.0)); // height + bottom edge)
expect(find.byType(Icon), findsNothing);
await tester.pumpWidget(buildFrame(
onDetailsPressed: () { },
));
expect(find.byType(Icon), findsOneWidget);
await tester.pumpWidget(buildFrame(
accountName: const Text('accountName'),
onDetailsPressed: () { },
));
expect(
tester.getCenter(find.text('accountName')).dy,
tester.getCenter(find.byType(Icon)).dy,
);
expect(
tester.getCenter(find.text('accountName')).dx,
lessThan(tester.getCenter(find.byType(Icon)).dx),
);
await tester.pumpWidget(buildFrame(
accountEmail: const Text('accountEmail'),
onDetailsPressed: () { },
));
expect(
tester.getCenter(find.text('accountEmail')).dy,
tester.getCenter(find.byType(Icon)).dy,
);
expect(
tester.getCenter(find.text('accountEmail')).dx,
lessThan(tester.getCenter(find.byType(Icon)).dx),
);
await tester.pumpWidget(buildFrame(
accountName: const Text('accountName'),
accountEmail: const Text('accountEmail'),
onDetailsPressed: () { },
));
expect(
tester.getCenter(find.text('accountEmail')).dy,
tester.getCenter(find.byType(Icon)).dy,
);
expect(
tester.getCenter(find.text('accountEmail')).dx,
lessThan(tester.getCenter(find.byType(Icon)).dx),
);
expect(
tester.getBottomLeft(find.text('accountEmail')).dy,
greaterThan(tester.getBottomLeft(find.text('accountName')).dy),
);
expect(
tester.getBottomLeft(find.text('accountEmail')).dx,
tester.getBottomLeft(find.text('accountName')).dx,
);
await tester.pumpWidget(buildFrame(
currentAccountPicture: const CircleAvatar(child: Text('A')),
));
expect(find.text('A'), findsOneWidget);
await tester.pumpWidget(buildFrame(
otherAccountsPictures: <Widget>[const CircleAvatar(child: Text('A'))],
));
expect(find.text('A'), findsOneWidget);
const Key avatarA = Key('A');
await tester.pumpWidget(buildFrame(
currentAccountPicture: const CircleAvatar(key: avatarA, child: Text('A')),
accountName: const Text('accountName'),
));
expect(
tester.getBottomLeft(find.byKey(avatarA)).dx,
tester.getBottomLeft(find.text('accountName')).dx,
);
expect(
tester.getBottomLeft(find.text('accountName')).dy,
greaterThan(tester.getBottomLeft(find.byKey(avatarA)).dy),
);
});
testWidgets('UserAccountsDrawerHeader null parameters RTL', (WidgetTester tester) async {
Widget buildFrame({
Widget? currentAccountPicture,
List<Widget>? otherAccountsPictures,
Widget? accountName,
Widget? accountEmail,
VoidCallback? onDetailsPressed,
EdgeInsets? margin,
}) {
return MaterialApp(
home: Directionality(
textDirection: TextDirection.rtl,
child: Material(
child: Center(
child: UserAccountsDrawerHeader(
currentAccountPicture: currentAccountPicture,
otherAccountsPictures: otherAccountsPictures,
accountName: accountName,
accountEmail: accountEmail,
onDetailsPressed: onDetailsPressed,
margin: margin,
),
),
),
),
);
}
await tester.pumpWidget(buildFrame());
final RenderBox box = tester.renderObject(find.byType(UserAccountsDrawerHeader));
expect(box.size.height, equals(160.0 + 1.0)); // height + bottom edge)
expect(find.byType(Icon), findsNothing);
await tester.pumpWidget(buildFrame(
onDetailsPressed: () { },
));
expect(find.byType(Icon), findsOneWidget);
await tester.pumpWidget(buildFrame(
accountName: const Text('accountName'),
onDetailsPressed: () { },
));
expect(
tester.getCenter(find.text('accountName')).dy,
tester.getCenter(find.byType(Icon)).dy,
);
expect(
tester.getCenter(find.text('accountName')).dx,
greaterThan(tester.getCenter(find.byType(Icon)).dx),
);
await tester.pumpWidget(buildFrame(
accountEmail: const Text('accountEmail'),
onDetailsPressed: () { },
));
expect(
tester.getCenter(find.text('accountEmail')).dy,
tester.getCenter(find.byType(Icon)).dy,
);
expect(
tester.getCenter(find.text('accountEmail')).dx,
greaterThan(tester.getCenter(find.byType(Icon)).dx),
);
await tester.pumpWidget(buildFrame(
accountName: const Text('accountName'),
accountEmail: const Text('accountEmail'),
onDetailsPressed: () { },
));
expect(
tester.getCenter(find.text('accountEmail')).dy,
tester.getCenter(find.byType(Icon)).dy,
);
expect(
tester.getCenter(find.text('accountEmail')).dx,
greaterThan(tester.getCenter(find.byType(Icon)).dx),
);
expect(
tester.getBottomLeft(find.text('accountEmail')).dy,
greaterThan(tester.getBottomLeft(find.text('accountName')).dy),
);
expect(
tester.getBottomRight(find.text('accountEmail')).dx,
tester.getBottomRight(find.text('accountName')).dx,
);
await tester.pumpWidget(buildFrame(
currentAccountPicture: const CircleAvatar(child: Text('A')),
));
expect(find.text('A'), findsOneWidget);
await tester.pumpWidget(buildFrame(
otherAccountsPictures: <Widget>[const CircleAvatar(child: Text('A'))],
));
expect(find.text('A'), findsOneWidget);
const Key avatarA = Key('A');
await tester.pumpWidget(buildFrame(
currentAccountPicture: const CircleAvatar(key: avatarA, child: Text('A')),
accountName: const Text('accountName'),
));
expect(
tester.getBottomRight(find.byKey(avatarA)).dx,
tester.getBottomRight(find.text('accountName')).dx,
);
expect(
tester.getBottomLeft(find.text('accountName')).dy,
greaterThan(tester.getBottomLeft(find.byKey(avatarA)).dy),
);
});
testWidgets('UserAccountsDrawerHeader provides semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await pumpTestWidget(tester);
expect(
semantics,
hasSemantics(
TestSemantics(
children: <TestSemantics>[
TestSemantics(
children: <TestSemantics>[
TestSemantics(
children: <TestSemantics>[
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
children: <TestSemantics>[
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
label: 'Signed in\nname\nemail',
textDirection: TextDirection.ltr,
children: <TestSemantics>[
TestSemantics(
label: r'B',
textDirection: TextDirection.ltr,
),
TestSemantics(
label: r'C',
textDirection: TextDirection.ltr,
),
TestSemantics(
label: r'D',
textDirection: TextDirection.ltr,
),
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton],
actions: <SemanticsAction>[SemanticsAction.tap],
label: r'Show accounts',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
],
),
],
),
ignoreId: true, ignoreTransform: true, ignoreRect: true,
),
);
semantics.dispose();
});
testWidgets('alternative account selectors have sufficient tap targets', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
await pumpTestWidget(tester);
expect(tester.getSemantics(find.text('B')), matchesSemantics(
label: 'B',
size: const Size(48.0, 48.0),
));
expect(tester.getSemantics(find.text('C')), matchesSemantics(
label: 'C',
size: const Size(48.0, 48.0),
));
expect(tester.getSemantics(find.text('D')), matchesSemantics(
label: 'D',
size: const Size(48.0, 48.0),
));
handle.dispose();
});
testWidgets('UserAccountsDrawerHeader provides semantics with missing properties', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await pumpTestWidget(
tester,
withEmail: false,
withName: false,
withOnDetailsPressedHandler: false,
);
expect(
semantics,
hasSemantics(
TestSemantics(
children: <TestSemantics>[
TestSemantics(
children: <TestSemantics>[
TestSemantics(
children: <TestSemantics>[
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
children: <TestSemantics>[
TestSemantics(
label: 'Signed in',
textDirection: TextDirection.ltr,
children: <TestSemantics>[
TestSemantics(
label: r'B',
textDirection: TextDirection.ltr,
),
TestSemantics(
label: r'C',
textDirection: TextDirection.ltr,
),
TestSemantics(
label: r'D',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
],
),
],
),
ignoreId: true, ignoreTransform: true, ignoreRect: true,
),
);
semantics.dispose();
});
}