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

@Tags(<String>['reduced-test-set'])
6
library;
7 8 9 10 11 12 13 14 15 16 17

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  final Offset basicOffset = Offset(CupertinoMagnifier.kDefaultSize.width / 2,
       CupertinoMagnifier.kDefaultSize.height - CupertinoMagnifier.kMagnifierAboveFocalPoint);
  const Rect reasonableTextField = Rect.fromLTRB(0, 100, 200, 200);
  final MagnifierController magnifierController = MagnifierController();

18
  // Make sure that your gesture in magnifierInfo is within the line in magnifierInfo,
19 20 21 22
  // or else the magnifier status will stay hidden and this will not complete.
  Future<void> showCupertinoMagnifier(
    BuildContext context,
    WidgetTester tester,
23
    ValueNotifier<MagnifierInfo> magnifierInfo,
24 25 26 27 28
  ) async {
    final Future<void> magnifierShown = magnifierController.show(
        context: context,
        builder: (_) => CupertinoTextMagnifier(
              controller: magnifierController,
29
              magnifierInfo: magnifierInfo,
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
            ));

    WidgetsBinding.instance.scheduleFrame();
    await tester.pumpAndSettle();

    await magnifierShown;
  }

  tearDown(() async {
      magnifierController.removeFromOverlay();
  });

  group('CupertinoTextEditingMagnifier', () {
    group('position', () {
      Offset getMagnifierPosition(WidgetTester tester) {
        final AnimatedPositioned animatedPositioned =
            tester.firstWidget(find.byType(AnimatedPositioned));
        return Offset(
            animatedPositioned.left ?? 0, animatedPositioned.top ?? 0);
      }

      testWidgets('should be at gesture position if does not violate any positioning rules', (WidgetTester tester) async {
        final Key fakeTextFieldKey = UniqueKey();
        final Key outerKey = UniqueKey();

        await tester.pumpWidget(
56
          ColoredBox(
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
            key: outerKey,
            color: const Color.fromARGB(255, 0, 255, 179),
            child: MaterialApp(
              home: Center(
                child: Container(
                  key: fakeTextFieldKey,
                  width: 10,
                  height: 10,
                  color: Colors.red,
                  child: const Placeholder(),
                ),
              ),
            ),
          ),
        );
        final BuildContext context = tester.element(find.byType(Placeholder));

        // Magnifier should be positioned directly over the red square.
        final RenderBox tapPointRenderBox =
            tester.firstRenderObject(find.byKey(fakeTextFieldKey)) as RenderBox;
        final Rect fakeTextFieldRect =
            tapPointRenderBox.localToGlobal(Offset.zero) & tapPointRenderBox.size;

80 81 82
        final ValueNotifier<MagnifierInfo> magnifier =
            ValueNotifier<MagnifierInfo>(
          MagnifierInfo(
83
            currentLineBoundaries: fakeTextFieldRect,
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
            fieldBounds: fakeTextFieldRect,
            caretRect: fakeTextFieldRect,
            // The tap position is dragBelow units below the text field.
            globalGesturePosition: fakeTextFieldRect.center,
          ),
        );

        await showCupertinoMagnifier(context, tester, magnifier);

        // Should show two red squares; original, and one in the magnifier,
        // directly ontop of one another.
        await expectLater(
          find.byKey(outerKey),
          matchesGoldenFile('cupertino_magnifier.position.default.png'),
        );
      });

      testWidgets('should never horizontally be outside of Screen Padding', (WidgetTester tester) async {
        await tester.pumpWidget(
          const MaterialApp(
            color: Color.fromARGB(7, 0, 129, 90),
            home: Placeholder(),
          ),
        );

        final BuildContext context = tester.firstElement(find.byType(Placeholder));

        await showCupertinoMagnifier(
          context,
          tester,
114 115
          ValueNotifier<MagnifierInfo>(
            MagnifierInfo(
116
              currentLineBoundaries: reasonableTextField,
117 118 119 120
              fieldBounds: reasonableTextField,
              caretRect: reasonableTextField,
              // The tap position is far out of the right side of the app.
              globalGesturePosition:
121
                  Offset(MediaQuery.sizeOf(context).width + 100, 0),
122 123 124 125 126 127
            ),
          ),
        );

        // Should be less than the right edge, since we have padding.
        expect(getMagnifierPosition(tester).dx,
128
            lessThan(MediaQuery.sizeOf(context).width));
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
      });

      testWidgets('should have some vertical drag', (WidgetTester tester) async {
        final double dragPositionBelowTextField = reasonableTextField.center.dy + 30;

        await tester.pumpWidget(
          const MaterialApp(
            color: Color.fromARGB(7, 0, 129, 90),
            home: Placeholder(),
          ),
        );

        final BuildContext context =
            tester.firstElement(find.byType(Placeholder));

        await showCupertinoMagnifier(
          context,
          tester,
147 148
          ValueNotifier<MagnifierInfo>(
            MagnifierInfo(
149
              currentLineBoundaries: reasonableTextField,
150 151 152 153
              fieldBounds: reasonableTextField,
              caretRect: reasonableTextField,
              // The tap position is dragBelow units below the text field.
              globalGesturePosition: Offset(
154
                  MediaQuery.sizeOf(context).width / 2,
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
                  dragPositionBelowTextField),
            ),
          ),
        );

        // The magnifier Y should be greater than the text field, since we "dragged" it down.
        expect(getMagnifierPosition(tester).dy + basicOffset.dy,
            greaterThan(reasonableTextField.center.dy));
        expect(getMagnifierPosition(tester).dy + basicOffset.dy,
            lessThan(dragPositionBelowTextField));
      });
    });

    group('status', () {
      testWidgets('should hide if gesture is far below the text field', (WidgetTester tester) async {
        await tester.pumpWidget(
          const MaterialApp(
            color: Color.fromARGB(7, 0, 129, 90),
            home: Placeholder(),
          ),
        );

        final BuildContext context =
            tester.firstElement(find.byType(Placeholder));

180 181 182
        final ValueNotifier<MagnifierInfo> magnifierinfo =
            ValueNotifier<MagnifierInfo>(
          MagnifierInfo(
183
            currentLineBoundaries: reasonableTextField,
184 185 186 187
            fieldBounds: reasonableTextField,
            caretRect: reasonableTextField,
            // The tap position is dragBelow units below the text field.
            globalGesturePosition: Offset(
188
                MediaQuery.sizeOf(context).width / 2, reasonableTextField.top),
189 190 191 192 193 194 195
          ),
        );

        // Show the magnifier initally, so that we get it in a not hidden state.
        await showCupertinoMagnifier(context, tester, magnifierinfo);

        // Move the gesture to one that should hide it.
196
        magnifierinfo.value = MagnifierInfo(
197
            currentLineBoundaries: reasonableTextField,
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
            fieldBounds: reasonableTextField,
            caretRect: reasonableTextField,
            globalGesturePosition: magnifierinfo.value.globalGesturePosition + const Offset(0, 100),
        );
        await tester.pumpAndSettle();

        expect(magnifierController.shown, false);
        expect(magnifierController.overlayEntry, isNotNull);
      });

      testWidgets('should re-show if gesture moves back up',
          (WidgetTester tester) async {
        await tester.pumpWidget(
          const MaterialApp(
            color: Color.fromARGB(7, 0, 129, 90),
            home: Placeholder(),
          ),
        );

        final BuildContext context =
            tester.firstElement(find.byType(Placeholder));

220 221 222
        final ValueNotifier<MagnifierInfo> magnifierInfo =
            ValueNotifier<MagnifierInfo>(
          MagnifierInfo(
223
            currentLineBoundaries: reasonableTextField,
224 225 226
            fieldBounds: reasonableTextField,
            caretRect: reasonableTextField,
            // The tap position is dragBelow units below the text field.
227
            globalGesturePosition: Offset(MediaQuery.sizeOf(context).width / 2, reasonableTextField.top),
228 229 230 231 232 233 234
          ),
        );

        // Show the magnifier initally, so that we get it in a not hidden state.
        await showCupertinoMagnifier(context, tester, magnifierInfo);

        // Move the gesture to one that should hide it.
235
        magnifierInfo.value = MagnifierInfo(
236
            currentLineBoundaries: reasonableTextField,
237 238 239 240 241 242 243 244 245 246
            fieldBounds: reasonableTextField,
            caretRect: reasonableTextField,
            globalGesturePosition:
                magnifierInfo.value.globalGesturePosition + const Offset(0, 100));
        await tester.pumpAndSettle();

        expect(magnifierController.shown, false);
        expect(magnifierController.overlayEntry, isNotNull);

        // Return the gesture to one that shows it.
247
        magnifierInfo.value = MagnifierInfo(
248
            currentLineBoundaries: reasonableTextField,
249 250
            fieldBounds: reasonableTextField,
            caretRect: reasonableTextField,
251
            globalGesturePosition: Offset(MediaQuery.sizeOf(context).width / 2,
252 253 254 255 256 257 258 259 260
                reasonableTextField.top));
        await tester.pumpAndSettle();

        expect(magnifierController.shown, true);
        expect(magnifierController.overlayEntry, isNotNull);
      });
    });
  });
}