tooltip_test.dart 43.2 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
Hixie's avatar
Hixie committed
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 'dart:ui';

9
import 'package:flutter/services.dart';
10 11
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/gestures.dart';
Hixie's avatar
Hixie committed
12 13 14 15
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';

16
import '../rendering/mock_canvas.dart';
17
import '../widgets/semantics_tester.dart';
18
import 'feedback_tester.dart';
Hixie's avatar
Hixie committed
19

Ian Hickson's avatar
Ian Hickson committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
// This file uses "as dynamic" in a few places to defeat the static
// analysis. In general you want to avoid using this style in your
// code, as it will cause the analyzer to be unable to help you catch
// errors.
//
// In this case, we do it because we are trying to call internal
// methods of the tooltip code in order to test it. Normally, the
// state of a tooltip is a private class, but by using a GlobalKey we
// can get a handle to that object and by using "as dynamic" we can
// bypass the analyzer's type checks and call methods that we aren't
// supposed to be able to know about.
//
// It's ok to do this in tests, but you really don't want to do it in
// production code.

35 36
const String tooltipText = 'TIP';

37 38 39 40 41 42 43
Finder _findTooltipContainer(String tooltipText) {
  return find.ancestor(
    of: find.text(tooltipText),
    matching: find.byType(Container),
  );
}

Hixie's avatar
Hixie committed
44
void main() {
45
  testWidgets('Does tooltip end up in the right place - center', (WidgetTester tester) async {
46
    final GlobalKey key = GlobalKey();
47
    await tester.pumpWidget(
48
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
49
        textDirection: TextDirection.ltr,
50
        child: Overlay(
Ian Hickson's avatar
Ian Hickson committed
51
          initialEntries: <OverlayEntry>[
52
            OverlayEntry(
Ian Hickson's avatar
Ian Hickson committed
53
              builder: (BuildContext context) {
54
                return Stack(
Ian Hickson's avatar
Ian Hickson committed
55
                  children: <Widget>[
56
                    Positioned(
Ian Hickson's avatar
Ian Hickson committed
57 58
                      left: 300.0,
                      top: 0.0,
59
                      child: Tooltip(
Ian Hickson's avatar
Ian Hickson committed
60 61 62 63 64 65
                        key: key,
                        message: tooltipText,
                        height: 20.0,
                        padding: const EdgeInsets.all(5.0),
                        verticalOffset: 20.0,
                        preferBelow: false,
66
                        child: const SizedBox(
Ian Hickson's avatar
Ian Hickson committed
67 68 69 70 71 72 73 74 75 76 77 78
                          width: 0.0,
                          height: 0.0,
                        ),
                      ),
                    ),
                  ],
                );
              },
            ),
          ],
        ),
      ),
79
    );
80
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
81
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
82

83 84 85 86 87 88 89 90
    /********************* 800x600 screen
     *      o            * y=0
     *      |            * }- 20.0 vertical offset, of which 10.0 is in the screen edge margin
     *   +----+          * \- (5.0 padding in height)
     *   |    |          * |- 20 height
     *   +----+          * /- (5.0 padding in height)
     *                   *
     *********************/
91

92 93 94
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
95
    final Offset tipInGlobal = tip.localToGlobal(tip.size.topCenter(Offset.zero));
96 97
    // The exact position of the left side depends on the font the test framework
    // happens to pick, so we don't test that.
98 99
    expect(tipInGlobal.dx, 300.0);
    expect(tipInGlobal.dy, 20.0);
100 101
  });

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
  testWidgets('Does tooltip end up in the right place - center with padding outside overlay', (WidgetTester tester) async {
    final GlobalKey key = GlobalKey();
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: Padding(
          padding: const EdgeInsets.all(20),
          child: Overlay(
            initialEntries: <OverlayEntry>[
              OverlayEntry(
                builder: (BuildContext context) {
                  return Stack(
                    children: <Widget>[
                      Positioned(
                        left: 300.0,
                        top: 0.0,
                        child: Tooltip(
                          key: key,
                          message: tooltipText,
                          height: 20.0,
                          padding: const EdgeInsets.all(5.0),
                          verticalOffset: 20.0,
                          preferBelow: false,
                          child: const SizedBox(
                            width: 0.0,
                            height: 0.0,
                          ),
                        ),
                      ),
                    ],
                  );
                },
              ),
            ],
          ),
        ),
      ),
    );
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

    /************************ 800x600 screen
     *   ________________   * }- 20.0 padding outside overlay
     *  |    o           |  * y=0
     *  |    |           |  * }- 20.0 vertical offset, of which 10.0 is in the screen edge margin
     *  | +----+         |  * \- (5.0 padding in height)
     *  | |    |         |  * |- 20 height
     *  | +----+         |  * /- (5.0 padding in height)
     *  |________________|  *
     *                      * } - 20.0 padding outside overlay
     ************************/

    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
    final Offset tipInGlobal = tip.localToGlobal(tip.size.topCenter(Offset.zero));
    // The exact position of the left side depends on the font the test framework
    // happens to pick, so we don't test that.
    expect(tipInGlobal.dx, 320.0);
    expect(tipInGlobal.dy, 40.0);
  });

164
  testWidgets('Does tooltip end up in the right place - top left', (WidgetTester tester) async {
165
    final GlobalKey key = GlobalKey();
166
    await tester.pumpWidget(
167
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
168
        textDirection: TextDirection.ltr,
169
        child: Overlay(
Ian Hickson's avatar
Ian Hickson committed
170
          initialEntries: <OverlayEntry>[
171
            OverlayEntry(
Ian Hickson's avatar
Ian Hickson committed
172
              builder: (BuildContext context) {
173
                return Stack(
Ian Hickson's avatar
Ian Hickson committed
174
                  children: <Widget>[
175
                    Positioned(
Ian Hickson's avatar
Ian Hickson committed
176 177
                      left: 0.0,
                      top: 0.0,
178
                      child: Tooltip(
Ian Hickson's avatar
Ian Hickson committed
179 180 181 182 183 184
                        key: key,
                        message: tooltipText,
                        height: 20.0,
                        padding: const EdgeInsets.all(5.0),
                        verticalOffset: 20.0,
                        preferBelow: false,
185
                        child: const SizedBox(
Ian Hickson's avatar
Ian Hickson committed
186 187 188 189 190 191 192 193 194 195 196 197
                          width: 0.0,
                          height: 0.0,
                        ),
                      ),
                    ),
                  ],
                );
              },
            ),
          ],
        ),
      ),
198
    );
199
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
200
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
Hixie's avatar
Hixie committed
201

202 203 204 205 206 207 208 209
    /********************* 800x600 screen
     *o                  * y=0
     *|                  * }- 20.0 vertical offset, of which 10.0 is in the screen edge margin
     *+----+             * \- (5.0 padding in height)
     *|    |             * |- 20 height
     *+----+             * /- (5.0 padding in height)
     *                   *
     *********************/
Hixie's avatar
Hixie committed
210

211 212 213
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
214
    expect(tip.size.height, equals(24.0)); // 14.0 height + 5.0 padding * 2 (top, bottom)
215
    expect(tip.localToGlobal(tip.size.topLeft(Offset.zero)), equals(const Offset(10.0, 20.0)));
216
  });
Hixie's avatar
Hixie committed
217

218
  testWidgets('Does tooltip end up in the right place - center prefer above fits', (WidgetTester tester) async {
219
    final GlobalKey key = GlobalKey();
220
    await tester.pumpWidget(
221
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
222
        textDirection: TextDirection.ltr,
223
        child: Overlay(
Ian Hickson's avatar
Ian Hickson committed
224
          initialEntries: <OverlayEntry>[
225
            OverlayEntry(
Ian Hickson's avatar
Ian Hickson committed
226
              builder: (BuildContext context) {
227
                return Stack(
Ian Hickson's avatar
Ian Hickson committed
228
                  children: <Widget>[
229
                    Positioned(
Ian Hickson's avatar
Ian Hickson committed
230 231
                      left: 400.0,
                      top: 300.0,
232
                      child: Tooltip(
Ian Hickson's avatar
Ian Hickson committed
233 234 235 236 237 238
                        key: key,
                        message: tooltipText,
                        height: 100.0,
                        padding: const EdgeInsets.all(0.0),
                        verticalOffset: 100.0,
                        preferBelow: false,
239
                        child: const SizedBox(
Ian Hickson's avatar
Ian Hickson committed
240 241 242 243 244 245 246 247 248 249 250 251
                          width: 0.0,
                          height: 0.0,
                        ),
                      ),
                    ),
                  ],
                );
              },
            ),
          ],
        ),
      ),
252
    );
253
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
254
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
Hixie's avatar
Hixie committed
255

256
    /********************* 800x600 screen
257
     *        ___        * }- 10.0 margin
258 259 260 261 262 263 264
     *       |___|       * }-100.0 height
     *         |         * }-100.0 vertical offset
     *         o         * y=300.0
     *                   *
     *                   *
     *                   *
     *********************/
Hixie's avatar
Hixie committed
265

266 267 268
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
269
    expect(tip.size.height, equals(100.0));
270 271
    expect(tip.localToGlobal(tip.size.topLeft(Offset.zero)).dy, equals(100.0));
    expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(200.0));
Hixie's avatar
Hixie committed
272 273
  });

274
  testWidgets('Does tooltip end up in the right place - center prefer above does not fit', (WidgetTester tester) async {
275
    final GlobalKey key = GlobalKey();
276
    await tester.pumpWidget(
277
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
278
        textDirection: TextDirection.ltr,
279
        child: Overlay(
Ian Hickson's avatar
Ian Hickson committed
280
          initialEntries: <OverlayEntry>[
281
            OverlayEntry(
Ian Hickson's avatar
Ian Hickson committed
282
              builder: (BuildContext context) {
283
                return Stack(
Ian Hickson's avatar
Ian Hickson committed
284
                  children: <Widget>[
285
                    Positioned(
Ian Hickson's avatar
Ian Hickson committed
286 287
                      left: 400.0,
                      top: 299.0,
288
                      child: Tooltip(
Ian Hickson's avatar
Ian Hickson committed
289 290 291 292 293 294
                        key: key,
                        message: tooltipText,
                        height: 190.0,
                        padding: const EdgeInsets.all(0.0),
                        verticalOffset: 100.0,
                        preferBelow: false,
295
                        child: const SizedBox(
Ian Hickson's avatar
Ian Hickson committed
296 297 298 299 300 301 302 303 304 305 306 307
                          width: 0.0,
                          height: 0.0,
                        ),
                      ),
                    ),
                  ],
                );
              },
            ),
          ],
        ),
      ),
308
    );
309
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
310
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
Hixie's avatar
Hixie committed
311

312 313
    // we try to put it here but it doesn't fit:
    /********************* 800x600 screen
314 315
     *        ___        * }- 10.0 margin
     *       |___|       * }-190.0 height (starts at y=9.0)
316 317 318 319 320 321
     *         |         * }-100.0 vertical offset
     *         o         * y=299.0
     *                   *
     *                   *
     *                   *
     *********************/
Hixie's avatar
Hixie committed
322

323 324 325 326 327 328
    // so we put it here:
    /********************* 800x600 screen
     *                   *
     *                   *
     *         o         * y=299.0
     *        _|_        * }-100.0 vertical offset
329 330
     *       |___|       * }-190.0 height
     *                   * }- 10.0 margin
331
     *********************/
Hixie's avatar
Hixie committed
332

333 334 335
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
336
    expect(tip.size.height, equals(190.0));
337 338
    expect(tip.localToGlobal(tip.size.topLeft(Offset.zero)).dy, equals(399.0));
    expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(589.0));
Hixie's avatar
Hixie committed
339 340
  });

341
  testWidgets('Does tooltip end up in the right place - center prefer below fits', (WidgetTester tester) async {
342
    final GlobalKey key = GlobalKey();
343
    await tester.pumpWidget(
344
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
345
        textDirection: TextDirection.ltr,
346
        child: Overlay(
Ian Hickson's avatar
Ian Hickson committed
347
          initialEntries: <OverlayEntry>[
348
            OverlayEntry(
Ian Hickson's avatar
Ian Hickson committed
349
              builder: (BuildContext context) {
350
                return Stack(
Ian Hickson's avatar
Ian Hickson committed
351
                  children: <Widget>[
352
                    Positioned(
Ian Hickson's avatar
Ian Hickson committed
353 354
                      left: 400.0,
                      top: 300.0,
355
                      child: Tooltip(
Ian Hickson's avatar
Ian Hickson committed
356 357 358 359 360 361
                        key: key,
                        message: tooltipText,
                        height: 190.0,
                        padding: const EdgeInsets.all(0.0),
                        verticalOffset: 100.0,
                        preferBelow: true,
362
                        child: const SizedBox(
Ian Hickson's avatar
Ian Hickson committed
363 364 365 366 367 368 369 370 371 372 373 374
                          width: 0.0,
                          height: 0.0,
                        ),
                      ),
                    ),
                  ],
                );
              },
            ),
          ],
        ),
      ),
375
    );
376
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
377
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
Hixie's avatar
Hixie committed
378

379 380 381 382 383
    /********************* 800x600 screen
     *                   *
     *                   *
     *         o         * y=300.0
     *        _|_        * }-100.0 vertical offset
384 385
     *       |___|       * }-190.0 height
     *                   * }- 10.0 margin
386
     *********************/
Hixie's avatar
Hixie committed
387

388 389 390
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
391
    expect(tip.size.height, equals(190.0));
392 393
    expect(tip.localToGlobal(tip.size.topLeft(Offset.zero)).dy, equals(400.0));
    expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(590.0));
Hixie's avatar
Hixie committed
394 395
  });

396
  testWidgets('Does tooltip end up in the right place - way off to the right', (WidgetTester tester) async {
397
    final GlobalKey key = GlobalKey();
398
    await tester.pumpWidget(
399
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
400
        textDirection: TextDirection.ltr,
401
        child: Overlay(
Ian Hickson's avatar
Ian Hickson committed
402
          initialEntries: <OverlayEntry>[
403
            OverlayEntry(
Ian Hickson's avatar
Ian Hickson committed
404
              builder: (BuildContext context) {
405
                return Stack(
Ian Hickson's avatar
Ian Hickson committed
406
                  children: <Widget>[
407
                    Positioned(
Ian Hickson's avatar
Ian Hickson committed
408 409
                      left: 1600.0,
                      top: 300.0,
410
                      child: Tooltip(
Ian Hickson's avatar
Ian Hickson committed
411 412 413 414 415 416
                        key: key,
                        message: tooltipText,
                        height: 10.0,
                        padding: const EdgeInsets.all(0.0),
                        verticalOffset: 10.0,
                        preferBelow: true,
417
                        child: const SizedBox(
Ian Hickson's avatar
Ian Hickson committed
418 419 420 421 422 423 424 425 426 427 428 429
                          width: 0.0,
                          height: 0.0,
                        ),
                      ),
                    ),
                  ],
                );
              },
            ),
          ],
        ),
      ),
430
    );
431
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
432
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
Hixie's avatar
Hixie committed
433

434 435 436 437 438 439 440 441 442
    /********************* 800x600 screen
     *                   *
     *                   *
     *                   * y=300.0;   target -->   o
     *              ___| * }-10.0 vertical offset
     *             |___| * }-10.0 height
     *                   *
     *                   * }-10.0 margin
     *********************/
Hixie's avatar
Hixie committed
443

444 445 446
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
447
    expect(tip.size.height, equals(14.0));
448 449
    expect(tip.localToGlobal(tip.size.topLeft(Offset.zero)).dy, equals(310.0));
    expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dx, equals(790.0));
450
    expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(324.0));
451
  });
Hixie's avatar
Hixie committed
452

453
  testWidgets('Does tooltip end up in the right place - near the edge', (WidgetTester tester) async {
454
    final GlobalKey key = GlobalKey();
455
    await tester.pumpWidget(
456
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
457
        textDirection: TextDirection.ltr,
458
        child: Overlay(
Ian Hickson's avatar
Ian Hickson committed
459
          initialEntries: <OverlayEntry>[
460
            OverlayEntry(
Ian Hickson's avatar
Ian Hickson committed
461
              builder: (BuildContext context) {
462
                return Stack(
Ian Hickson's avatar
Ian Hickson committed
463
                  children: <Widget>[
464
                    Positioned(
Ian Hickson's avatar
Ian Hickson committed
465 466
                      left: 780.0,
                      top: 300.0,
467
                      child: Tooltip(
Ian Hickson's avatar
Ian Hickson committed
468 469 470 471 472 473
                        key: key,
                        message: tooltipText,
                        height: 10.0,
                        padding: const EdgeInsets.all(0.0),
                        verticalOffset: 10.0,
                        preferBelow: true,
474
                        child: const SizedBox(
Ian Hickson's avatar
Ian Hickson committed
475 476 477 478 479 480 481 482 483 484 485 486
                          width: 0.0,
                          height: 0.0,
                        ),
                      ),
                    ),
                  ],
                );
              },
            ),
          ],
        ),
      ),
487
    );
488
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
489
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
Hixie's avatar
Hixie committed
490

491 492 493 494 495 496 497 498 499
    /********************* 800x600 screen
     *                   *
     *                   *
     *                o  * y=300.0
     *              __|  * }-10.0 vertical offset
     *             |___| * }-10.0 height
     *                   *
     *                   * }-10.0 margin
     *********************/
Hixie's avatar
Hixie committed
500

501 502 503
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
504
    expect(tip.size.height, equals(14.0));
505 506
    expect(tip.localToGlobal(tip.size.topLeft(Offset.zero)).dy, equals(310.0));
    expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dx, equals(790.0));
507
    expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(324.0));
508
  });
Hixie's avatar
Hixie committed
509

510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
  testWidgets('Custom tooltip margin', (WidgetTester tester) async {
    const double _customMarginValue = 10.0;
    final GlobalKey key = GlobalKey();
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: Overlay(
          initialEntries: <OverlayEntry>[
            OverlayEntry(
              builder: (BuildContext context) {
                return Tooltip(
                  key: key,
                  message: tooltipText,
                  padding: const EdgeInsets.all(0.0),
                  margin: const EdgeInsets.all(_customMarginValue),
525
                  child: const SizedBox(
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
                    width: 0.0,
                    height: 0.0,
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

    final Offset topLeftTipInGlobal = tester.getTopLeft(
      _findTooltipContainer(tooltipText),
    );
    final Offset topLeftTooltipContentInGlobal = tester.getTopLeft(find.text(tooltipText));
    expect(topLeftTooltipContentInGlobal.dx, topLeftTipInGlobal.dx + _customMarginValue);
    expect(topLeftTooltipContentInGlobal.dy, topLeftTipInGlobal.dy + _customMarginValue);

    final Offset topRightTipInGlobal = tester.getTopRight(
      _findTooltipContainer(tooltipText),
    );
    final Offset topRightTooltipContentInGlobal = tester.getTopRight(find.text(tooltipText));
    expect(topRightTooltipContentInGlobal.dx, topRightTipInGlobal.dx - _customMarginValue);
    expect(topRightTooltipContentInGlobal.dy, topRightTipInGlobal.dy + _customMarginValue);

    final Offset bottomLeftTipInGlobal = tester.getBottomLeft(
      _findTooltipContainer(tooltipText),
    );
    final Offset bottomLeftTooltipContentInGlobal = tester.getBottomLeft(find.text(tooltipText));
    expect(bottomLeftTooltipContentInGlobal.dx, bottomLeftTipInGlobal.dx + _customMarginValue);
    expect(bottomLeftTooltipContentInGlobal.dy, bottomLeftTipInGlobal.dy - _customMarginValue);

    final Offset bottomRightTipInGlobal = tester.getBottomRight(
      _findTooltipContainer(tooltipText),
    );
    final Offset bottomRightTooltipContentInGlobal = tester.getBottomRight(find.text(tooltipText));
    expect(bottomRightTooltipContentInGlobal.dx, bottomRightTipInGlobal.dx - _customMarginValue);
    expect(bottomRightTooltipContentInGlobal.dy, bottomRightTipInGlobal.dy - _customMarginValue);
  });

568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
  testWidgets('Default tooltip message textStyle - light', (WidgetTester tester) async {
    final GlobalKey key = GlobalKey();
    await tester.pumpWidget(MaterialApp(
      home: Tooltip(
        key: key,
        message: tooltipText,
        child: Container(
          width: 100.0,
          height: 100.0,
          color: Colors.green[500],
        ),
      ),
    ));
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

    final TextStyle textStyle = tester.widget<Text>(find.text(tooltipText)).style;
    expect(textStyle.color, Colors.white);
    expect(textStyle.fontFamily, 'Roboto');
    expect(textStyle.decoration, TextDecoration.none);
588
    expect(textStyle.debugLabel, '((englishLike body1 2014).merge(blackMountainView bodyText2)).copyWith');
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
  });

  testWidgets('Default tooltip message textStyle - dark', (WidgetTester tester) async {
    final GlobalKey key = GlobalKey();
    await tester.pumpWidget(MaterialApp(
      theme: ThemeData(
        brightness: Brightness.dark,
      ),
      home: Tooltip(
        key: key,
        message: tooltipText,
        child: Container(
          width: 100.0,
          height: 100.0,
          color: Colors.green[500],
        ),
      ),
    ));
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

    final TextStyle textStyle = tester.widget<Text>(find.text(tooltipText)).style;
    expect(textStyle.color, Colors.black);
    expect(textStyle.fontFamily, 'Roboto');
    expect(textStyle.decoration, TextDecoration.none);
614
    expect(textStyle.debugLabel, '((englishLike body1 2014).merge(whiteMountainView bodyText2)).copyWith');
615 616 617 618 619 620 621 622 623
  });

  testWidgets('Custom tooltip message textStyle', (WidgetTester tester) async {
    final GlobalKey key = GlobalKey();
    await tester.pumpWidget(MaterialApp(
      home: Tooltip(
        key: key,
        textStyle: const TextStyle(
          color: Colors.orange,
624
          decoration: TextDecoration.underline,
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
        ),
        message: tooltipText,
        child: Container(
          width: 100.0,
          height: 100.0,
          color: Colors.green[500],
        ),
      ),
    ));
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

    final TextStyle textStyle = tester.widget<Text>(find.text(tooltipText)).style;
    expect(textStyle.color, Colors.orange);
    expect(textStyle.fontFamily, null);
    expect(textStyle.decoration, TextDecoration.underline);
  });

643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
  testWidgets('Tooltip overlay respects ambient Directionality', (WidgetTester tester) async {
    // Regression test for https://github.com/flutter/flutter/issues/40702.
    Widget buildApp(String text, TextDirection textDirection) {
      return MaterialApp(
        home: Directionality(
          textDirection: textDirection,
          child: Center(
            child: Tooltip(
              message: text,
              child: Container(
                width: 100.0,
                height: 100.0,
                color: Colors.green[500],
              ),
            ),
          ),
        ),
      );
    }

    await tester.pumpWidget(buildApp(tooltipText, TextDirection.rtl));
    await tester.longPress(find.byType(Tooltip));
    expect(find.text(tooltipText), findsOneWidget);
    RenderParagraph tooltipRenderParagraph = tester.renderObject<RenderParagraph>(find.text(tooltipText));
    expect(tooltipRenderParagraph.textDirection, TextDirection.rtl);

    await tester.pumpWidget(buildApp(tooltipText, TextDirection.ltr));
    await tester.longPress(find.byType(Tooltip));
    expect(find.text(tooltipText), findsOneWidget);
    tooltipRenderParagraph = tester.renderObject<RenderParagraph>(find.text(tooltipText));
    expect(tooltipRenderParagraph.textDirection, TextDirection.ltr);
  });

676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
  testWidgets('Tooltip overlay wrapped with a non-fallback DefaultTextStyle widget', (WidgetTester tester) async {
    // A Material widget is needed as an ancestor of the Text widget.
    // It is invalid to have text in a Material application that
    // does not have a Material ancestor.
    final GlobalKey key = GlobalKey();
    await tester.pumpWidget(MaterialApp(
      home: Tooltip(
        key: key,
        message: tooltipText,
        child: Container(
          width: 100.0,
          height: 100.0,
          color: Colors.green[500],
        ),
      ),
    ));
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

    final TextStyle textStyle = tester.widget<DefaultTextStyle>(
      find.ancestor(
        of: find.text(tooltipText),
        matching: find.byType(DefaultTextStyle),
      ).first,
    ).style;

    // The default fallback text style results in a text with a
    // double underline of Color(0xffffff00).
    expect(textStyle.decoration, isNot(TextDecoration.underline));
    expect(textStyle.decorationColor, isNot(const Color(0xffffff00)));
    expect(textStyle.decorationStyle, isNot(TextDecorationStyle.double));
  });

709 710 711 712 713 714 715 716 717 718 719 720
  testWidgets('Does tooltip end up with the right default size, shape, and color', (WidgetTester tester) async {
    final GlobalKey key = GlobalKey();
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: Overlay(
          initialEntries: <OverlayEntry>[
            OverlayEntry(
              builder: (BuildContext context) {
                return Tooltip(
                  key: key,
                  message: tooltipText,
721
                  child: const SizedBox(
722 723 724 725 726 727 728 729 730 731 732 733 734
                    width: 0.0,
                    height: 0.0,
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

735 736 737
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
738 739 740 741 742 743
    expect(tip.size.height, equals(32.0));
    expect(tip.size.width, equals(74.0));
    expect(tip, paints..rrect(
      rrect: RRect.fromRectAndRadius(tip.paintBounds, const Radius.circular(4.0)),
      color: const Color(0xe6616161),
    ));
744
  });
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762

  testWidgets('Can tooltip decoration be customized', (WidgetTester tester) async {
    final GlobalKey key = GlobalKey();
    const Decoration customDecoration = ShapeDecoration(
      shape: StadiumBorder(),
      color: Color(0x80800000),
    );
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: Overlay(
          initialEntries: <OverlayEntry>[
            OverlayEntry(
              builder: (BuildContext context) {
                return Tooltip(
                  key: key,
                  decoration: customDecoration,
                  message: tooltipText,
763
                  child: const SizedBox(
764 765 766 767 768 769 770 771 772 773 774 775 776
                    width: 0.0,
                    height: 0.0,
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
    (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file.
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

777 778 779
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
780 781 782 783 784
    expect(tip.size.height, equals(32.0));
    expect(tip.size.width, equals(74.0));
    expect(tip, paints..path(
      color: const Color(0x80800000),
    ));
785
  });
786

787
  testWidgets('Tooltip stays after long press', (WidgetTester tester) async {
788
    await tester.pumpWidget(
789 790 791
      MaterialApp(
        home: Center(
          child: Tooltip(
792
            message: tooltipText,
793
            child: Container(
794 795
              width: 100.0,
              height: 100.0,
796
              color: Colors.green[500],
797 798 799
            ),
          ),
        ),
800
      ),
801 802
    );

803
    final Finder tooltip = find.byType(Tooltip);
804
    TestGesture gesture = await tester.startGesture(tester.getCenter(tooltip));
805 806

    // long press reveals tooltip
807 808
    await tester.pump(kLongPressTimeout);
    await tester.pump(const Duration(milliseconds: 10));
809
    expect(find.text(tooltipText), findsOneWidget);
810 811 812 813
    await gesture.up();

    // tap (down, up) gesture hides tooltip, since its not
    // a long press
814 815
    await tester.tap(tooltip);
    await tester.pump(const Duration(milliseconds: 10));
816 817 818
    expect(find.text(tooltipText), findsNothing);

    // long press once more
819 820 821
    gesture = await tester.startGesture(tester.getCenter(tooltip));
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 300));
822
    expect(find.text(tooltipText), findsNothing);
823

824
    await tester.pump(kLongPressTimeout);
825
    await tester.pump(const Duration(milliseconds: 10));
826
    expect(find.text(tooltipText), findsOneWidget);
827 828

    // keep holding the long press, should still show tooltip
829
    await tester.pump(kLongPressTimeout);
830
    expect(find.text(tooltipText), findsOneWidget);
831 832 833
    gesture.up();
  });

834 835
  testWidgets('Tooltip shows/hides when hovered', (WidgetTester tester) async {
    const Duration waitDuration = Duration(milliseconds: 0);
836 837 838 839 840
    TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
    addTearDown(() async {
      if (gesture != null)
        return gesture.removePointer();
    });
841 842 843 844 845 846
    await gesture.addPointer();
    await gesture.moveTo(const Offset(1.0, 1.0));
    await tester.pump();
    await gesture.moveTo(Offset.zero);

    await tester.pumpWidget(
847
      const MaterialApp(
848 849 850 851
        home: Center(
          child: Tooltip(
            message: tooltipText,
            waitDuration: waitDuration,
852
            child: SizedBox(
853 854 855 856 857
              width: 100.0,
              height: 100.0,
            ),
          ),
        ),
858
      ),
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
    );

    final Finder tooltip = find.byType(Tooltip);
    await gesture.moveTo(Offset.zero);
    await tester.pump();
    await gesture.moveTo(tester.getCenter(tooltip));
    await tester.pump();
    // Wait for it to appear.
    await tester.pump(waitDuration);
    expect(find.text(tooltipText), findsOneWidget);

    // Wait a looong time to make sure that it doesn't go away if the mouse is
    // still over the widget.
    await tester.pump(const Duration(days: 1));
    await tester.pumpAndSettle();
    expect(find.text(tooltipText), findsOneWidget);

    await gesture.moveTo(Offset.zero);
    await tester.pump();

    // Wait for it to disappear.
    await tester.pumpAndSettle();
    await gesture.removePointer();
882
    gesture = null;
883 884 885
    expect(find.text(tooltipText), findsNothing);
  });

886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
  testWidgets('Tooltip does not attempt to show after unmount', (WidgetTester tester) async {
    // Regression test for https://github.com/flutter/flutter/issues/54096.
    const Duration waitDuration = Duration(seconds: 1);
    final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
    addTearDown(() async {
      if (gesture != null)
        return gesture.removePointer();
    });
    await gesture.addPointer();
    await gesture.moveTo(const Offset(1.0, 1.0));
    await tester.pump();
    await gesture.moveTo(Offset.zero);

    await tester.pumpWidget(
      const MaterialApp(
        home: Center(
          child: Tooltip(
            message: tooltipText,
            waitDuration: waitDuration,
            child: SizedBox(
              width: 100.0,
              height: 100.0,
            ),
          ),
        ),
      ),
    );

    final Finder tooltip = find.byType(Tooltip);
    await gesture.moveTo(Offset.zero);
    await tester.pump();
    await gesture.moveTo(tester.getCenter(tooltip));
    await tester.pump();

    // Pump another random widget to unmount the Tooltip widget.
    await tester.pumpWidget(
        const MaterialApp(
          home: Center(
            child: SizedBox(),
        ),
      ),
    );

    // If the issue regresses, an exception will be thrown while we are waiting.
    await tester.pump(waitDuration);
  });

933
  testWidgets('Does tooltip contribute semantics', (WidgetTester tester) async {
934
    final SemanticsTester semantics = SemanticsTester(tester);
935

936
    final GlobalKey key = GlobalKey();
937
    await tester.pumpWidget(
938
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
939
        textDirection: TextDirection.ltr,
940
        child: Overlay(
Ian Hickson's avatar
Ian Hickson committed
941
          initialEntries: <OverlayEntry>[
942
            OverlayEntry(
Ian Hickson's avatar
Ian Hickson committed
943
              builder: (BuildContext context) {
944
                return Stack(
Ian Hickson's avatar
Ian Hickson committed
945
                  children: <Widget>[
946
                    Positioned(
Ian Hickson's avatar
Ian Hickson committed
947 948
                      left: 780.0,
                      top: 300.0,
949
                      child: Tooltip(
Ian Hickson's avatar
Ian Hickson committed
950 951
                        key: key,
                        message: tooltipText,
952
                        child: const SizedBox(width: 10.0, height: 10.0),
Ian Hickson's avatar
Ian Hickson committed
953 954 955 956 957 958 959 960 961
                      ),
                    ),
                  ],
                );
              },
            ),
          ],
        ),
      ),
962
    );
963

964
    final TestSemantics expected = TestSemantics.root(
965
      children: <TestSemantics>[
966
        TestSemantics.rootChild(
967 968 969 970
          id: 1,
          label: 'TIP',
          textDirection: TextDirection.ltr,
        ),
971
      ],
972 973 974
    );

    expect(semantics, hasSemantics(expected, ignoreTransform: true, ignoreRect: true));
Hixie's avatar
Hixie committed
975

976
    // Before using "as dynamic" in your code, see note at the top of the file.
977
    (key.currentState as dynamic).ensureTooltipVisible(); // this triggers a rebuild of the semantics because the tree changes
Hixie's avatar
Hixie committed
978

979
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
980

981
    expect(semantics, hasSemantics(expected, ignoreTransform: true, ignoreRect: true));
982 983

    semantics.dispose();
Hixie's avatar
Hixie committed
984
  });
985 986 987

  testWidgets('Tooltip overlay does not update', (WidgetTester tester) async {
    Widget buildApp(String text) {
988 989 990
      return MaterialApp(
        home: Center(
          child: Tooltip(
991
            message: text,
992
            child: Container(
993 994
              width: 100.0,
              height: 100.0,
995
              color: Colors.green[500],
996 997 998
            ),
          ),
        ),
999 1000 1001 1002 1003 1004 1005 1006
      );
    }

    await tester.pumpWidget(buildApp(tooltipText));
    await tester.longPress(find.byType(Tooltip));
    expect(find.text(tooltipText), findsOneWidget);
    await tester.pumpWidget(buildApp('NEW'));
    expect(find.text(tooltipText), findsOneWidget);
1007
    await tester.tapAt(const Offset(5.0, 5.0));
1008 1009 1010 1011 1012 1013 1014
    await tester.pump();
    await tester.pump(const Duration(seconds: 1));
    expect(find.text(tooltipText), findsNothing);
    await tester.longPress(find.byType(Tooltip));
    expect(find.text(tooltipText), findsNothing);
  });

1015 1016
  testWidgets('Tooltip text scales with textScaleFactor', (WidgetTester tester) async {
    Widget buildApp(String text, { double textScaleFactor }) {
1017 1018 1019
      return MediaQuery(
        data: MediaQueryData(textScaleFactor: textScaleFactor),
        child: Directionality(
1020
          textDirection: TextDirection.ltr,
1021
          child: Navigator(
1022
            onGenerateRoute: (RouteSettings settings) {
1023
              return MaterialPageRoute<void>(
1024
                builder: (BuildContext context) {
1025 1026
                  return Center(
                    child: Tooltip(
1027
                      message: text,
1028
                      child: Container(
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
                        width: 100.0,
                        height: 100.0,
                        color: Colors.green[500],
                      ),
                    ),
                  );
                }
              );
            },
          ),
        ),
      );
    }

    await tester.pumpWidget(buildApp(tooltipText, textScaleFactor: 1.0));
    await tester.longPress(find.byType(Tooltip));
    expect(find.text(tooltipText), findsOneWidget);
    expect(tester.getSize(find.text(tooltipText)), equals(const Size(42.0, 14.0)));
1047 1048 1049
    RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
1050 1051 1052 1053 1054 1055
    expect(tip.size.height, equals(32.0));

    await tester.pumpWidget(buildApp(tooltipText, textScaleFactor: 4.0));
    await tester.longPress(find.byType(Tooltip));
    expect(find.text(tooltipText), findsOneWidget);
    expect(tester.getSize(find.text(tooltipText)), equals(const Size(168.0, 56.0)));
1056 1057 1058
    tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
1059
    expect(tip.size.height, equals(56.0));
1060
  });
1061

1062
  testWidgets('Haptic feedback', (WidgetTester tester) async {
1063
    final FeedbackTester feedback = FeedbackTester();
Ian Hickson's avatar
Ian Hickson committed
1064
    await tester.pumpWidget(
1065 1066 1067
      MaterialApp(
        home: Center(
          child: Tooltip(
Ian Hickson's avatar
Ian Hickson committed
1068
            message: 'Foo',
1069
            child: Container(
Ian Hickson's avatar
Ian Hickson committed
1070 1071 1072 1073 1074 1075 1076
              width: 100.0,
              height: 100.0,
              color: Colors.green[500],
            ),
          ),
        ),
      ),
1077 1078 1079 1080 1081 1082 1083 1084 1085
    );

    await tester.longPress(find.byType(Tooltip));
    await tester.pumpAndSettle(const Duration(seconds: 1));
    expect(feedback.hapticCount, 1);

    feedback.dispose();
  });

1086
  testWidgets('Semantics included', (WidgetTester tester) async {
1087
    final SemanticsTester semantics = SemanticsTester(tester);
1088 1089

    await tester.pumpWidget(
1090 1091
      const MaterialApp(
        home: Center(
1092
          child: Tooltip(
1093
            message: 'Foo',
1094
            child: Text('Bar'),
1095 1096 1097 1098 1099
          ),
        ),
      ),
    );

1100
    expect(semantics, hasSemantics(TestSemantics.root(
1101
      children: <TestSemantics>[
1102
        TestSemantics.rootChild(
1103
          children: <TestSemantics>[
1104
            TestSemantics(
1105
              children: <TestSemantics>[
1106
                TestSemantics(
1107 1108 1109 1110 1111 1112 1113
                  flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
                  children: <TestSemantics>[
                    TestSemantics(
                      label: 'Foo\nBar',
                      textDirection: TextDirection.ltr,
                    ),
                  ],
1114
                ),
1115 1116
              ],
            ),
1117
          ],
1118 1119 1120 1121 1122 1123 1124 1125
        ),
      ],
    ), ignoreRect: true, ignoreId: true, ignoreTransform: true));

    semantics.dispose();
  });

  testWidgets('Semantics excluded', (WidgetTester tester) async {
1126
    final SemanticsTester semantics = SemanticsTester(tester);
1127 1128

    await tester.pumpWidget(
1129 1130
      const MaterialApp(
        home: Center(
1131
          child: Tooltip(
1132
            message: 'Foo',
1133
            child: Text('Bar'),
1134 1135 1136 1137 1138 1139
            excludeFromSemantics: true,
          ),
        ),
      ),
    );

1140
    expect(semantics, hasSemantics(TestSemantics.root(
1141
      children: <TestSemantics>[
1142
        TestSemantics.rootChild(
1143
          children: <TestSemantics>[
1144
            TestSemantics(
1145
              children: <TestSemantics>[
1146
                TestSemantics(
1147 1148 1149 1150 1151 1152 1153
                  flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
                  children: <TestSemantics>[
                    TestSemantics(
                      label: 'Bar',
                      textDirection: TextDirection.ltr,
                    ),
                  ],
1154
                ),
1155 1156
              ],
            ),
1157
          ],
1158 1159 1160 1161 1162 1163 1164
        ),
      ],
    ), ignoreRect: true, ignoreId: true, ignoreTransform: true));

    semantics.dispose();
  });

1165 1166
  testWidgets('has semantic events', (WidgetTester tester) async {
    final List<dynamic> semanticEvents = <dynamic>[];
1167
    SystemChannels.accessibility.setMockMessageHandler((dynamic message) async {
1168 1169
      semanticEvents.add(message);
    });
1170
    final SemanticsTester semantics = SemanticsTester(tester);
1171 1172

    await tester.pumpWidget(
1173 1174 1175
      MaterialApp(
        home: Center(
          child: Tooltip(
1176
            message: 'Foo',
1177
            child: Container(
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
              width: 100.0,
              height: 100.0,
              color: Colors.green[500],
            ),
          ),
        ),
      ),
    );

    await tester.longPress(find.byType(Tooltip));
    final RenderObject object = tester.firstRenderObject(find.byType(Tooltip));

    expect(semanticEvents, unorderedEquals(<dynamic>[
      <String, dynamic>{
        'type': 'longPress',
        'nodeId': findDebugSemantics(object).id,
        'data': <String, dynamic>{},
      },
      <String, dynamic>{
        'type': 'tooltip',
        'data': <String, dynamic>{
          'message': 'Foo',
        },
      },
    ]));
    semantics.dispose();
    SystemChannels.accessibility.setMockMessageHandler(null);
  });
1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229
  testWidgets('default Tooltip debugFillProperties', (WidgetTester tester) async {
    final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();

    const Tooltip(message: 'message',).debugFillProperties(builder);

    final List<String> description = builder.properties
      .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
      .map((DiagnosticsNode node) => node.toString()).toList();

    expect(description, <String>[
      '"message"',
    ]);
  });
  testWidgets('Tooltip implements debugFillProperties', (WidgetTester tester) async {
    final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();

    // Not checking controller, inputFormatters, focusNode
    const Tooltip(
      key: ValueKey<String>('foo'),
      message: 'message',
      decoration: BoxDecoration(),
      waitDuration: Duration(seconds: 1),
      showDuration: Duration(seconds: 2),
      padding: EdgeInsets.zero,
1230
      margin: EdgeInsets.all(5.0),
1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244
      height: 100.0,
      excludeFromSemantics: true,
      preferBelow: false,
      verticalOffset: 50.0,
    ).debugFillProperties(builder);

    final List<String> description = builder.properties
      .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
      .map((DiagnosticsNode node) => node.toString()).toList();

    expect(description, <String>[
      '"message"',
      'height: 100.0',
      'padding: EdgeInsets.zero',
1245
      'margin: EdgeInsets.all(5.0)',
1246 1247 1248 1249 1250 1251 1252
      'vertical offset: 50.0',
      'position: above',
      'semantics: excluded',
      'wait duration: 0:00:01.000000',
      'show duration: 0:00:02.000000',
    ]);
  });
1253 1254 1255 1256 1257
}

SemanticsNode findDebugSemantics(RenderObject object) {
  if (object.debugSemantics != null)
    return object.debugSemantics;
1258
  return findDebugSemantics(object.parent as RenderObject);
Hixie's avatar
Hixie committed
1259
}