user_accounts_drawer_header_test.dart 19.7 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// @dart = 2.8

7 8
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
9
import 'package:flutter_test/flutter_test.dart';
10

11 12
import '../widgets/semantics_tester.dart';

13 14 15
const Key avatarA = Key('A');
const Key avatarC = Key('C');
const Key avatarD = Key('D');
16

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

70 71 72
void main() {
  testWidgets('UserAccountsDrawerHeader test', (WidgetTester tester) async {
    await pumpTestWidget(tester);
73

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
    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));

91
    // Verify height = height + top padding + bottom margin + bottom edge)
92
    box = tester.renderObject(find.byType(UserAccountsDrawerHeader));
93
    expect(box.size.height, equals(160.0 + 20.0 + 8.0 + 1.0));
94

95 96 97 98 99 100 101
    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));

102 103
    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
104 105
    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
106
    expect(avatarDTopRight.dx - avatarCTopRight.dx, equals(40.0 + 16.0)); // size + space between
107
  });
108

109 110 111 112 113 114 115 116 117 118
  testWidgets('UserAccountsDrawerHeader icon rotation test', (WidgetTester tester) async {
    await pumpTestWidget(tester);
    Transform transformWidget = tester.firstWidget(find.byType(Transform));

    // 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();
119
    await tester.pump(const Duration(milliseconds: 10));
120 121 122 123 124 125 126 127 128 129 130 131
    expect(tester.hasRunningAnimations, isTrue);

    await tester.pumpAndSettle();
    await tester.pump();
    transformWidget = tester.firstWidget(find.byType(Transform));

    // 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();
132
    await tester.pump(const Duration(milliseconds: 10));
133 134 135 136 137 138 139 140 141 142 143
    expect(tester.hasRunningAnimations, isTrue);

    await tester.pumpAndSettle();
    await tester.pump();
    transformWidget = tester.firstWidget(find.byType(Transform));

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

144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
  // Regression test for https://github.com/flutter/flutter/issues/25801.
  testWidgets('UserAccountsDrawerHeader icon does not rotate after setState', (WidgetTester tester) async {
    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(find.byType(Transform));

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

172
    expect(await tester.pumpAndSettle(), 1);
173 174 175 176 177 178 179
    transformWidget = tester.firstWidget(find.byType(Transform));

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

180 181 182 183 184 185 186 187 188 189 190
  testWidgets('UserAccountsDrawerHeader icon rotation test speeeeeedy', (WidgetTester tester) async {
    await pumpTestWidget(tester);
    Transform transformWidget = tester.firstWidget(find.byType(Transform));

    // 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();
191
    await tester.pump(const Duration(milliseconds: 100));
192 193 194 195 196
    expect(tester.hasRunningAnimations, isTrue);

    // Icon starts to rotate up mid animation.
    await tester.tap(find.byType(Icon));
    await tester.pump();
197
    await tester.pump(const Duration(milliseconds: 100));
198 199 200 201 202
    expect(tester.hasRunningAnimations, isTrue);

    // Icon starts to rotate down again still mid animation.
    await tester.tap(find.byType(Icon));
    await tester.pump();
203
    await tester.pump(const Duration(milliseconds: 100));
204 205 206 207 208
    expect(tester.hasRunningAnimations, isTrue);

    // Icon starts to rotate up to its original position mid animation.
    await tester.tap(find.byType(Icon));
    await tester.pump();
209
    await tester.pump(const Duration(milliseconds: 100));
210 211 212 213 214 215 216 217 218 219
    expect(tester.hasRunningAnimations, isTrue);

    await tester.pumpAndSettle();
    await tester.pump();
    transformWidget = tester.firstWidget(find.byType(Transform));

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

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
  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);
  });

252
  testWidgets('UserAccountsDrawerHeader null parameters LTR', (WidgetTester tester) async {
253 254 255 256 257 258
    Widget buildFrame({
      Widget currentAccountPicture,
      List<Widget> otherAccountsPictures,
      Widget accountName,
      Widget accountEmail,
      VoidCallback onDetailsPressed,
259
      EdgeInsets margin,
260
    }) {
261 262 263 264
      return MaterialApp(
        home: Material(
          child: Center(
            child: UserAccountsDrawerHeader(
265 266 267 268 269 270 271
              currentAccountPicture: currentAccountPicture,
              otherAccountsPictures: otherAccountsPictures,
              accountName: accountName,
              accountEmail: accountEmail,
              onDetailsPressed: onDetailsPressed,
              margin: margin,
            ),
272
          ),
273 274 275 276 277
        ),
      );
    }

    await tester.pumpWidget(buildFrame());
278 279
    final RenderBox box = tester.renderObject(find.byType(UserAccountsDrawerHeader));
    expect(box.size.height, equals(160.0 + 1.0)); // height + bottom edge)
280 281 282 283 284 285 286 287
    expect(find.byType(Icon), findsNothing);

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

    await tester.pumpWidget(buildFrame(
288
      accountName: const Text('accountName'),
289 290 291
      onDetailsPressed: () { },
    ));
    expect(
292
      tester.getCenter(find.text('accountName')).dy,
293
      tester.getCenter(find.byType(Icon)).dy,
294
    );
295 296
    expect(
      tester.getCenter(find.text('accountName')).dx,
297
      lessThan(tester.getCenter(find.byType(Icon)).dx),
298
    );
299 300

    await tester.pumpWidget(buildFrame(
301
      accountEmail: const Text('accountEmail'),
302 303 304
      onDetailsPressed: () { },
    ));
    expect(
305
      tester.getCenter(find.text('accountEmail')).dy,
306
      tester.getCenter(find.byType(Icon)).dy,
307
    );
308 309
    expect(
      tester.getCenter(find.text('accountEmail')).dx,
310
      lessThan(tester.getCenter(find.byType(Icon)).dx),
311
    );
312 313

    await tester.pumpWidget(buildFrame(
314 315
      accountName: const Text('accountName'),
      accountEmail: const Text('accountEmail'),
316 317 318
      onDetailsPressed: () { },
    ));
    expect(
319
      tester.getCenter(find.text('accountEmail')).dy,
320
      tester.getCenter(find.byType(Icon)).dy,
321
    );
322 323
    expect(
      tester.getCenter(find.text('accountEmail')).dx,
324
      lessThan(tester.getCenter(find.byType(Icon)).dx),
325
    );
326
    expect(
327
      tester.getBottomLeft(find.text('accountEmail')).dy,
328
      greaterThan(tester.getBottomLeft(find.text('accountName')).dy),
329 330
    );
    expect(
331
      tester.getBottomLeft(find.text('accountEmail')).dx,
332
      tester.getBottomLeft(find.text('accountName')).dx,
333 334 335
    );

    await tester.pumpWidget(buildFrame(
336
      currentAccountPicture: const CircleAvatar(child: Text('A')),
337 338 339 340
    ));
    expect(find.text('A'), findsOneWidget);

    await tester.pumpWidget(buildFrame(
341
      otherAccountsPictures: <Widget>[const CircleAvatar(child: Text('A'))],
342 343 344
    ));
    expect(find.text('A'), findsOneWidget);

345
    const Key avatarA = Key('A');
346
    await tester.pumpWidget(buildFrame(
347
      currentAccountPicture: const CircleAvatar(key: avatarA, child: Text('A')),
348
      accountName: const Text('accountName'),
349 350
    ));
    expect(
351
      tester.getBottomLeft(find.byKey(avatarA)).dx,
352
      tester.getBottomLeft(find.text('accountName')).dx,
353 354
    );
    expect(
355
      tester.getBottomLeft(find.text('accountName')).dy,
356
      greaterThan(tester.getBottomLeft(find.byKey(avatarA)).dy),
357 358
    );
  });
359 360 361 362 363 364 365 366 367 368

  testWidgets('UserAccountsDrawerHeader null parameters RTL', (WidgetTester tester) async {
    Widget buildFrame({
      Widget currentAccountPicture,
      List<Widget> otherAccountsPictures,
      Widget accountName,
      Widget accountEmail,
      VoidCallback onDetailsPressed,
      EdgeInsets margin,
    }) {
369 370
      return MaterialApp(
        home: Directionality(
371
          textDirection: TextDirection.rtl,
372 373 374
          child: Material(
            child: Center(
              child: UserAccountsDrawerHeader(
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
                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,
404
      tester.getCenter(find.byType(Icon)).dy,
405 406 407
    );
    expect(
      tester.getCenter(find.text('accountName')).dx,
408
      greaterThan(tester.getCenter(find.byType(Icon)).dx),
409 410 411 412 413 414 415 416
    );

    await tester.pumpWidget(buildFrame(
      accountEmail: const Text('accountEmail'),
      onDetailsPressed: () { },
    ));
    expect(
      tester.getCenter(find.text('accountEmail')).dy,
417
      tester.getCenter(find.byType(Icon)).dy,
418 419 420
    );
    expect(
      tester.getCenter(find.text('accountEmail')).dx,
421
      greaterThan(tester.getCenter(find.byType(Icon)).dx),
422 423 424 425 426 427 428 429 430
    );

    await tester.pumpWidget(buildFrame(
      accountName: const Text('accountName'),
      accountEmail: const Text('accountEmail'),
      onDetailsPressed: () { },
    ));
    expect(
      tester.getCenter(find.text('accountEmail')).dy,
431
      tester.getCenter(find.byType(Icon)).dy,
432 433 434
    );
    expect(
      tester.getCenter(find.text('accountEmail')).dx,
435
      greaterThan(tester.getCenter(find.byType(Icon)).dx),
436 437 438
    );
    expect(
      tester.getBottomLeft(find.text('accountEmail')).dy,
439
      greaterThan(tester.getBottomLeft(find.text('accountName')).dy),
440 441 442
    );
    expect(
      tester.getBottomRight(find.text('accountEmail')).dx,
443
      tester.getBottomRight(find.text('accountName')).dx,
444 445 446
    );

    await tester.pumpWidget(buildFrame(
447
      currentAccountPicture: const CircleAvatar(child: Text('A')),
448 449 450 451
    ));
    expect(find.text('A'), findsOneWidget);

    await tester.pumpWidget(buildFrame(
452
      otherAccountsPictures: <Widget>[const CircleAvatar(child: Text('A'))],
453 454 455
    ));
    expect(find.text('A'), findsOneWidget);

456
    const Key avatarA = Key('A');
457
    await tester.pumpWidget(buildFrame(
458
      currentAccountPicture: const CircleAvatar(key: avatarA, child: Text('A')),
459 460 461 462
      accountName: const Text('accountName'),
    ));
    expect(
      tester.getBottomRight(find.byKey(avatarA)).dx,
463
      tester.getBottomRight(find.text('accountName')).dx,
464 465 466
    );
    expect(
      tester.getBottomLeft(find.text('accountName')).dy,
467
      greaterThan(tester.getBottomLeft(find.byKey(avatarA)).dy),
468 469 470 471
    );
  });

  testWidgets('UserAccountsDrawerHeader provides semantics', (WidgetTester tester) async {
472
    final SemanticsTester semantics = SemanticsTester(tester);
473 474 475 476 477
    await pumpTestWidget(tester);

    expect(
      semantics,
      hasSemantics(
478
        TestSemantics(
479
          children: <TestSemantics>[
480
            TestSemantics(
481
              children: <TestSemantics>[
482
                TestSemantics(
483 484
                  children: <TestSemantics>[
                    TestSemantics(
485
                  flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
486
                  children: <TestSemantics>[
487
                    TestSemantics(
488
                      flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
489
                      label: 'Signed in\nname\nemail',
490
                      textDirection: TextDirection.ltr,
491
                      children: <TestSemantics>[
492
                        TestSemantics(
493 494 495
                          label: r'B',
                          textDirection: TextDirection.ltr,
                        ),
496
                        TestSemantics(
497 498 499
                          label: r'C',
                          textDirection: TextDirection.ltr,
                        ),
500
                        TestSemantics(
501 502 503
                          label: r'D',
                          textDirection: TextDirection.ltr,
                        ),
504
                        TestSemantics(
505 506 507 508 509 510
                          flags: <SemanticsFlag>[SemanticsFlag.isButton],
                          actions: <SemanticsAction>[SemanticsAction.tap],
                          label: r'Show accounts',
                          textDirection: TextDirection.ltr,
                        ),
                      ],
511 512
                    ),
                  ],
513
                ),
514 515
                  ],
                ),
516 517 518 519 520 521 522 523 524 525 526
              ],
            ),
          ],
        ),
        ignoreId: true, ignoreTransform: true, ignoreRect: true,
      ),
    );

    semantics.dispose();
  });

527 528 529 530
  testWidgets('alternative account selectors have sufficient tap targets', (WidgetTester tester) async {
    final SemanticsHandle handle = tester.ensureSemantics();
    await pumpTestWidget(tester);

531
    expect(tester.getSemantics(find.text('B')), matchesSemantics(
532 533 534 535
      label: 'B',
      size: const Size(48.0, 48.0),
    ));

536
    expect(tester.getSemantics(find.text('C')), matchesSemantics(
537 538 539 540
      label: 'C',
      size: const Size(48.0, 48.0),
    ));

541
    expect(tester.getSemantics(find.text('D')), matchesSemantics(
542 543 544 545 546 547
      label: 'D',
      size: const Size(48.0, 48.0),
    ));
    handle.dispose();
  });

548
  testWidgets('UserAccountsDrawerHeader provides semantics with missing properties', (WidgetTester tester) async {
549
    final SemanticsTester semantics = SemanticsTester(tester);
550 551 552 553 554 555 556 557 558 559
    await pumpTestWidget(
      tester,
      withEmail: false,
      withName: false,
      withOnDetailsPressedHandler: false,
    );

    expect(
      semantics,
      hasSemantics(
560
        TestSemantics(
561
          children: <TestSemantics>[
562
            TestSemantics(
563
              children: <TestSemantics>[
564
                TestSemantics(
565
                  children: <TestSemantics>[
566
                    TestSemantics(
567
                      flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
568
                      children: <TestSemantics>[
569
                        TestSemantics(
570
                          label: 'Signed in',
571
                          textDirection: TextDirection.ltr,
572 573 574 575 576 577 578 579 580 581 582 583 584 585
                          children: <TestSemantics>[
                            TestSemantics(
                              label: r'B',
                              textDirection: TextDirection.ltr,
                            ),
                            TestSemantics(
                              label: r'C',
                              textDirection: TextDirection.ltr,
                            ),
                            TestSemantics(
                              label: r'D',
                              textDirection: TextDirection.ltr,
                            ),
                          ],
586 587
                        ),
                      ],
588 589
                    ),
                  ],
590 591 592 593 594 595 596 597 598 599 600
                ),
              ],
            ),
          ],
        ),
        ignoreId: true, ignoreTransform: true, ignoreRect: true,
      ),
    );

    semantics.dispose();
  });
601
}