tooltip_test.dart 44.4 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
import 'dart:ui';

7
import 'package:flutter/services.dart';
8 9
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/gestures.dart';
Hixie's avatar
Hixie committed
10 11 12
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

13
import '../rendering/mock_canvas.dart';
14
import '../widgets/semantics_tester.dart';
15
import 'feedback_tester.dart';
Hixie's avatar
Hixie committed
16

Ian Hickson's avatar
Ian Hickson committed
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
// 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.

32 33
const String tooltipText = 'TIP';

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

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

80 81 82 83 84 85 86 87
    /********************* 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)
     *                   *
     *********************/
88

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

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

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

199 200 201 202 203 204 205 206
    /********************* 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
207

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

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

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

263 264 265
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
266
    expect(tip.size.height, equals(100.0));
267 268
    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
269 270
  });

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

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

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

330 331 332
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
333
    expect(tip.size.height, equals(190.0));
334 335
    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
336 337
  });

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

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

385 386 387
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
388
    expect(tip.size.height, equals(190.0));
389 390
    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
391 392
  });

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

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

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

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

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

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

507 508 509 510 511 512 513 514 515 516 517 518 519
  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,
520
                  padding: EdgeInsets.zero,
521
                  margin: const EdgeInsets.all(_customMarginValue),
522
                  child: const SizedBox(
523 524 525 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
                    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);
  });

565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
  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)

581
    final TextStyle textStyle = tester.widget<Text>(find.text(tooltipText)).style!;
582 583 584
    expect(textStyle.color, Colors.white);
    expect(textStyle.fontFamily, 'Roboto');
    expect(textStyle.decoration, TextDecoration.none);
585
    expect(textStyle.debugLabel, '((englishLike body1 2014).merge(blackMountainView bodyText2)).copyWith');
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
  });

  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)

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

  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,
621
          decoration: TextDecoration.underline,
622 623 624 625 626 627 628 629 630 631 632 633
        ),
        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)

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

640 641 642 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
  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);
  });

673 674 675 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
  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));
  });

706 707 708 709 710 711 712 713 714 715 716 717
  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,
718
                  child: const SizedBox(
719 720 721 722 723 724 725 726 727 728 729 730 731
                    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)

732 733 734
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
735 736 737 738 739 740
    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),
    ));
741
  });
742

743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
  testWidgets('Tooltip default size, shape, and color test for Desktop', (WidgetTester tester) async {
    // Regressing test for https://github.com/flutter/flutter/issues/68601
    final GlobalKey key = GlobalKey();
    await tester.pumpWidget(
      MaterialApp(
        home: Tooltip(
          key: key,
          message: tooltipText,
          child: const SizedBox(
            width: 0.0,
            height: 0.0,
          ),
        ),
      ),
    );
    (key.currentState as dynamic).ensureTooltipVisible();
    await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

    final RenderParagraph tooltipRenderParagraph = tester.renderObject<RenderParagraph>(find.text(tooltipText));
    expect(tooltipRenderParagraph.textSize.height, equals(10.0));

    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
    expect(tip.size.height, equals(24.0));
    expect(tip.size.width, equals(46.0));
    expect(tip, paints..rrect(
      rrect: RRect.fromRectAndRadius(tip.paintBounds, const Radius.circular(4.0)),
      color: const Color(0xe6616161),
    ));
  }, variant: const TargetPlatformVariant(<TargetPlatform>{TargetPlatform.macOS, TargetPlatform.linux, TargetPlatform.windows}));

775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
  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,
792
                  child: const SizedBox(
793 794 795 796 797 798 799 800 801 802 803 804 805
                    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)

806 807 808
    final RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
809 810 811 812 813
    expect(tip.size.height, equals(32.0));
    expect(tip.size.width, equals(74.0));
    expect(tip, paints..path(
      color: const Color(0x80800000),
    ));
814
  });
815

816
  testWidgets('Tooltip stays after long press', (WidgetTester tester) async {
817
    await tester.pumpWidget(
818 819 820
      MaterialApp(
        home: Center(
          child: Tooltip(
821
            message: tooltipText,
822
            child: Container(
823 824
              width: 100.0,
              height: 100.0,
825
              color: Colors.green[500],
826 827 828
            ),
          ),
        ),
829
      ),
830 831
    );

832
    final Finder tooltip = find.byType(Tooltip);
833
    TestGesture gesture = await tester.startGesture(tester.getCenter(tooltip));
834 835

    // long press reveals tooltip
836 837
    await tester.pump(kLongPressTimeout);
    await tester.pump(const Duration(milliseconds: 10));
838
    expect(find.text(tooltipText), findsOneWidget);
839 840 841 842
    await gesture.up();

    // tap (down, up) gesture hides tooltip, since its not
    // a long press
843 844
    await tester.tap(tooltip);
    await tester.pump(const Duration(milliseconds: 10));
845 846 847
    expect(find.text(tooltipText), findsNothing);

    // long press once more
848 849 850
    gesture = await tester.startGesture(tester.getCenter(tooltip));
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 300));
851
    expect(find.text(tooltipText), findsNothing);
852

853
    await tester.pump(kLongPressTimeout);
854
    await tester.pump(const Duration(milliseconds: 10));
855
    expect(find.text(tooltipText), findsOneWidget);
856 857

    // keep holding the long press, should still show tooltip
858
    await tester.pump(kLongPressTimeout);
859
    expect(find.text(tooltipText), findsOneWidget);
860
    await gesture.up();
861 862
  });

863
  testWidgets('Tooltip shows/hides when hovered', (WidgetTester tester) async {
864
    const Duration waitDuration = Duration.zero;
865
    TestGesture? gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
866 867 868 869
    addTearDown(() async {
      if (gesture != null)
        return gesture.removePointer();
    });
Michael Goderbauer's avatar
Michael Goderbauer committed
870
    await gesture.addPointer();
871 872 873 874 875
    await gesture.moveTo(const Offset(1.0, 1.0));
    await tester.pump();
    await gesture.moveTo(Offset.zero);

    await tester.pumpWidget(
876
      const MaterialApp(
877 878 879 880
        home: Center(
          child: Tooltip(
            message: tooltipText,
            waitDuration: waitDuration,
881
            child: SizedBox(
882 883 884 885 886
              width: 100.0,
              height: 100.0,
            ),
          ),
        ),
887
      ),
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
    );

    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();
911
    gesture = null;
912 913 914
    expect(find.text(tooltipText), findsNothing);
  });

915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
  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);
  });

962
  testWidgets('Does tooltip contribute semantics', (WidgetTester tester) async {
963
    final SemanticsTester semantics = SemanticsTester(tester);
964

965
    final GlobalKey key = GlobalKey();
966
    await tester.pumpWidget(
967
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
968
        textDirection: TextDirection.ltr,
969
        child: Overlay(
Ian Hickson's avatar
Ian Hickson committed
970
          initialEntries: <OverlayEntry>[
971
            OverlayEntry(
Ian Hickson's avatar
Ian Hickson committed
972
              builder: (BuildContext context) {
973
                return Stack(
Ian Hickson's avatar
Ian Hickson committed
974
                  children: <Widget>[
975
                    Positioned(
Ian Hickson's avatar
Ian Hickson committed
976 977
                      left: 780.0,
                      top: 300.0,
978
                      child: Tooltip(
Ian Hickson's avatar
Ian Hickson committed
979 980
                        key: key,
                        message: tooltipText,
981
                        child: const SizedBox(width: 10.0, height: 10.0),
Ian Hickson's avatar
Ian Hickson committed
982 983 984 985 986 987 988 989 990
                      ),
                    ),
                  ],
                );
              },
            ),
          ],
        ),
      ),
991
    );
992

993
    final TestSemantics expected = TestSemantics.root(
994
      children: <TestSemantics>[
995
        TestSemantics.rootChild(
996 997 998 999
          id: 1,
          label: 'TIP',
          textDirection: TextDirection.ltr,
        ),
1000
      ],
1001 1002 1003
    );

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

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

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

1010
    expect(semantics, hasSemantics(expected, ignoreTransform: true, ignoreRect: true));
1011 1012

    semantics.dispose();
Hixie's avatar
Hixie committed
1013
  });
1014 1015 1016

  testWidgets('Tooltip overlay does not update', (WidgetTester tester) async {
    Widget buildApp(String text) {
1017 1018 1019
      return MaterialApp(
        home: Center(
          child: Tooltip(
1020
            message: text,
1021
            child: Container(
1022 1023
              width: 100.0,
              height: 100.0,
1024
              color: Colors.green[500],
1025 1026 1027
            ),
          ),
        ),
1028 1029 1030 1031 1032 1033 1034 1035
      );
    }

    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);
1036
    await tester.tapAt(const Offset(5.0, 5.0));
1037 1038 1039 1040 1041 1042 1043
    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);
  });

1044
  testWidgets('Tooltip text scales with textScaleFactor', (WidgetTester tester) async {
1045
    Widget buildApp(String text, { required double textScaleFactor }) {
1046 1047 1048
      return MediaQuery(
        data: MediaQueryData(textScaleFactor: textScaleFactor),
        child: Directionality(
1049
          textDirection: TextDirection.ltr,
1050
          child: Navigator(
1051
            onGenerateRoute: (RouteSettings settings) {
1052
              return MaterialPageRoute<void>(
1053
                builder: (BuildContext context) {
1054 1055
                  return Center(
                    child: Tooltip(
1056
                      message: text,
1057
                      child: Container(
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
                        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)));
1076 1077 1078
    RenderBox tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
1079 1080 1081 1082 1083 1084
    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)));
1085 1086 1087
    tip = tester.renderObject(
      _findTooltipContainer(tooltipText),
    );
1088
    expect(tip.size.height, equals(56.0));
1089
  });
1090

1091
  testWidgets('Haptic feedback', (WidgetTester tester) async {
1092
    final FeedbackTester feedback = FeedbackTester();
Ian Hickson's avatar
Ian Hickson committed
1093
    await tester.pumpWidget(
1094 1095 1096
      MaterialApp(
        home: Center(
          child: Tooltip(
Ian Hickson's avatar
Ian Hickson committed
1097
            message: 'Foo',
1098
            child: Container(
Ian Hickson's avatar
Ian Hickson committed
1099 1100 1101 1102 1103 1104 1105
              width: 100.0,
              height: 100.0,
              color: Colors.green[500],
            ),
          ),
        ),
      ),
1106 1107 1108 1109 1110 1111 1112 1113 1114
    );

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

    feedback.dispose();
  });

1115
  testWidgets('Semantics included', (WidgetTester tester) async {
1116
    final SemanticsTester semantics = SemanticsTester(tester);
1117 1118

    await tester.pumpWidget(
1119 1120
      const MaterialApp(
        home: Center(
1121
          child: Tooltip(
1122
            message: 'Foo',
1123
            child: Text('Bar'),
1124 1125 1126 1127 1128
          ),
        ),
      ),
    );

1129
    expect(semantics, hasSemantics(TestSemantics.root(
1130
      children: <TestSemantics>[
1131
        TestSemantics.rootChild(
1132
          children: <TestSemantics>[
1133
            TestSemantics(
1134
              children: <TestSemantics>[
1135
                TestSemantics(
1136 1137 1138 1139 1140 1141 1142
                  flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
                  children: <TestSemantics>[
                    TestSemantics(
                      label: 'Foo\nBar',
                      textDirection: TextDirection.ltr,
                    ),
                  ],
1143
                ),
1144 1145
              ],
            ),
1146
          ],
1147 1148 1149 1150 1151 1152 1153 1154
        ),
      ],
    ), ignoreRect: true, ignoreId: true, ignoreTransform: true));

    semantics.dispose();
  });

  testWidgets('Semantics excluded', (WidgetTester tester) async {
1155
    final SemanticsTester semantics = SemanticsTester(tester);
1156 1157

    await tester.pumpWidget(
1158 1159
      const MaterialApp(
        home: Center(
1160
          child: Tooltip(
1161
            message: 'Foo',
1162
            child: Text('Bar'),
1163 1164 1165 1166 1167 1168
            excludeFromSemantics: true,
          ),
        ),
      ),
    );

1169
    expect(semantics, hasSemantics(TestSemantics.root(
1170
      children: <TestSemantics>[
1171
        TestSemantics.rootChild(
1172
          children: <TestSemantics>[
1173
            TestSemantics(
1174
              children: <TestSemantics>[
1175
                TestSemantics(
1176 1177 1178 1179 1180 1181 1182
                  flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
                  children: <TestSemantics>[
                    TestSemantics(
                      label: 'Bar',
                      textDirection: TextDirection.ltr,
                    ),
                  ],
1183
                ),
1184 1185
              ],
            ),
1186
          ],
1187 1188 1189 1190 1191 1192 1193
        ),
      ],
    ), ignoreRect: true, ignoreId: true, ignoreTransform: true));

    semantics.dispose();
  });

1194 1195
  testWidgets('has semantic events', (WidgetTester tester) async {
    final List<dynamic> semanticEvents = <dynamic>[];
1196
    SystemChannels.accessibility.setMockMessageHandler((dynamic message) async {
1197 1198
      semanticEvents.add(message);
    });
1199
    final SemanticsTester semantics = SemanticsTester(tester);
1200 1201

    await tester.pumpWidget(
1202 1203 1204
      MaterialApp(
        home: Center(
          child: Tooltip(
1205
            message: 'Foo',
1206
            child: Container(
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
              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);
  });
1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258
  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,
1259
      margin: EdgeInsets.all(5.0),
1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273
      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',
1274
      'margin: EdgeInsets.all(5.0)',
1275 1276 1277 1278 1279 1280 1281
      'vertical offset: 50.0',
      'position: above',
      'semantics: excluded',
      'wait duration: 0:00:01.000000',
      'show duration: 0:00:02.000000',
    ]);
  });
1282 1283 1284 1285
}

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