// 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. import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/gestures.dart'; void main() { testWidgets('Uncontested scrolls start immediately', (WidgetTester tester) async { bool didStartDrag = false; double updatedDragDelta; bool didEndDrag = false; final Widget widget = new GestureDetector( onVerticalDragStart: (DragStartDetails details) { didStartDrag = true; }, onVerticalDragUpdate: (DragUpdateDetails details) { updatedDragDelta = details.primaryDelta; }, onVerticalDragEnd: (DragEndDetails details) { didEndDrag = true; }, child: new Container( color: const Color(0xFF00FF00), ), ); await tester.pumpWidget(widget); expect(didStartDrag, isFalse); expect(updatedDragDelta, isNull); expect(didEndDrag, isFalse); const Offset firstLocation = const Offset(10.0, 10.0); final TestGesture gesture = await tester.startGesture(firstLocation, pointer: 7); expect(didStartDrag, isTrue); didStartDrag = false; expect(updatedDragDelta, isNull); expect(didEndDrag, isFalse); const Offset secondLocation = const Offset(10.0, 9.0); await gesture.moveTo(secondLocation); expect(didStartDrag, isFalse); expect(updatedDragDelta, -1.0); updatedDragDelta = null; expect(didEndDrag, isFalse); await gesture.up(); expect(didStartDrag, isFalse); expect(updatedDragDelta, isNull); expect(didEndDrag, isTrue); didEndDrag = false; await tester.pumpWidget(new Container()); }); testWidgets('Match two scroll gestures in succession', (WidgetTester tester) async { int gestureCount = 0; double dragDistance = 0.0; const Offset downLocation = const Offset(10.0, 10.0); const Offset upLocation = const Offset(10.0, 50.0); // must be far enough to be more than kTouchSlop final Widget widget = new GestureDetector( onVerticalDragUpdate: (DragUpdateDetails details) { dragDistance += details.primaryDelta; }, onVerticalDragEnd: (DragEndDetails details) { gestureCount += 1; }, onHorizontalDragUpdate: (DragUpdateDetails details) { fail('gesture should not match'); }, onHorizontalDragEnd: (DragEndDetails details) { fail('gesture should not match'); }, child: new Container( color: const Color(0xFF00FF00), ), ); await tester.pumpWidget(widget); TestGesture gesture = await tester.startGesture(downLocation, pointer: 7); await gesture.moveTo(upLocation); await gesture.up(); gesture = await tester.startGesture(downLocation, pointer: 7); await gesture.moveTo(upLocation); await gesture.up(); expect(gestureCount, 2); expect(dragDistance, 40.0 * 2.0); // delta between down and up, twice await tester.pumpWidget(new Container()); }); testWidgets('Pan doesn\'t crash', (WidgetTester tester) async { bool didStartPan = false; Offset panDelta; bool didEndPan = false; await tester.pumpWidget( new GestureDetector( onPanStart: (DragStartDetails details) { didStartPan = true; }, onPanUpdate: (DragUpdateDetails details) { panDelta = details.delta; }, onPanEnd: (DragEndDetails details) { didEndPan = true; }, child: new Container( color: const Color(0xFF00FF00), ), ), ); expect(didStartPan, isFalse); expect(panDelta, isNull); expect(didEndPan, isFalse); await tester.dragFrom(const Offset(10.0, 10.0), const Offset(20.0, 30.0)); expect(didStartPan, isTrue); expect(panDelta.dx, 20.0); expect(panDelta.dy, 30.0); expect(didEndPan, isTrue); }); testWidgets('Translucent', (WidgetTester tester) async { bool didReceivePointerDown; bool didTap; Future<Null> pumpWidgetTree(HitTestBehavior behavior) { return tester.pumpWidget( 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( width: 100.0, height: 100.0, child: new GestureDetector( onTap: () { didTap = true; }, behavior: behavior, ), ), ], ), ), ); } didReceivePointerDown = false; didTap = false; await pumpWidgetTree(null); await tester.tapAt(const Offset(10.0, 10.0)); expect(didReceivePointerDown, isTrue); expect(didTap, isTrue); didReceivePointerDown = false; didTap = false; await pumpWidgetTree(HitTestBehavior.deferToChild); await tester.tapAt(const Offset(10.0, 10.0)); expect(didReceivePointerDown, isTrue); expect(didTap, isFalse); didReceivePointerDown = false; didTap = false; await pumpWidgetTree(HitTestBehavior.opaque); await tester.tapAt(const Offset(10.0, 10.0)); expect(didReceivePointerDown, isFalse); expect(didTap, isTrue); didReceivePointerDown = false; didTap = false; await pumpWidgetTree(HitTestBehavior.translucent); await tester.tapAt(const Offset(10.0, 10.0)); expect(didReceivePointerDown, isTrue); expect(didTap, isTrue); }); testWidgets('Empty', (WidgetTester tester) async { bool didTap = false; await tester.pumpWidget( new Center( child: new GestureDetector( onTap: () { didTap = true; }, ), ), ); expect(didTap, isFalse); await tester.tapAt(const Offset(10.0, 10.0)); 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(), ), ), ); expect(didTap, isFalse); await tester.tapAt(const Offset(10.0, 10.0)); expect(didTap, isFalse); }); testWidgets('cache unchanged callbacks', (WidgetTester tester) async { final GestureTapCallback inputCallback = () {}; await tester.pumpWidget( new Center( child: new GestureDetector( onTap: inputCallback, child: new Container(), ), ), ); final RenderSemanticsGestureHandler renderObj1 = tester.renderObject(find.byType(GestureDetector)); final GestureTapCallback actualCallback1 = renderObj1.onTap; await tester.pumpWidget( new Center( child: new GestureDetector( onTap: inputCallback, child: new Container(), ), ), ); final RenderSemanticsGestureHandler renderObj2 = tester.renderObject(find.byType(GestureDetector)); final GestureTapCallback actualCallback2 = renderObj2.onTap; expect(renderObj1, same(renderObj2)); expect(actualCallback1, same(actualCallback2)); // Should be cached. }); 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)); // If the timeout is less than kPressTimeout the recognizer will just trigger // 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); }); }