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

import 'package:flutter/cupertino.dart';
6 7 8
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
9
import 'package:flutter_test/flutter_test.dart';
10 11

void main() {
12
  final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
13
  const double kOpenScale = 1.15;
14

15
  Widget getChild() {
16 17 18 19 20 21 22
    return Container(
      width: 300.0,
      height: 100.0,
      color: CupertinoColors.activeOrange,
    );
  }

23 24 25 26
  Widget getBuilder(BuildContext context, Animation<double> animation) {
    return getChild();
  }

27
  Widget getContextMenu({
28 29
    Alignment alignment = Alignment.center,
    Size screenSize = const Size(800.0, 600.0),
30
    Widget? child,
31 32 33 34 35 36 37 38 39 40 41 42 43
  }) {
    return CupertinoApp(
      home: CupertinoPageScaffold(
        child: MediaQuery(
          data: MediaQueryData(size: screenSize),
          child: Align(
            alignment: alignment,
            child: CupertinoContextMenu(
              actions: <CupertinoContextMenuAction>[
                CupertinoContextMenuAction(
                  child: Text('CupertinoContextMenuAction $alignment'),
                ),
              ],
44
              child: child ?? getChild(),
45 46 47 48 49 50 51
            ),
          ),
        ),
      ),
    );
  }

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
  Widget getBuilderContextMenu({
    Alignment alignment = Alignment.center,
    Size screenSize = const Size(800.0, 600.0),
    CupertinoContextMenuBuilder? builder,
  }) {
    return CupertinoApp(
      home: CupertinoPageScaffold(
        child: MediaQuery(
          data: MediaQueryData(size: screenSize),
          child: Align(
            alignment: alignment,
            child: CupertinoContextMenu.builder(
              actions: <CupertinoContextMenuAction>[
                CupertinoContextMenuAction(
                  child: Text('CupertinoContextMenuAction $alignment'),
                ),
              ],
              builder: builder ?? getBuilder,
            ),
          ),
        ),
      ),
    );
  }

77
  // Finds the child widget that is rendered inside of _DecoyChild.
78
  Finder findDecoyChild(Widget child) {
79
    return find.descendant(
80
      of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DecoyChild'),
81 82 83 84 85
      matching: find.byWidget(child),
    );
  }

  // Finds the child widget rendered inside of _ContextMenuRouteStatic.
86
  Finder findStatic() {
87 88 89 90 91 92
    return find.descendant(
      of: find.byType(CupertinoApp),
      matching: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_ContextMenuRouteStatic'),
    );
  }

93
  Finder findStaticChild(Widget child) {
94
    return find.descendant(
95
      of: findStatic(),
96 97 98 99
      matching: find.byWidget(child),
    );
  }

100
  Finder findStaticChildDecoration(WidgetTester tester) {
101
    return find.descendant(
102
      of: findStatic(),
103 104 105 106
      matching: find.byType(DecoratedBox),
    );
  }

107 108 109 110 111 112 113 114 115 116 117 118 119 120
  Finder findFittedBox() {
    return find.descendant(
      of: findStatic(),
      matching: find.byType(FittedBox),
    );
  }

  Finder findStaticDefaultPreview() {
    return find.descendant(
      of: findFittedBox(),
      matching: find.byType(ClipRRect),
    );
  }

121 122 123
  group('CupertinoContextMenu before and during opening', () {
    testWidgets('An unopened CupertinoContextMenu renders child in the same place as without', (WidgetTester tester) async {
      // Measure the child in the scene with no CupertinoContextMenu.
124
      final Widget child = getChild();
125 126 127 128 129 130 131 132 133 134 135 136
      await tester.pumpWidget(
        CupertinoApp(
          home: CupertinoPageScaffold(
            child: Center(
              child: child,
            ),
          ),
        ),
      );
      final Rect childRect = tester.getRect(find.byWidget(child));

      // When wrapped in a CupertinoContextMenu, the child is rendered in the same Rect.
137
      await tester.pumpWidget(getContextMenu(child: child));
138 139 140 141 142
      expect(find.byWidget(child), findsOneWidget);
      expect(tester.getRect(find.byWidget(child)), childRect);
    });

    testWidgets('Can open CupertinoContextMenu by tap and hold', (WidgetTester tester) async {
143 144
      final Widget child = getChild();
      await tester.pumpWidget(getContextMenu(child: child));
145 146
      expect(find.byWidget(child), findsOneWidget);
      final Rect childRect = tester.getRect(find.byWidget(child));
147
      expect(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DecoyChild'), findsNothing);
148 149 150 151 152 153

      // Start a press on the child.
      final TestGesture gesture = await tester.startGesture(childRect.center);
      await tester.pump();

      // The _DecoyChild is showing directly on top of the child.
154 155
      expect(findDecoyChild(child), findsOneWidget);
      Rect decoyChildRect = tester.getRect(findDecoyChild(child));
156 157
      expect(childRect, equals(decoyChildRect));

158
      expect(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DecoyChild'), findsOneWidget);
159 160

      // After a small delay, the _DecoyChild has begun to animate.
161
      await tester.pump(const Duration(milliseconds: 400));
162
      decoyChildRect = tester.getRect(findDecoyChild(child));
163 164 165
      expect(childRect, isNot(equals(decoyChildRect)));

      // Eventually the decoy fully scales by _kOpenSize.
166
      await tester.pump(const Duration(milliseconds: 800));
167
      decoyChildRect = tester.getRect(findDecoyChild(child));
168
      expect(childRect, isNot(equals(decoyChildRect)));
169
      expect(decoyChildRect.width, childRect.width * kOpenScale);
170 171 172 173 174

      // Then the CupertinoContextMenu opens.
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();
175
      expect(findStatic(), findsOneWidget);
176 177 178
    });

    testWidgets('CupertinoContextMenu is in the correct position when within a nested navigator', (WidgetTester tester) async {
179
      final Widget child = getChild();
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
      await tester.pumpWidget(CupertinoApp(
        home: CupertinoPageScaffold(
          child: MediaQuery(
            data: const MediaQueryData(size: Size(800, 600)),
            child: Align(
              alignment: Alignment.bottomRight,
              child: SizedBox(
                width: 700,
                height: 500,
                child: Navigator(
                  onGenerateRoute: (RouteSettings settings) {
                    return CupertinoPageRoute<void>(
                      builder: (BuildContext context) => Align(
                        child: CupertinoContextMenu(
                          actions: const <CupertinoContextMenuAction>[
                            CupertinoContextMenuAction(
                              child: Text('CupertinoContextMenuAction'),
                            ),
                          ],
                          child: child
                        ),
                      )
                    );
                  }
                )
              )
            )
          )
        )
      ));
      expect(find.byWidget(child), findsOneWidget);
      final Rect childRect = tester.getRect(find.byWidget(child));
212
      expect(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DecoyChild'), findsNothing);
213 214 215 216 217 218

      // Start a press on the child.
      final TestGesture gesture = await tester.startGesture(childRect.center);
      await tester.pump();

      // The _DecoyChild is showing directly on top of the child.
219 220
      expect(findDecoyChild(child), findsOneWidget);
      Rect decoyChildRect = tester.getRect(findDecoyChild(child));
221 222
      expect(childRect, equals(decoyChildRect));

223
      expect(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DecoyChild'), findsOneWidget);
224

225
      // After a small delay, the _DecoyChild has begun to animate.
226
      await tester.pump(const Duration(milliseconds: 400));
227
      decoyChildRect = tester.getRect(findDecoyChild(child));
228 229 230
      expect(childRect, isNot(equals(decoyChildRect)));

      // Eventually the decoy fully scales by _kOpenSize.
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
      await tester.pump(const Duration(milliseconds: 800));
      decoyChildRect = tester.getRect(findDecoyChild(child));
      expect(childRect, isNot(equals(decoyChildRect)));
      expect(decoyChildRect.width, childRect.width * kOpenScale);

      // Then the CupertinoContextMenu opens.
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();
      expect(findStatic(), findsOneWidget);
    });

    testWidgets('CupertinoContextMenu with a basic builder opens and closes the same as when providing a child', (WidgetTester tester) async {
      final Widget child = getChild();
      await tester.pumpWidget(getBuilderContextMenu(builder: (BuildContext context, Animation<double> animation) {
        return child;
      }));
      expect(find.byWidget(child), findsOneWidget);
      final Rect childRect = tester.getRect(find.byWidget(child));
      expect(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DecoyChild'), findsNothing);

      // Start a press on the child.
      final TestGesture gesture = await tester.startGesture(childRect.center);
      await tester.pump();

      // The _DecoyChild is showing directly on top of the child.
      expect(findDecoyChild(child), findsOneWidget);
      Rect decoyChildRect = tester.getRect(findDecoyChild(child));
      expect(childRect, equals(decoyChildRect));

      expect(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DecoyChild'), findsOneWidget);

      // After a small delay, the _DecoyChild has begun to animate.
      await tester.pump(const Duration(milliseconds: 400));
      decoyChildRect = tester.getRect(findDecoyChild(child));
      expect(childRect, isNot(equals(decoyChildRect)));

      // Eventually the decoy fully scales by _kOpenSize.
      await tester.pump(const Duration(milliseconds: 800));
270
      decoyChildRect = tester.getRect(findDecoyChild(child));
271
      expect(childRect, isNot(equals(decoyChildRect)));
272
      expect(decoyChildRect.width, childRect.width * kOpenScale);
273 274 275 276 277

      // Then the CupertinoContextMenu opens.
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();
278
      expect(findStatic(), findsOneWidget);
279
    });
280

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
    testWidgets('CupertinoContextMenu with a builder can change the animation', (WidgetTester tester) async {
      await tester.pumpWidget(getBuilderContextMenu(builder: (BuildContext context, Animation<double> animation) {
        return Container(
          width: 300.0,
          height: 100.0,
          decoration: BoxDecoration(
            color: CupertinoColors.activeOrange,
            borderRadius: BorderRadius.circular(25.0 * animation.value)
          ),
        );
      }));

      final Widget child = find.descendant(of: find.byType(TickerMode), matching: find.byType(Container)).evaluate().single.widget;
      final Rect childRect = tester.getRect(find.byWidget(child));
      expect(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DecoyChild'), findsNothing);

      // Start a press on the child.
      await tester.startGesture(childRect.center);
      await tester.pump();

      Finder findBuilderDecoyChild() {
        return find.descendant(
          of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DecoyChild'),
          matching: find.byType(Container),
        );
      }

      final Container decoyContainer = tester.firstElement(findBuilderDecoyChild()).widget as Container;
      final BoxDecoration? decoyDecoration = decoyContainer.decoration as BoxDecoration?;
      expect(decoyDecoration?.borderRadius, equals(BorderRadius.circular(0)));

      expect(findBuilderDecoyChild(), findsOneWidget);

      // After a small delay, the _DecoyChild has begun to animate with a different border radius.
      await tester.pump(const Duration(milliseconds: 500));
      final Container decoyLaterContainer = tester.firstElement(findBuilderDecoyChild()).widget as Container;
      final BoxDecoration? decoyLaterDecoration = decoyLaterContainer.decoration as BoxDecoration?;
      expect(decoyLaterDecoration?.borderRadius, isNot(equals(BorderRadius.circular(0))));
    });

321
    testWidgets('Hovering over Cupertino context menu updates cursor to clickable on Web', (WidgetTester tester) async {
322
      final Widget child  = getChild();
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
      await tester.pumpWidget(CupertinoApp(
        home: CupertinoPageScaffold(
          child: Center(
            child: CupertinoContextMenu(
              actions: const <CupertinoContextMenuAction>[
                CupertinoContextMenuAction(
                  child: Text('CupertinoContextMenuAction One'),
                ),
              ],
              child: child,
            ),
          ),
        ),
      ));

      final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1);
      await gesture.addPointer(location: const Offset(10, 10));
      await tester.pumpAndSettle();
      expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic);

      final Offset contextMenu = tester.getCenter(find.byWidget(child));
      await gesture.moveTo(contextMenu);
      await tester.pumpAndSettle();
      expect(
        RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
        kIsWeb ? SystemMouseCursors.click : SystemMouseCursors.basic,
      );
    });
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376

    testWidgets('CupertinoContextMenu is in the correct position when within a Transform.scale', (WidgetTester tester) async {
      final Widget child = getChild();
      await tester.pumpWidget(CupertinoApp(
        home: CupertinoPageScaffold(
          child: MediaQuery(
            data: const MediaQueryData(size: Size(800, 600)),
            child: Transform.scale(
              scale: 0.5,
              child: Align(
                //alignment: Alignment.bottomRight,
                child: CupertinoContextMenu(
                  actions: const <CupertinoContextMenuAction>[
                    CupertinoContextMenuAction(
                      child: Text('CupertinoContextMenuAction'),
                    ),
                  ],
                  child: child
                ),
              )
            )
          )
        )
      ));
      expect(find.byWidget(child), findsOneWidget);
      final Rect childRect = tester.getRect(find.byWidget(child));
377
      expect(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DecoyChild'), findsNothing);
378 379 380 381 382 383 384 385 386 387

      // Start a press on the child.
      final TestGesture gesture = await tester.startGesture(childRect.center);
      await tester.pump();

      // The _DecoyChild is showing directly on top of the child.
      expect(findDecoyChild(child), findsOneWidget);
      Rect decoyChildRect = tester.getRect(findDecoyChild(child));
      expect(childRect, equals(decoyChildRect));

388
      expect(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DecoyChild'), findsOneWidget);
389 390

      // After a small delay, the _DecoyChild has begun to animate.
391
      await tester.pump(const Duration(milliseconds: 400));
392 393 394 395
      decoyChildRect = tester.getRect(findDecoyChild(child));
      expect(childRect, isNot(equals(decoyChildRect)));

      // Eventually the decoy fully scales by _kOpenSize.
396
      await tester.pump(const Duration(milliseconds: 800));
397 398 399 400 401 402 403 404 405 406
      decoyChildRect = tester.getRect(findDecoyChild(child));
      expect(childRect, isNot(equals(decoyChildRect)));
      expect(decoyChildRect.width, childRect.width * kOpenScale);

      // Then the CupertinoContextMenu opens.
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();
      expect(findStatic(), findsOneWidget);
    });
407 408 409
  });

  group('CupertinoContextMenu when open', () {
410
    testWidgets('Last action does not have border', (WidgetTester tester) async {
411
      final Widget child  = getChild();
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
      await tester.pumpWidget(CupertinoApp(
        home: CupertinoPageScaffold(
          child: Center(
            child: CupertinoContextMenu(
              actions: const <CupertinoContextMenuAction>[
                CupertinoContextMenuAction(
                  child: Text('CupertinoContextMenuAction One'),
                ),
              ],
              child: child,
            ),
          ),
        ),
      ));

      // Open the CupertinoContextMenu
      final TestGesture firstGesture = await tester.startGesture(tester.getCenter(find.byWidget(child)));
      await tester.pumpAndSettle();
      await firstGesture.up();
      await tester.pumpAndSettle();
432
      expect(findStatic(), findsOneWidget);
433

434
      expect(findStaticChildDecoration(tester), findsNWidgets(1));
435 436 437 438

      // Close the CupertinoContextMenu.
      await tester.tapAt(const Offset(1.0, 1.0));
      await tester.pumpAndSettle();
439
      expect(findStatic(), findsNothing);
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463

      await tester.pumpWidget(CupertinoApp(
        home: CupertinoPageScaffold(
          child: Center(
            child: CupertinoContextMenu(
              actions: const <CupertinoContextMenuAction>[
                CupertinoContextMenuAction(
                  child: Text('CupertinoContextMenuAction One'),
                ),
                CupertinoContextMenuAction(
                  child: Text('CupertinoContextMenuAction Two'),
                ),
              ],
              child: child,
            ),
          ),
        ),
      ));

      // Open the CupertinoContextMenu
      final TestGesture secondGesture = await tester.startGesture(tester.getCenter(find.byWidget(child)));
      await tester.pumpAndSettle();
      await secondGesture.up();
      await tester.pumpAndSettle();
464
      expect(findStatic(), findsOneWidget);
465

466
      expect(findStaticChildDecoration(tester), findsNWidgets(3));
467 468
    });

469
    testWidgets('Can close CupertinoContextMenu by background tap', (WidgetTester tester) async {
470 471
      final Widget child = getChild();
      await tester.pumpWidget(getContextMenu(child: child));
472 473 474 475 476 477 478

      // Open the CupertinoContextMenu
      final Rect childRect = tester.getRect(find.byWidget(child));
      final TestGesture gesture = await tester.startGesture(childRect.center);
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();
479
      expect(findStatic(), findsOneWidget);
480 481 482 483

      // Tap and ensure that the CupertinoContextMenu is closed.
      await tester.tapAt(const Offset(1.0, 1.0));
      await tester.pumpAndSettle();
484
      expect(findStatic(), findsNothing);
485
    });
486 487

    testWidgets('Can close CupertinoContextMenu by dragging down', (WidgetTester tester) async {
488 489
      final Widget child = getChild();
      await tester.pumpWidget(getContextMenu(child: child));
490 491 492 493 494 495 496

      // Open the CupertinoContextMenu
      final Rect childRect = tester.getRect(find.byWidget(child));
      final TestGesture gesture = await tester.startGesture(childRect.center);
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();
497
      expect(findStatic(), findsOneWidget);
498 499

      // Drag down not far enough and it bounces back and doesn't close.
500 501
      expect(findStaticChild(child), findsOneWidget);
      Offset staticChildCenter = tester.getCenter(findStaticChild(child));
502 503 504 505 506 507 508 509
      TestGesture swipeGesture = await tester.startGesture(staticChildCenter);
      await swipeGesture.moveBy(
        const Offset(0.0, 100.0),
        timeStamp: const Duration(milliseconds: 100),
      );
      await tester.pump();
      await swipeGesture.up();
      await tester.pump();
510
      expect(tester.getCenter(findStaticChild(child)).dy, greaterThan(staticChildCenter.dy));
511
      await tester.pumpAndSettle();
512 513
      expect(tester.getCenter(findStaticChild(child)), equals(staticChildCenter));
      expect(findStatic(), findsOneWidget);
514 515

      // Drag down far enough and it does close.
516 517
      expect(findStaticChild(child), findsOneWidget);
      staticChildCenter = tester.getCenter(findStaticChild(child));
518 519 520 521 522 523 524 525
      swipeGesture = await tester.startGesture(staticChildCenter);
      await swipeGesture.moveBy(
        const Offset(0.0, 200.0),
        timeStamp: const Duration(milliseconds: 100),
      );
      await tester.pump();
      await swipeGesture.up();
      await tester.pumpAndSettle();
526
      expect(findStatic(), findsNothing);
527
    });
528 529

    testWidgets('Can close CupertinoContextMenu by flinging down', (WidgetTester tester) async {
530 531
      final Widget child = getChild();
      await tester.pumpWidget(getContextMenu(child: child));
532 533 534 535 536 537 538

      // Open the CupertinoContextMenu
      final Rect childRect = tester.getRect(find.byWidget(child));
      final TestGesture gesture = await tester.startGesture(childRect.center);
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();
539
      expect(findStatic(), findsOneWidget);
540 541

      // Fling up and nothing happens.
542 543
      expect(findStaticChild(child), findsOneWidget);
      await tester.fling(findStaticChild(child), const Offset(0.0, -100.0), 1000.0);
544
      await tester.pumpAndSettle();
545
      expect(findStaticChild(child), findsOneWidget);
546 547

      // Fling down to close the menu.
548 549
      expect(findStaticChild(child), findsOneWidget);
      await tester.fling(findStaticChild(child), const Offset(0.0, 100.0), 1000.0);
550
      await tester.pumpAndSettle();
551
      expect(findStatic(), findsNothing);
552
    });
553

554
    testWidgets("Backdrop is added using ModalRoute's filter parameter", (WidgetTester tester) async {
555 556
      final Widget child = getChild();
      await tester.pumpWidget(getContextMenu(child: child));
557 558 559 560 561 562 563 564
      expect(find.byType(BackdropFilter), findsNothing);

      // Open the CupertinoContextMenu
      final Rect childRect = tester.getRect(find.byWidget(child));
      final TestGesture gesture = await tester.startGesture(childRect.center);
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();
565
      expect(findStatic(), findsOneWidget);
566
      expect(find.byType(BackdropFilter), findsOneWidget);
567
    });
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585

    testWidgets('Preview widget should have the correct border radius', (WidgetTester tester) async {
      final Widget child = getChild();
      await tester.pumpWidget(getContextMenu(child: child));

      // Open the CupertinoContextMenu.
      final Rect childRect = tester.getRect(find.byWidget(child));
      final TestGesture gesture = await tester.startGesture(childRect.center);
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();
      expect(findStatic(), findsOneWidget);

      // Check border radius.
      expect(findStaticDefaultPreview(), findsOneWidget);
      final ClipRRect previewWidget = tester.firstWidget(findStaticDefaultPreview()) as ClipRRect;
      expect(previewWidget.borderRadius, equals(BorderRadius.circular(12.0)));
    });
586 587
  });

588
  group("Open layout differs depending on child's position on screen", () {
589 590 591 592 593
    testWidgets('Portrait', (WidgetTester tester) async {
      const Size portraitScreenSize = Size(600.0, 800.0);
      await binding.setSurfaceSize(portraitScreenSize);

      // Pump a CupertinoContextMenu in the center of the screen and open it.
594 595
      final Widget child = getChild();
      await tester.pumpWidget(getContextMenu(
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
        screenSize: portraitScreenSize,
        child: child,
      ));
      expect(find.byType(CupertinoContextMenuAction), findsNothing);
      Rect childRect = tester.getRect(find.byWidget(child));
      TestGesture gesture = await tester.startGesture(childRect.center);
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();

      // The position of the action is in the center of the screen.
      expect(find.byType(CupertinoContextMenuAction), findsOneWidget);
      final Offset center = tester.getTopLeft(find.byType(CupertinoContextMenuAction));

      // Close the CupertinoContextMenu.
      await tester.tapAt(const Offset(1.0, 1.0));
      await tester.pumpAndSettle();
613
      expect(findStatic(), findsNothing);
614 615

      // Pump a CupertinoContextMenu on the left of the screen and open it.
616
      await tester.pumpWidget(getContextMenu(
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
        alignment: Alignment.centerLeft,
        screenSize: portraitScreenSize,
        child: child,
      ));
      expect(find.byType(CupertinoContextMenuAction), findsNothing);
      await tester.pumpAndSettle();
      childRect = tester.getRect(find.byWidget(child));
      gesture = await tester.startGesture(childRect.center);
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();

      // The position of the action is on the left of the screen.
      expect(find.byType(CupertinoContextMenuAction), findsOneWidget);
      final Offset left = tester.getTopLeft(find.byType(CupertinoContextMenuAction));
      expect(left.dx, lessThan(center.dx));

      // Close the CupertinoContextMenu.
      await tester.tapAt(const Offset(1.0, 1.0));
      await tester.pumpAndSettle();
637
      expect(findStatic(), findsNothing);
638 639

      // Pump a CupertinoContextMenu on the right of the screen and open it.
640
      await tester.pumpWidget(getContextMenu(
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
        alignment: Alignment.centerRight,
        screenSize: portraitScreenSize,
        child: child,
      ));
      expect(find.byType(CupertinoContextMenuAction), findsNothing);
      childRect = tester.getRect(find.byWidget(child));
      gesture = await tester.startGesture(childRect.center);
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();

      // The position of the action is on the right of the screen.
      expect(find.byType(CupertinoContextMenuAction), findsOneWidget);
      final Offset right = tester.getTopLeft(find.byType(CupertinoContextMenuAction));
      expect(right.dx, greaterThan(center.dx));

      // Set the screen back to its normal size.
      await binding.setSurfaceSize(const Size(800.0, 600.0));
659
    });
660 661 662

    testWidgets('Landscape', (WidgetTester tester) async {
      // Pump a CupertinoContextMenu in the center of the screen and open it.
663 664
      final Widget child = getChild();
      await tester.pumpWidget(getContextMenu(
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
        child: child,
      ));
      expect(find.byType(CupertinoContextMenuAction), findsNothing);
      Rect childRect = tester.getRect(find.byWidget(child));
      TestGesture gesture = await tester.startGesture(childRect.center);
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();

      // Landscape doesn't support a centered action list, so the action is on
      // the left side of the screen.
      expect(find.byType(CupertinoContextMenuAction), findsOneWidget);
      final Offset center = tester.getTopLeft(find.byType(CupertinoContextMenuAction));

      // Close the CupertinoContextMenu.
      await tester.tapAt(const Offset(1.0, 1.0));
      await tester.pumpAndSettle();
682
      expect(findStatic(), findsNothing);
683 684

      // Pump a CupertinoContextMenu on the left of the screen and open it.
685
      await tester.pumpWidget(getContextMenu(
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
        alignment: Alignment.centerLeft,
        child: child,
      ));
      expect(find.byType(CupertinoContextMenuAction), findsNothing);
      childRect = tester.getRect(find.byWidget(child));
      gesture = await tester.startGesture(childRect.center);
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();

      // The position of the action is on the right of the screen, which is the
      // same as for center aligned children in landscape.
      expect(find.byType(CupertinoContextMenuAction), findsOneWidget);
      final Offset left = tester.getTopLeft(find.byType(CupertinoContextMenuAction));
      expect(left.dx, equals(center.dx));

      // Close the CupertinoContextMenu.
      await tester.tapAt(const Offset(1.0, 1.0));
      await tester.pumpAndSettle();
705
      expect(findStatic(), findsNothing);
706 707

      // Pump a CupertinoContextMenu on the right of the screen and open it.
708
      await tester.pumpWidget(getContextMenu(
709 710 711 712 713 714 715 716 717 718 719 720 721 722
        alignment: Alignment.centerRight,
        child: child,
      ));
      expect(find.byType(CupertinoContextMenuAction), findsNothing);
      childRect = tester.getRect(find.byWidget(child));
      gesture = await tester.startGesture(childRect.center);
      await tester.pumpAndSettle();
      await gesture.up();
      await tester.pumpAndSettle();

      // The position of the action is on the left of the screen.
      expect(find.byType(CupertinoContextMenuAction), findsOneWidget);
      final Offset right = tester.getTopLeft(find.byType(CupertinoContextMenuAction));
      expect(right.dx, lessThan(left.dx));
723
    });
724 725
  });
}