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

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

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

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

93 94 95 96 97 98 99
    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));

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

107 108 109 110 111 112 113 114 115 116
  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();
117
    await tester.pump(const Duration(milliseconds: 10));
118 119 120 121 122 123 124 125 126 127 128 129
    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();
130
    await tester.pump(const Duration(milliseconds: 10));
131 132 133 134 135 136 137 138 139 140 141
    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);
  });

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

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

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

178 179 180 181 182 183 184 185 186 187 188
  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();
189
    await tester.pump(const Duration(milliseconds: 100));
190 191 192 193 194
    expect(tester.hasRunningAnimations, isTrue);

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

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

    // Icon starts to rotate up to its original position mid animation.
    await tester.tap(find.byType(Icon));
    await tester.pump();
207
    await tester.pump(const Duration(milliseconds: 100));
208 209 210 211 212 213 214 215 216 217
    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);
  });
218

219 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
  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);
  });

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

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

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

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

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

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

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

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

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

  testWidgets('UserAccountsDrawerHeader null parameters RTL', (WidgetTester tester) async {
    Widget buildFrame({
      Widget currentAccountPicture,
      List<Widget> otherAccountsPictures,
      Widget accountName,
      Widget accountEmail,
      VoidCallback onDetailsPressed,
      EdgeInsets margin,
    }) {
367 368
      return MaterialApp(
        home: Directionality(
369
          textDirection: TextDirection.rtl,
370 371 372
          child: Material(
            child: Center(
              child: UserAccountsDrawerHeader(
373 374 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
                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,
402
      tester.getCenter(find.byType(Icon)).dy,
403 404 405
    );
    expect(
      tester.getCenter(find.text('accountName')).dx,
406
      greaterThan(tester.getCenter(find.byType(Icon)).dx),
407 408 409 410 411 412 413 414
    );

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

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

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

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

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

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

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

    semantics.dispose();
  });

521 522 523 524
  testWidgets('alternative account selectors have sufficient tap targets', (WidgetTester tester) async {
    final SemanticsHandle handle = tester.ensureSemantics();
    await pumpTestWidget(tester);

525
    expect(tester.getSemantics(find.text('B')), matchesSemantics(
526 527 528 529
      label: 'B',
      size: const Size(48.0, 48.0),
    ));

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

535
    expect(tester.getSemantics(find.text('D')), matchesSemantics(
536 537 538 539 540 541
      label: 'D',
      size: const Size(48.0, 48.0),
    ));
    handle.dispose();
  });

542
  testWidgets('UserAccountsDrawerHeader provides semantics with missing properties', (WidgetTester tester) async {
543
    final SemanticsTester semantics = SemanticsTester(tester);
544 545 546 547 548 549 550 551 552 553
    await pumpTestWidget(
      tester,
      withEmail: false,
      withName: false,
      withOnDetailsPressedHandler: false,
    );

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

    semantics.dispose();
  });
591
}