user_accounts_drawer_header_test.dart 21.7 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6
// 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';
7
import 'package:flutter_test/flutter_test.dart';
8

9 10
import '../widgets/semantics_tester.dart';

11 12 13
const Key avatarA = Key('A');
const Key avatarC = Key('C');
const Key avatarD = Key('D');
14

15 16
Future<void> pumpTestWidget(
  WidgetTester tester, {
17 18 19 20 21
      bool withName = true,
      bool withEmail = true,
      bool withOnDetailsPressedHandler = true,
      Size otherAccountsPictureSize = const Size.square(40.0),
      Size currentAccountPictureSize  = const Size.square(72.0),
22 23
      Color? primaryColor,
      Color? colorSchemePrimary,
24
    }) async {
25
  await tester.pumpWidget(
26
    MaterialApp(
27 28 29 30
      theme: ThemeData(
        primaryColor: primaryColor,
        colorScheme: const ColorScheme.light().copyWith(primary: colorSchemePrimary),
      ),
31
      home: MediaQuery(
32
        data: const MediaQueryData(
33
          padding: EdgeInsets.only(
34 35 36 37
            left: 10.0,
            top: 20.0,
            right: 30.0,
            bottom: 40.0,
38
          ),
39
        ),
40 41 42
        child: Material(
          child: Center(
            child: UserAccountsDrawerHeader(
43
              onDetailsPressed: withOnDetailsPressedHandler ? () { } : null,
44 45
              currentAccountPictureSize: currentAccountPictureSize,
              otherAccountsPicturesSize: otherAccountsPictureSize,
46
              currentAccountPicture: const ExcludeSemantics(
47
                child: CircleAvatar(
48
                  key: avatarA,
49
                  child: Text('A'),
50
                ),
51
              ),
52
              otherAccountsPictures: const <Widget>[
53 54
                CircleAvatar(
                  child: Text('B'),
55
                ),
56
                CircleAvatar(
57
                  key: avatarC,
58
                  child: Text('C'),
59
                ),
60
                CircleAvatar(
61
                  key: avatarD,
62
                  child: Text('D'),
63
                ),
64 65
                CircleAvatar(
                  child: Text('E'),
66
                ),
67 68 69
              ],
              accountName: withName ? const Text('name') : null,
              accountEmail: withEmail ? const Text('email') : null,
70
            ),
71 72 73
          ),
        ),
      ),
74 75 76
    ),
  );
}
77

78
void main() {
79 80 81 82 83 84
  // Find the exact transform which is the descendant of [UserAccountsDrawerHeader].
  final Finder findTransform = find.descendant(
    of: find.byType(UserAccountsDrawerHeader),
    matching: find.byType(Transform),
  );

85 86 87 88 89 90 91 92 93 94 95 96 97
  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);
  });

98 99
  testWidgets('UserAccountsDrawerHeader test', (WidgetTester tester) async {
    await pumpTestWidget(tester);
100

101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
    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));

118
    // Verify height = height + top padding + bottom margin + bottom edge)
119
    box = tester.renderObject(find.byType(UserAccountsDrawerHeader));
120
    expect(box.size.height, equals(160.0 + 20.0 + 8.0 + 1.0));
121

122 123 124 125 126 127 128
    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));

129 130
    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
131 132
    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
133
    expect(avatarDTopRight.dx - avatarCTopRight.dx, equals(40.0 + 16.0)); // size + space between
134
  });
135

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
  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);
  });

153 154
  testWidgets('UserAccountsDrawerHeader icon rotation test', (WidgetTester tester) async {
    await pumpTestWidget(tester);
155
    Transform transformWidget = tester.firstWidget(findTransform);
156 157 158 159 160 161 162

    // 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();
163
    await tester.pump(const Duration(milliseconds: 10));
164 165 166 167
    expect(tester.hasRunningAnimations, isTrue);

    await tester.pumpAndSettle();
    await tester.pump();
168
    transformWidget = tester.firstWidget(findTransform);
169 170 171 172 173 174 175

    // 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();
176
    await tester.pump(const Duration(milliseconds: 10));
177 178 179 180
    expect(tester.hasRunningAnimations, isTrue);

    await tester.pumpAndSettle();
    await tester.pump();
181
    transformWidget = tester.firstWidget(findTransform);
182 183 184 185 186 187

    // Icon has rotated 180 degrees back to the original position.
    expect(transformWidget.transform.getRotation()[0], 1.0);
    expect(transformWidget.transform.getRotation()[4], 1.0);
  });

188 189
  // Regression test for https://github.com/flutter/flutter/issues/25801.
  testWidgets('UserAccountsDrawerHeader icon does not rotate after setState', (WidgetTester tester) async {
190
    late StateSetter testSetState;
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
    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'),
            );
          },
        ),
      ),
    ));

206
    Transform transformWidget = tester.firstWidget(findTransform);
207 208 209 210 211 212 213 214 215

    // 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);

216
    expect(await tester.pumpAndSettle(), 1);
217
    transformWidget = tester.firstWidget(findTransform);
218 219 220 221 222 223

    // Icon has not rotated.
    expect(transformWidget.transform.getRotation()[0], 1.0);
    expect(transformWidget.transform.getRotation()[4], 1.0);
  });

224 225
  testWidgets('UserAccountsDrawerHeader icon rotation test speeeeeedy', (WidgetTester tester) async {
    await pumpTestWidget(tester);
226
    Transform transformWidget = tester.firstWidget(findTransform);
227 228 229 230 231 232 233 234

    // 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();
235
    await tester.pump(const Duration(milliseconds: 100));
236 237 238 239 240
    expect(tester.hasRunningAnimations, isTrue);

    // Icon starts to rotate up mid animation.
    await tester.tap(find.byType(Icon));
    await tester.pump();
241
    await tester.pump(const Duration(milliseconds: 100));
242 243 244 245 246
    expect(tester.hasRunningAnimations, isTrue);

    // Icon starts to rotate down again still mid animation.
    await tester.tap(find.byType(Icon));
    await tester.pump();
247
    await tester.pump(const Duration(milliseconds: 100));
248 249 250 251 252
    expect(tester.hasRunningAnimations, isTrue);

    // Icon starts to rotate up to its original position mid animation.
    await tester.tap(find.byType(Icon));
    await tester.pump();
253
    await tester.pump(const Duration(milliseconds: 100));
254 255 256 257
    expect(tester.hasRunningAnimations, isTrue);

    await tester.pumpAndSettle();
    await tester.pump();
258
    transformWidget = tester.firstWidget(findTransform);
259 260 261 262 263

    // Icon has rotated 180 degrees back to the original position.
    expect(transformWidget.transform.getRotation()[0], 1.0);
    expect(transformWidget.transform.getRotation()[4], 1.0);
  });
264

265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
  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);
  });

296
  testWidgets('UserAccountsDrawerHeader null parameters LTR', (WidgetTester tester) async {
297
    Widget buildFrame({
298 299 300 301 302 303
      Widget? currentAccountPicture,
      List<Widget>? otherAccountsPictures,
      Widget? accountName,
      Widget? accountEmail,
      VoidCallback? onDetailsPressed,
      EdgeInsets? margin,
304
    }) {
305 306 307 308
      return MaterialApp(
        home: Material(
          child: Center(
            child: UserAccountsDrawerHeader(
309 310 311 312 313 314 315
              currentAccountPicture: currentAccountPicture,
              otherAccountsPictures: otherAccountsPictures,
              accountName: accountName,
              accountEmail: accountEmail,
              onDetailsPressed: onDetailsPressed,
              margin: margin,
            ),
316
          ),
317 318 319 320 321
        ),
      );
    }

    await tester.pumpWidget(buildFrame());
322 323
    final RenderBox box = tester.renderObject(find.byType(UserAccountsDrawerHeader));
    expect(box.size.height, equals(160.0 + 1.0)); // height + bottom edge)
324 325 326 327 328 329 330 331
    expect(find.byType(Icon), findsNothing);

    await tester.pumpWidget(buildFrame(
      onDetailsPressed: () { },
    ));
    expect(find.byType(Icon), findsOneWidget);

    await tester.pumpWidget(buildFrame(
332
      accountName: const Text('accountName'),
333 334 335
      onDetailsPressed: () { },
    ));
    expect(
336
      tester.getCenter(find.text('accountName')).dy,
337
      tester.getCenter(find.byType(Icon)).dy,
338
    );
339 340
    expect(
      tester.getCenter(find.text('accountName')).dx,
341
      lessThan(tester.getCenter(find.byType(Icon)).dx),
342
    );
343 344

    await tester.pumpWidget(buildFrame(
345
      accountEmail: const Text('accountEmail'),
346 347 348
      onDetailsPressed: () { },
    ));
    expect(
349
      tester.getCenter(find.text('accountEmail')).dy,
350
      tester.getCenter(find.byType(Icon)).dy,
351
    );
352 353
    expect(
      tester.getCenter(find.text('accountEmail')).dx,
354
      lessThan(tester.getCenter(find.byType(Icon)).dx),
355
    );
356 357

    await tester.pumpWidget(buildFrame(
358 359
      accountName: const Text('accountName'),
      accountEmail: const Text('accountEmail'),
360 361 362
      onDetailsPressed: () { },
    ));
    expect(
363
      tester.getCenter(find.text('accountEmail')).dy,
364
      tester.getCenter(find.byType(Icon)).dy,
365
    );
366 367
    expect(
      tester.getCenter(find.text('accountEmail')).dx,
368
      lessThan(tester.getCenter(find.byType(Icon)).dx),
369
    );
370
    expect(
371
      tester.getBottomLeft(find.text('accountEmail')).dy,
372
      greaterThan(tester.getBottomLeft(find.text('accountName')).dy),
373 374
    );
    expect(
375
      tester.getBottomLeft(find.text('accountEmail')).dx,
376
      tester.getBottomLeft(find.text('accountName')).dx,
377 378 379
    );

    await tester.pumpWidget(buildFrame(
380
      currentAccountPicture: const CircleAvatar(child: Text('A')),
381 382 383 384
    ));
    expect(find.text('A'), findsOneWidget);

    await tester.pumpWidget(buildFrame(
385
      otherAccountsPictures: <Widget>[const CircleAvatar(child: Text('A'))],
386 387 388
    ));
    expect(find.text('A'), findsOneWidget);

389
    const Key avatarA = Key('A');
390
    await tester.pumpWidget(buildFrame(
391
      currentAccountPicture: const CircleAvatar(key: avatarA, child: Text('A')),
392
      accountName: const Text('accountName'),
393 394
    ));
    expect(
395
      tester.getBottomLeft(find.byKey(avatarA)).dx,
396
      tester.getBottomLeft(find.text('accountName')).dx,
397 398
    );
    expect(
399
      tester.getBottomLeft(find.text('accountName')).dy,
400
      greaterThan(tester.getBottomLeft(find.byKey(avatarA)).dy),
401 402
    );
  });
403 404 405

  testWidgets('UserAccountsDrawerHeader null parameters RTL', (WidgetTester tester) async {
    Widget buildFrame({
406 407 408 409 410 411
      Widget? currentAccountPicture,
      List<Widget>? otherAccountsPictures,
      Widget? accountName,
      Widget? accountEmail,
      VoidCallback? onDetailsPressed,
      EdgeInsets? margin,
412
    }) {
413 414
      return MaterialApp(
        home: Directionality(
415
          textDirection: TextDirection.rtl,
416 417 418
          child: Material(
            child: Center(
              child: UserAccountsDrawerHeader(
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
                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,
448
      tester.getCenter(find.byType(Icon)).dy,
449 450 451
    );
    expect(
      tester.getCenter(find.text('accountName')).dx,
452
      greaterThan(tester.getCenter(find.byType(Icon)).dx),
453 454 455 456 457 458 459 460
    );

    await tester.pumpWidget(buildFrame(
      accountEmail: const Text('accountEmail'),
      onDetailsPressed: () { },
    ));
    expect(
      tester.getCenter(find.text('accountEmail')).dy,
461
      tester.getCenter(find.byType(Icon)).dy,
462 463 464
    );
    expect(
      tester.getCenter(find.text('accountEmail')).dx,
465
      greaterThan(tester.getCenter(find.byType(Icon)).dx),
466 467 468 469 470 471 472 473 474
    );

    await tester.pumpWidget(buildFrame(
      accountName: const Text('accountName'),
      accountEmail: const Text('accountEmail'),
      onDetailsPressed: () { },
    ));
    expect(
      tester.getCenter(find.text('accountEmail')).dy,
475
      tester.getCenter(find.byType(Icon)).dy,
476 477 478
    );
    expect(
      tester.getCenter(find.text('accountEmail')).dx,
479
      greaterThan(tester.getCenter(find.byType(Icon)).dx),
480 481 482
    );
    expect(
      tester.getBottomLeft(find.text('accountEmail')).dy,
483
      greaterThan(tester.getBottomLeft(find.text('accountName')).dy),
484 485 486
    );
    expect(
      tester.getBottomRight(find.text('accountEmail')).dx,
487
      tester.getBottomRight(find.text('accountName')).dx,
488 489 490
    );

    await tester.pumpWidget(buildFrame(
491
      currentAccountPicture: const CircleAvatar(child: Text('A')),
492 493 494 495
    ));
    expect(find.text('A'), findsOneWidget);

    await tester.pumpWidget(buildFrame(
496
      otherAccountsPictures: <Widget>[const CircleAvatar(child: Text('A'))],
497 498 499
    ));
    expect(find.text('A'), findsOneWidget);

500
    const Key avatarA = Key('A');
501
    await tester.pumpWidget(buildFrame(
502
      currentAccountPicture: const CircleAvatar(key: avatarA, child: Text('A')),
503 504 505 506
      accountName: const Text('accountName'),
    ));
    expect(
      tester.getBottomRight(find.byKey(avatarA)).dx,
507
      tester.getBottomRight(find.text('accountName')).dx,
508 509 510
    );
    expect(
      tester.getBottomLeft(find.text('accountName')).dy,
511
      greaterThan(tester.getBottomLeft(find.byKey(avatarA)).dy),
512 513 514 515
    );
  });

  testWidgets('UserAccountsDrawerHeader provides semantics', (WidgetTester tester) async {
516
    final SemanticsTester semantics = SemanticsTester(tester);
517 518 519 520 521
    await pumpTestWidget(tester);

    expect(
      semantics,
      hasSemantics(
522
        TestSemantics(
523
          children: <TestSemantics>[
524
            TestSemantics(
525
              children: <TestSemantics>[
526
                TestSemantics(
527 528
                  children: <TestSemantics>[
                    TestSemantics(
529
                      flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
530
                      children: <TestSemantics>[
531
                        TestSemantics(
532 533
                          flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
                          label: 'Signed in\nname\nemail',
534
                          textDirection: TextDirection.ltr,
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
                          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,
                            ),
                          ],
555 556
                        ),
                      ],
557 558
                    ),
                  ],
559 560 561 562 563 564 565 566 567 568 569 570
                ),
              ],
            ),
          ],
        ),
        ignoreId: true, ignoreTransform: true, ignoreRect: true,
      ),
    );

    semantics.dispose();
  });

571 572 573 574
  testWidgets('alternative account selectors have sufficient tap targets', (WidgetTester tester) async {
    final SemanticsHandle handle = tester.ensureSemantics();
    await pumpTestWidget(tester);

575
    expect(tester.getSemantics(find.text('B')), matchesSemantics(
576 577 578 579
      label: 'B',
      size: const Size(48.0, 48.0),
    ));

580
    expect(tester.getSemantics(find.text('C')), matchesSemantics(
581 582 583 584
      label: 'C',
      size: const Size(48.0, 48.0),
    ));

585
    expect(tester.getSemantics(find.text('D')), matchesSemantics(
586 587 588 589 590 591
      label: 'D',
      size: const Size(48.0, 48.0),
    ));
    handle.dispose();
  });

592
  testWidgets('UserAccountsDrawerHeader provides semantics with missing properties', (WidgetTester tester) async {
593
    final SemanticsTester semantics = SemanticsTester(tester);
594 595 596 597 598 599 600 601 602 603
    await pumpTestWidget(
      tester,
      withEmail: false,
      withName: false,
      withOnDetailsPressedHandler: false,
    );

    expect(
      semantics,
      hasSemantics(
604
        TestSemantics(
605
          children: <TestSemantics>[
606
            TestSemantics(
607
              children: <TestSemantics>[
608
                TestSemantics(
609
                  children: <TestSemantics>[
610
                    TestSemantics(
611
                      flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
612
                      children: <TestSemantics>[
613
                        TestSemantics(
614
                          label: 'Signed in',
615
                          textDirection: TextDirection.ltr,
616 617 618 619 620 621 622 623 624 625 626 627 628 629
                          children: <TestSemantics>[
                            TestSemantics(
                              label: r'B',
                              textDirection: TextDirection.ltr,
                            ),
                            TestSemantics(
                              label: r'C',
                              textDirection: TextDirection.ltr,
                            ),
                            TestSemantics(
                              label: r'D',
                              textDirection: TextDirection.ltr,
                            ),
                          ],
630 631
                        ),
                      ],
632 633
                    ),
                  ],
634 635 636 637 638 639 640 641 642 643 644
                ),
              ],
            ),
          ],
        ),
        ignoreId: true, ignoreTransform: true, ignoreRect: true,
      ),
    );

    semantics.dispose();
  });
645
}