gesture_detector_test.dart 9.33 KB
Newer Older
Hixie's avatar
Hixie committed
1 2 3 4
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

Adam Barth's avatar
Adam Barth committed
5
import 'package:flutter_test/flutter_test.dart';
6
import 'package:flutter/widgets.dart';
7
import 'package:flutter/rendering.dart';
8
import 'package:flutter/gestures.dart';
9 10

void main() {
11
  testWidgets('Uncontested scrolls start immediately', (WidgetTester tester) async {
12 13 14 15
    bool didStartDrag = false;
    double updatedDragDelta;
    bool didEndDrag = false;

16
    final Widget widget = new GestureDetector(
17
      onVerticalDragStart: (DragStartDetails details) {
18 19
        didStartDrag = true;
      },
20 21
      onVerticalDragUpdate: (DragUpdateDetails details) {
        updatedDragDelta = details.primaryDelta;
22
      },
23
      onVerticalDragEnd: (DragEndDetails details) {
24 25 26
        didEndDrag = true;
      },
      child: new Container(
27
        color: const Color(0xFF00FF00),
28
      ),
29 30
    );

31
    await tester.pumpWidget(widget);
32 33 34 35
    expect(didStartDrag, isFalse);
    expect(updatedDragDelta, isNull);
    expect(didEndDrag, isFalse);

36
    const Offset firstLocation = Offset(10.0, 10.0);
37
    final TestGesture gesture = await tester.startGesture(firstLocation, pointer: 7);
38 39 40 41 42
    expect(didStartDrag, isTrue);
    didStartDrag = false;
    expect(updatedDragDelta, isNull);
    expect(didEndDrag, isFalse);

43
    const Offset secondLocation = Offset(10.0, 9.0);
44
    await gesture.moveTo(secondLocation);
45 46 47 48 49
    expect(didStartDrag, isFalse);
    expect(updatedDragDelta, -1.0);
    updatedDragDelta = null;
    expect(didEndDrag, isFalse);

50
    await gesture.up();
51 52 53 54 55
    expect(didStartDrag, isFalse);
    expect(updatedDragDelta, isNull);
    expect(didEndDrag, isTrue);
    didEndDrag = false;

56
    await tester.pumpWidget(new Container());
57
  });
58

59
  testWidgets('Match two scroll gestures in succession', (WidgetTester tester) async {
60 61 62
    int gestureCount = 0;
    double dragDistance = 0.0;

63 64
    const Offset downLocation = Offset(10.0, 10.0);
    const Offset upLocation = Offset(10.0, 50.0); // must be far enough to be more than kTouchSlop
65

66
    final Widget widget = new GestureDetector(
67 68
      onVerticalDragUpdate: (DragUpdateDetails details) { dragDistance += details.primaryDelta; },
      onVerticalDragEnd: (DragEndDetails details) { gestureCount += 1; },
69 70
      onHorizontalDragUpdate: (DragUpdateDetails details) { fail('gesture should not match'); },
      onHorizontalDragEnd: (DragEndDetails details) { fail('gesture should not match'); },
71
      child: new Container(
72
        color: const Color(0xFF00FF00),
73
      ),
74
    );
75
    await tester.pumpWidget(widget);
76

77 78 79
    TestGesture gesture = await tester.startGesture(downLocation, pointer: 7);
    await gesture.moveTo(upLocation);
    await gesture.up();
80

81 82 83
    gesture = await tester.startGesture(downLocation, pointer: 7);
    await gesture.moveTo(upLocation);
    await gesture.up();
84

85
    expect(gestureCount, 2);
86
    expect(dragDistance, 40.0 * 2.0); // delta between down and up, twice
Adam Barth's avatar
Adam Barth committed
87

88
    await tester.pumpWidget(new Container());
89
  });
90

91
  testWidgets('Pan doesn\'t crash', (WidgetTester tester) async {
92 93 94
    bool didStartPan = false;
    Offset panDelta;
    bool didEndPan = false;
95

96
    await tester.pumpWidget(
97
      new GestureDetector(
98
        onPanStart: (DragStartDetails details) {
99 100
          didStartPan = true;
        },
101 102
        onPanUpdate: (DragUpdateDetails details) {
          panDelta = details.delta;
103
        },
104
        onPanEnd: (DragEndDetails details) {
105 106 107
          didEndPan = true;
        },
        child: new Container(
108
          color: const Color(0xFF00FF00),
109 110
        ),
      ),
111
    );
112

113 114 115
    expect(didStartPan, isFalse);
    expect(panDelta, isNull);
    expect(didEndPan, isFalse);
116

117
    await tester.dragFrom(const Offset(10.0, 10.0), const Offset(20.0, 30.0));
118

119 120 121 122
    expect(didStartPan, isTrue);
    expect(panDelta.dx, 20.0);
    expect(panDelta.dy, 30.0);
    expect(didEndPan, isTrue);
123
  });
124

125
  testWidgets('Translucent', (WidgetTester tester) async {
126 127 128
    bool didReceivePointerDown;
    bool didTap;

129 130
    Future<Null> pumpWidgetTree(HitTestBehavior behavior) {
      return tester.pumpWidget(
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
        new Directionality(
          textDirection: TextDirection.ltr,
          child: new Stack(
            children: <Widget>[
              new Listener(
                onPointerDown: (_) {
                  didReceivePointerDown = true;
                },
                child: new Container(
                  width: 100.0,
                  height: 100.0,
                  color: const Color(0xFF00FF00),
                ),
              ),
              new Container(
146 147
                width: 100.0,
                height: 100.0,
148 149 150 151 152 153 154 155 156 157
                child: new GestureDetector(
                  onTap: () {
                    didTap = true;
                  },
                  behavior: behavior,
                ),
              ),
            ],
          ),
        ),
158 159 160 161 162
      );
    }

    didReceivePointerDown = false;
    didTap = false;
163
    await pumpWidgetTree(null);
164
    await tester.tapAt(const Offset(10.0, 10.0));
165 166 167 168 169
    expect(didReceivePointerDown, isTrue);
    expect(didTap, isTrue);

    didReceivePointerDown = false;
    didTap = false;
170
    await pumpWidgetTree(HitTestBehavior.deferToChild);
171
    await tester.tapAt(const Offset(10.0, 10.0));
172 173 174 175 176
    expect(didReceivePointerDown, isTrue);
    expect(didTap, isFalse);

    didReceivePointerDown = false;
    didTap = false;
177
    await pumpWidgetTree(HitTestBehavior.opaque);
178
    await tester.tapAt(const Offset(10.0, 10.0));
179 180 181 182 183
    expect(didReceivePointerDown, isFalse);
    expect(didTap, isTrue);

    didReceivePointerDown = false;
    didTap = false;
184
    await pumpWidgetTree(HitTestBehavior.translucent);
185
    await tester.tapAt(const Offset(10.0, 10.0));
186 187 188
    expect(didReceivePointerDown, isTrue);
    expect(didTap, isTrue);

189
  });
190 191 192 193 194 195 196 197 198

  testWidgets('Empty', (WidgetTester tester) async {
    bool didTap = false;
    await tester.pumpWidget(
      new Center(
        child: new GestureDetector(
          onTap: () {
            didTap = true;
          },
199 200
        ),
      ),
201 202
    );
    expect(didTap, isFalse);
203
    await tester.tapAt(const Offset(10.0, 10.0));
204 205 206 207 208 209 210 211 212 213 214 215
    expect(didTap, isTrue);
  });

  testWidgets('Only container', (WidgetTester tester) async {
    bool didTap = false;
    await tester.pumpWidget(
      new Center(
        child: new GestureDetector(
          onTap: () {
            didTap = true;
          },
          child: new Container(),
216 217
        ),
      ),
218 219
    );
    expect(didTap, isFalse);
220
    await tester.tapAt(const Offset(10.0, 10.0));
221 222
    expect(didTap, isFalse);
  });
223 224 225 226 227

  testWidgets('cache unchanged callbacks', (WidgetTester tester) async {
    final GestureTapCallback inputCallback = () {};

    await tester.pumpWidget(
228 229 230 231 232 233
      new Center(
        child: new GestureDetector(
          onTap: inputCallback,
          child: new Container(),
        ),
      ),
234 235 236 237 238 239
    );

    final RenderSemanticsGestureHandler renderObj1 = tester.renderObject(find.byType(GestureDetector));
    final GestureTapCallback actualCallback1 = renderObj1.onTap;

    await tester.pumpWidget(
240 241 242 243 244 245
      new Center(
        child: new GestureDetector(
          onTap: inputCallback,
          child: new Container(),
        ),
      ),
246 247 248 249 250 251 252 253
    );

    final RenderSemanticsGestureHandler renderObj2 = tester.renderObject(find.byType(GestureDetector));
    final GestureTapCallback actualCallback2 = renderObj2.onTap;

    expect(renderObj1, same(renderObj2));
    expect(actualCallback1, same(actualCallback2)); // Should be cached.
  });
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290

  testWidgets('Tap down occurs after kPressTimeout', (WidgetTester tester) async {
    int tapDown = 0;
    int tap = 0;
    int tapCancel = 0;
    int longPress = 0;

    await tester.pumpWidget(
      new Container(
        alignment: Alignment.topLeft,
        child: new Container(
          alignment: Alignment.center,
          height: 100.0,
          color: const Color(0xFF00FF00),
          child: new GestureDetector(
            onTapDown: (TapDownDetails details) {
              tapDown += 1;
            },
            onTap: () {
              tap += 1;
            },
            onTapCancel: () {
              tapCancel += 1;
            },
            onLongPress: () {
              longPress += 1;
            },
          ),
        ),
      ),
    );

    // Pointer is dragged from the center of the 800x100 gesture detector
    // to a point (400,300) below it. This always causes onTapCancel to be
    // called; onTap should never be called.
    Future<Null> dragOut(Duration timeout) async {
      final TestGesture gesture = await tester.startGesture(const Offset(400.0, 50.0));
Josh Soref's avatar
Josh Soref committed
291
      // If the timeout is less than kPressTimeout the recognizer will just trigger
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
      // the onTapCancel callback. If the timeout is greater than kLongPressTimeout
      // then onTapDown, onLongPress, and onCancel will be called.
      await tester.pump(timeout);
      await gesture.moveTo(const Offset(400.0, 300.0));
      await gesture.up();
    }

    await dragOut(kPressTimeout * 0.5); // generates tapCancel
    expect(tapDown, 0);
    expect(tapCancel, 1);
    expect(tap, 0);
    expect(longPress, 0);

    await dragOut(kPressTimeout); // generates tapDown, tapCancel
    expect(tapDown, 1);
    expect(tapCancel, 2);
    expect(tap, 0);
    expect(longPress, 0);

    await dragOut(kLongPressTimeout); // generates tapDown, longPress, tapCancel
    expect(tapDown, 2);
    expect(tapCancel, 3);
    expect(tap, 0);
    expect(longPress, 1);
  });
317
}