// Copyright 2018 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/gestures.dart' show PointerDeviceKind; import 'package:flutter/widgets.dart'; void main() { int tapCount; int singleTapUpCount; int singleTapCancelCount; int singleLongTapStartCount; int doubleTapDownCount; int forcePressStartCount; int forcePressEndCount; int dragStartCount; int dragUpdateCount; int dragEndCount; const Offset forcePressOffset = Offset(400.0, 50.0); void _handleTapDown(TapDownDetails details) { tapCount++; } void _handleSingleTapUp(TapUpDetails details) { singleTapUpCount++; } void _handleSingleTapCancel() { singleTapCancelCount++; } void _handleSingleLongTapStart(LongPressStartDetails details) { singleLongTapStartCount++; } void _handleDoubleTapDown(TapDownDetails details) { doubleTapDownCount++; } void _handleForcePressStart(ForcePressDetails details) { forcePressStartCount++; } void _handleForcePressEnd(ForcePressDetails details) { forcePressEndCount++; } void _handleDragSelectionStart(DragStartDetails details) { dragStartCount++; } void _handleDragSelectionUpdate(DragStartDetails _, DragUpdateDetails details) { dragUpdateCount++; } void _handleDragSelectionEnd(DragEndDetails details) { dragEndCount++; } setUp(() { tapCount = 0; singleTapUpCount = 0; singleTapCancelCount = 0; singleLongTapStartCount = 0; doubleTapDownCount = 0; forcePressStartCount = 0; forcePressEndCount = 0; dragStartCount = 0; dragUpdateCount = 0; dragEndCount = 0; }); Future pumpGestureDetector(WidgetTester tester) async { await tester.pumpWidget( TextSelectionGestureDetector( behavior: HitTestBehavior.opaque, onTapDown: _handleTapDown, onSingleTapUp: _handleSingleTapUp, onSingleTapCancel: _handleSingleTapCancel, onSingleLongTapStart: _handleSingleLongTapStart, onDoubleTapDown: _handleDoubleTapDown, onForcePressStart: _handleForcePressStart, onForcePressEnd: _handleForcePressEnd, onDragSelectionStart: _handleDragSelectionStart, onDragSelectionUpdate: _handleDragSelectionUpdate, onDragSelectionEnd: _handleDragSelectionEnd, child: Container(), ), ); } testWidgets('a series of taps all call onTaps', (WidgetTester tester) async { await pumpGestureDetector(tester); await tester.tapAt(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 150)); await tester.tapAt(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 150)); await tester.tapAt(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 150)); await tester.tapAt(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 150)); await tester.tapAt(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 150)); await tester.tapAt(const Offset(200, 200)); expect(tapCount, 6); }); testWidgets('in a series of rapid taps, onTapDown and onDoubleTapDown alternate', (WidgetTester tester) async { await pumpGestureDetector(tester); await tester.tapAt(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 50)); expect(singleTapUpCount, 1); await tester.tapAt(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 50)); expect(singleTapUpCount, 1); expect(doubleTapDownCount, 1); await tester.tapAt(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 50)); expect(singleTapUpCount, 2); expect(doubleTapDownCount, 1); await tester.tapAt(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 50)); expect(singleTapUpCount, 2); expect(doubleTapDownCount, 2); await tester.tapAt(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 50)); expect(singleTapUpCount, 3); expect(doubleTapDownCount, 2); await tester.tapAt(const Offset(200, 200)); expect(singleTapUpCount, 3); expect(doubleTapDownCount, 3); expect(tapCount, 6); }); testWidgets('quick tap-tap-hold is a double tap down', (WidgetTester tester) async { await pumpGestureDetector(tester); await tester.tapAt(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 50)); expect(singleTapUpCount, 1); final TestGesture gesture = await tester.startGesture(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 200)); expect(singleTapUpCount, 1); // Every down is counted. expect(tapCount, 2); // No cancels because the second tap of the double tap is a second successful // single tap behind the scene. expect(singleTapCancelCount, 0); expect(doubleTapDownCount, 1); // The double tap down hold supersedes the single tap down. expect(singleLongTapStartCount, 0); await gesture.up(); // Nothing else happens on up. expect(singleTapUpCount, 1); expect(tapCount, 2); expect(singleTapCancelCount, 0); expect(doubleTapDownCount, 1); expect(singleLongTapStartCount, 0); }); testWidgets('a very quick swipe is ignored', (WidgetTester tester) async { await pumpGestureDetector(tester); final TestGesture gesture = await tester.startGesture(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 20)); await gesture.moveBy(const Offset(100, 100)); await tester.pump(); expect(singleTapUpCount, 0); expect(tapCount, 0); expect(singleTapCancelCount, 0); expect(doubleTapDownCount, 0); expect(singleLongTapStartCount, 0); await gesture.up(); // Nothing else happens on up. expect(singleTapUpCount, 0); expect(tapCount, 0); expect(singleTapCancelCount, 0); expect(doubleTapDownCount, 0); expect(singleLongTapStartCount, 0); }); testWidgets('a slower swipe has a tap down and a canceled tap', (WidgetTester tester) async { await pumpGestureDetector(tester); final TestGesture gesture = await tester.startGesture(const Offset(200, 200)); await tester.pump(const Duration(milliseconds: 120)); await gesture.moveBy(const Offset(100, 100)); await tester.pump(); expect(singleTapUpCount, 0); expect(tapCount, 1); expect(singleTapCancelCount, 1); expect(doubleTapDownCount, 0); expect(singleLongTapStartCount, 0); }); testWidgets('a force press intiates a force press', (WidgetTester tester) async { await pumpGestureDetector(tester); const int pointerValue = 1; final TestGesture gesture = await tester.createGesture(); await gesture.downWithCustomEvent( forcePressOffset, const PointerDownEvent( pointer: pointerValue, position: forcePressOffset, pressure: 0.0, pressureMax: 6.0, pressureMin: 0.0, ), ); await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); await gesture.up(); await tester.pumpAndSettle(); await gesture.downWithCustomEvent( forcePressOffset, const PointerDownEvent( pointer: pointerValue, position: forcePressOffset, pressure: 0.0, pressureMax: 6.0, pressureMin: 0.0, ), ); await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); await gesture.up(); await tester.pump(const Duration(milliseconds: 20)); await gesture.downWithCustomEvent( forcePressOffset, const PointerDownEvent( pointer: pointerValue, position: forcePressOffset, pressure: 0.0, pressureMax: 6.0, pressureMin: 0.0, ), ); await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); await gesture.up(); await tester.pump(const Duration(milliseconds: 20)); await gesture.downWithCustomEvent( forcePressOffset, const PointerDownEvent( pointer: pointerValue, position: forcePressOffset, pressure: 0.0, pressureMax: 6.0, pressureMin: 0.0, ), ); await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); await gesture.up(); expect(forcePressStartCount, 4); }); testWidgets('a tap and then force press intiates a force press and not a double tap', (WidgetTester tester) async { await pumpGestureDetector(tester); const int pointerValue = 1; final TestGesture gesture = await tester.createGesture(); await gesture.downWithCustomEvent( forcePressOffset, const PointerDownEvent( pointer: pointerValue, position: forcePressOffset, pressure: 0.0, pressureMax: 6.0, pressureMin: 0.0, ), ); // Initiate a quick tap. await gesture.updateWithCustomEvent( const PointerMoveEvent( pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.0, pressureMin: 0, pressureMax: 1, ) ); await tester.pump(const Duration(milliseconds: 50)); await gesture.up(); // Initiate a force tap. await gesture.downWithCustomEvent( forcePressOffset, const PointerDownEvent( pointer: pointerValue, position: forcePressOffset, pressure: 0.0, pressureMax: 6.0, pressureMin: 0.0, ), ); await gesture.updateWithCustomEvent(const PointerMoveEvent( pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1, )); expect(forcePressStartCount, 1); await tester.pump(const Duration(milliseconds: 50)); await gesture.up(); await tester.pumpAndSettle(); expect(forcePressEndCount, 1); expect(doubleTapDownCount, 0); }); testWidgets('a long press from a touch device is recognized as a long single tap', (WidgetTester tester) async { await pumpGestureDetector(tester); const int pointerValue = 1; final TestGesture gesture = await tester.startGesture(const Offset(200.0, 200.0), pointer: pointerValue, kind: PointerDeviceKind.touch); await tester.pump(const Duration(seconds: 2)); await gesture.up(); await tester.pumpAndSettle(); expect(tapCount, 1); expect(singleTapUpCount, 0); expect(singleLongTapStartCount, 1); }); testWidgets('a long press from a mouse is just a tap', (WidgetTester tester) async { await pumpGestureDetector(tester); const int pointerValue = 1; final TestGesture gesture = await tester.startGesture(const Offset(200.0, 200.0), pointer: pointerValue, kind: PointerDeviceKind.mouse); await tester.pump(const Duration(seconds: 2)); await gesture.up(); await tester.pumpAndSettle(); expect(tapCount, 1); expect(singleTapUpCount, 1); expect(singleLongTapStartCount, 0); await gesture.removePointer(); }); testWidgets('a touch drag is not recognized for text selection', (WidgetTester tester) async { await pumpGestureDetector(tester); const int pointerValue = 1; final TestGesture gesture = await tester.startGesture(const Offset(200.0, 200.0), pointer: pointerValue, kind: PointerDeviceKind.touch); await tester.pump(); await gesture.moveBy(const Offset(210.0, 200.0)); await tester.pump(); await gesture.up(); await tester.pumpAndSettle(); expect(tapCount, 0); expect(singleTapUpCount, 0); expect(dragStartCount, 0); expect(dragUpdateCount, 0); expect(dragEndCount, 0); await gesture.removePointer(); }); testWidgets('a mouse drag is recognized for text selection', (WidgetTester tester) async { await pumpGestureDetector(tester); const int pointerValue = 1; final TestGesture gesture = await tester.startGesture(const Offset(200.0, 200.0), pointer: pointerValue, kind: PointerDeviceKind.mouse); await tester.pump(); await gesture.moveBy(const Offset(210.0, 200.0)); await tester.pump(); await gesture.up(); await tester.pumpAndSettle(); expect(tapCount, 0); expect(singleTapUpCount, 0); expect(dragStartCount, 1); expect(dragUpdateCount, 1); expect(dragEndCount, 1); await gesture.removePointer(); }); testWidgets('a slow mouse drag is still recognized for text selection', (WidgetTester tester) async { await pumpGestureDetector(tester); const int pointerValue = 1; final TestGesture gesture = await tester.startGesture(const Offset(200.0, 200.0), pointer: pointerValue, kind: PointerDeviceKind.mouse); await tester.pump(const Duration(seconds: 2)); await gesture.moveBy(const Offset(210.0, 200.0)); await tester.pump(); await gesture.up(); await tester.pumpAndSettle(); expect(dragStartCount, 1); expect(dragUpdateCount, 1); expect(dragEndCount, 1); await gesture.removePointer(); }); }