Unverified Commit 20299a2c authored by Tong Mu's avatar Tong Mu Committed by GitHub

Add buttons customization to WidgetController and related testing classes (#31095)

* Add buttons to WidgetController and TestPointer

* Add more buttons

* Let TestPointer handle default device

* Use getter only buttons
parent f545f47d
......@@ -15,6 +15,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/gestures.dart' show kPrimaryButton;
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_gallery/gallery/demos.dart';
......@@ -132,8 +133,8 @@ class _LiveWidgetController extends LiveWidgetController {
}
@override
Future<void> tap(Finder finder, { int pointer }) async {
await super.tap(await _waitForElement(finder), pointer: pointer);
Future<void> tap(Finder finder, { int pointer, int buttons = kPrimaryButton }) async {
await super.tap(await _waitForElement(finder), pointer: pointer, buttons: buttons);
}
Future<void> scrollIntoView(Finder finder, {double alignment}) async {
......
......@@ -252,14 +252,14 @@ abstract class WidgetController {
///
/// If the center of the widget is not exposed, this might send events to
/// another object.
Future<void> tap(Finder finder, {int pointer}) {
return tapAt(getCenter(finder), pointer: pointer);
Future<void> tap(Finder finder, {int pointer, int buttons = kPrimaryButton}) {
return tapAt(getCenter(finder), pointer: pointer, buttons: buttons);
}
/// Dispatch a pointer down / pointer up sequence at the given location.
Future<void> tapAt(Offset location, {int pointer}) {
Future<void> tapAt(Offset location, {int pointer, int buttons = kPrimaryButton}) {
return TestAsyncUtils.guard<void>(() async {
final TestGesture gesture = await startGesture(location, pointer: pointer);
final TestGesture gesture = await startGesture(location, pointer: pointer, buttons: buttons);
await gesture.up();
});
}
......@@ -269,9 +269,9 @@ abstract class WidgetController {
///
/// If the center of the widget is not exposed, this might send events to
/// another object.
Future<TestGesture> press(Finder finder, {int pointer}) {
Future<TestGesture> press(Finder finder, {int pointer, int buttons = kPrimaryButton}) {
return TestAsyncUtils.guard<TestGesture>(() {
return startGesture(getCenter(finder), pointer: pointer);
return startGesture(getCenter(finder), pointer: pointer, buttons: buttons);
});
}
......@@ -281,15 +281,15 @@ abstract class WidgetController {
///
/// If the center of the widget is not exposed, this might send events to
/// another object.
Future<void> longPress(Finder finder, {int pointer}) {
return longPressAt(getCenter(finder), pointer: pointer);
Future<void> longPress(Finder finder, {int pointer, int buttons = kPrimaryButton}) {
return longPressAt(getCenter(finder), pointer: pointer, buttons: buttons);
}
/// Dispatch a pointer down / pointer up sequence at the given location with
/// a delay of [kLongPressTimeout] + [kPressTimeout] between the two events.
Future<void> longPressAt(Offset location, {int pointer}) {
Future<void> longPressAt(Offset location, {int pointer, int buttons = kPrimaryButton}) {
return TestAsyncUtils.guard<void>(() async {
final TestGesture gesture = await startGesture(location, pointer: pointer);
final TestGesture gesture = await startGesture(location, pointer: pointer, buttons: buttons);
await pump(kLongPressTimeout + kPressTimeout);
await gesture.up();
});
......@@ -320,6 +320,7 @@ abstract class WidgetController {
Offset offset,
double speed, {
int pointer,
int buttons = kPrimaryButton,
Duration frameInterval = const Duration(milliseconds: 16),
Offset initialOffset = Offset.zero,
Duration initialOffsetDelay = const Duration(seconds: 1),
......@@ -329,6 +330,7 @@ abstract class WidgetController {
offset,
speed,
pointer: pointer,
buttons: buttons,
frameInterval: frameInterval,
initialOffset: initialOffset,
initialOffsetDelay: initialOffsetDelay,
......@@ -365,6 +367,7 @@ abstract class WidgetController {
Offset offset,
double speed, {
int pointer,
int buttons = kPrimaryButton,
Duration frameInterval = const Duration(milliseconds: 16),
Offset initialOffset = Offset.zero,
Duration initialOffsetDelay = const Duration(seconds: 1),
......@@ -372,7 +375,7 @@ abstract class WidgetController {
assert(offset.distance > 0.0);
assert(speed > 0.0); // speed is pixels/second
return TestAsyncUtils.guard<void>(() async {
final TestPointer testPointer = TestPointer(pointer ?? _getNextPointer());
final TestPointer testPointer = TestPointer(pointer ?? _getNextPointer(), PointerDeviceKind.touch, null, buttons);
final HitTestResult result = hitTestOnBinding(startLocation);
const int kMoveCount = 50; // Needs to be >= kHistorySize, see _LeastSquaresVelocityTrackerStrategy
final double timeStampDelta = 1000.0 * offset.distance / (kMoveCount * speed);
......@@ -434,9 +437,23 @@ abstract class WidgetController {
/// 'touchSlopY' variables should be set to 0. However, generally, these values
/// should be left to their default values.
/// {@end template}
Future<void> drag(Finder finder, Offset offset, { int pointer, double touchSlopX = kDragSlopDefault, double touchSlopY = kDragSlopDefault }) {
Future<void> drag(
Finder finder,
Offset offset, {
int pointer,
int buttons = kPrimaryButton,
double touchSlopX = kDragSlopDefault,
double touchSlopY = kDragSlopDefault,
}) {
assert(kDragSlopDefault > kTouchSlop);
return dragFrom(getCenter(finder), offset, pointer: pointer, touchSlopX: touchSlopX, touchSlopY: touchSlopY);
return dragFrom(
getCenter(finder),
offset,
pointer: pointer,
buttons: buttons,
touchSlopX: touchSlopX,
touchSlopY: touchSlopY,
);
}
/// Attempts a drag gesture consisting of a pointer down, a move by
......@@ -447,10 +464,17 @@ abstract class WidgetController {
/// instead.
///
/// {@macro flutter.flutter_test.drag}
Future<void> dragFrom(Offset startLocation, Offset offset, { int pointer, double touchSlopX = kDragSlopDefault, double touchSlopY = kDragSlopDefault }) {
Future<void> dragFrom(
Offset startLocation,
Offset offset, {
int pointer,
int buttons = kPrimaryButton,
double touchSlopX = kDragSlopDefault,
double touchSlopY = kDragSlopDefault,
}) {
assert(kDragSlopDefault > kTouchSlop);
return TestAsyncUtils.guard<void>(() async {
final TestGesture gesture = await startGesture(startLocation, pointer: pointer);
final TestGesture gesture = await startGesture(startLocation, pointer: pointer, buttons: buttons);
assert(gesture != null);
final double xSign = offset.dx.sign;
......@@ -538,14 +562,18 @@ abstract class WidgetController {
///
/// You can use [startGesture] instead if your gesture begins with a down
/// event.
Future<TestGesture> createGesture({int pointer, PointerDeviceKind kind = PointerDeviceKind.touch}) async {
final TestGesture gesture = TestGesture(
Future<TestGesture> createGesture({
int pointer,
PointerDeviceKind kind = PointerDeviceKind.touch,
int buttons = kPrimaryButton,
}) async {
return TestGesture(
hitTester: hitTestOnBinding,
dispatcher: sendEventToBinding,
kind: kind,
pointer: pointer ?? _getNextPointer(),
buttons: buttons,
);
return gesture;
}
/// Creates a gesture with an initial down gesture at a particular point, and
......@@ -558,8 +586,13 @@ abstract class WidgetController {
Offset downLocation, {
int pointer,
PointerDeviceKind kind = PointerDeviceKind.touch,
int buttons = kPrimaryButton,
}) async {
final TestGesture result = await createGesture(pointer: pointer, kind: kind);
final TestGesture result = await createGesture(
pointer: pointer,
kind: kind,
buttons: buttons,
);
await result.down(downLocation);
return result;
}
......
......@@ -26,8 +26,12 @@ class TestPointer {
this.pointer = 1,
this.kind = PointerDeviceKind.touch,
this._device,
]) : assert(kind != null),
assert(pointer != null) {
int buttons = kPrimaryButton,
])
: assert(kind != null),
assert(pointer != null),
assert(buttons != null),
_buttons = buttons {
switch (kind) {
case PointerDeviceKind.mouse:
_device ??= 1;
......@@ -57,6 +61,11 @@ class TestPointer {
/// [PointerDeviceKind.touch].
final PointerDeviceKind kind;
/// The kind of buttons to simulate on Down and Move events. Defaults to
/// [kPrimaryButton].
int get buttons => _buttons;
int _buttons;
/// Whether the pointer simulated by this object is currently down.
///
/// A pointer is released (goes up) by calling [up] or [cancel].
......@@ -73,8 +82,14 @@ class TestPointer {
/// If a custom event is created outside of this class, this function is used
/// to set the [isDown].
bool setDownInfo(PointerEvent event, Offset newLocation) {
bool setDownInfo(
PointerEvent event,
Offset newLocation, {
int buttons,
}) {
_location = newLocation;
if (buttons != null)
_buttons = buttons;
switch (event.runtimeType) {
case PointerDownEvent:
assert(!isDown);
......@@ -95,16 +110,26 @@ class TestPointer {
///
/// By default, the time stamp on the event is [Duration.zero]. You can give a
/// specific time stamp by passing the `timeStamp` argument.
PointerDownEvent down(Offset newLocation, { Duration timeStamp = Duration.zero }) {
///
/// By default, the set of buttons in the last down or move event is used.
/// You can give a specific set of buttons by passing the `buttons` argument.
PointerDownEvent down(
Offset newLocation, {
Duration timeStamp = Duration.zero,
int buttons,
}) {
assert(!isDown);
_isDown = true;
_location = newLocation;
if (buttons != null)
_buttons = buttons;
return PointerDownEvent(
timeStamp: timeStamp,
kind: kind,
device: _device,
pointer: pointer,
position: location,
buttons: _buttons,
);
}
......@@ -115,7 +140,14 @@ class TestPointer {
///
/// [isDown] must be true when this is called, since move events can only
/// be generated when the pointer is down.
PointerMoveEvent move(Offset newLocation, { Duration timeStamp = Duration.zero }) {
///
/// By default, the set of buttons in the last down or move event is used.
/// You can give a specific set of buttons by passing the `buttons` argument.
PointerMoveEvent move(
Offset newLocation, {
Duration timeStamp = Duration.zero,
int buttons,
}) {
assert(
isDown,
'Move events can only be generated when the pointer is down. To '
......@@ -123,6 +155,8 @@ class TestPointer {
'up, use hover() instead.');
final Offset delta = newLocation - location;
_location = newLocation;
if (buttons != null)
_buttons = buttons;
return PointerMoveEvent(
timeStamp: timeStamp,
kind: kind,
......@@ -130,6 +164,7 @@ class TestPointer {
pointer: pointer,
position: newLocation,
delta: delta,
buttons: _buttons,
);
}
......@@ -291,13 +326,16 @@ class TestGesture {
@required HitTester hitTester,
int pointer = 1,
PointerDeviceKind kind = PointerDeviceKind.touch,
int device,
int buttons = kPrimaryButton,
}) : assert(dispatcher != null),
assert(hitTester != null),
assert(pointer != null),
assert(kind != null),
assert(buttons != null),
_dispatcher = dispatcher,
_hitTester = hitTester,
_pointer = TestPointer(pointer, kind),
_pointer = TestPointer(pointer, kind, device, buttons),
_result = null;
/// Dispatch a pointer down event at the given `downLocation`, caching the
......
......@@ -264,4 +264,154 @@ void main() {
}
},
);
testWidgets(
'WidgetTester.tap must respect buttons',
(WidgetTester tester) async {
final List<String> logs = <String>[];
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Listener(
onPointerDown: (PointerDownEvent event) => logs.add('down ${event.buttons}'),
onPointerMove: (PointerMoveEvent event) => logs.add('move ${event.buttons}'),
onPointerUp: (PointerUpEvent event) => logs.add('up ${event.buttons}'),
child: const Text('test'),
),
),
);
await tester.tap(find.text('test'), buttons: kSecondaryMouseButton);
const String b = '$kSecondaryMouseButton';
for(int i = 0; i < logs.length; i++) {
if (i == 0)
expect(logs[i], 'down $b');
else if (i != logs.length - 1)
expect(logs[i], 'move $b');
else
expect(logs[i], 'up 0');
}
},
);
testWidgets(
'WidgetTester.press must respect buttons',
(WidgetTester tester) async {
final List<String> logs = <String>[];
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Listener(
onPointerDown: (PointerDownEvent event) => logs.add('down ${event.buttons}'),
onPointerMove: (PointerMoveEvent event) => logs.add('move ${event.buttons}'),
onPointerUp: (PointerUpEvent event) => logs.add('up ${event.buttons}'),
child: const Text('test'),
),
),
);
await tester.press(find.text('test'), buttons: kSecondaryMouseButton);
const String b = '$kSecondaryMouseButton';
expect(logs, equals(<String>['down $b']));
},
);
testWidgets(
'WidgetTester.longPress must respect buttons',
(WidgetTester tester) async {
final List<String> logs = <String>[];
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Listener(
onPointerDown: (PointerDownEvent event) => logs.add('down ${event.buttons}'),
onPointerMove: (PointerMoveEvent event) => logs.add('move ${event.buttons}'),
onPointerUp: (PointerUpEvent event) => logs.add('up ${event.buttons}'),
child: const Text('test'),
),
),
);
await tester.longPress(find.text('test'), buttons: kSecondaryMouseButton);
await tester.pumpAndSettle();
const String b = '$kSecondaryMouseButton';
for(int i = 0; i < logs.length; i++) {
if (i == 0)
expect(logs[i], 'down $b');
else if (i != logs.length - 1)
expect(logs[i], 'move $b');
else
expect(logs[i], 'up 0');
}
},
);
testWidgets(
'WidgetTester.drag must respect buttons',
(WidgetTester tester) async {
final List<String> logs = <String>[];
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Listener(
onPointerDown: (PointerDownEvent event) => logs.add('down ${event.buttons}'),
onPointerMove: (PointerMoveEvent event) => logs.add('move ${event.buttons}'),
onPointerUp: (PointerUpEvent event) => logs.add('up ${event.buttons}'),
child: const Text('test'),
),
),
);
await tester.drag(find.text('test'), const Offset(-150.0, 200.0), buttons: kSecondaryMouseButton);
const String b = '$kSecondaryMouseButton';
for(int i = 0; i < logs.length; i++) {
if (i == 0)
expect(logs[i], 'down $b');
else if (i != logs.length - 1)
expect(logs[i], 'move $b');
else
expect(logs[i], 'up 0');
}
},
);
testWidgets(
'WidgetTester.fling must respect buttons',
(WidgetTester tester) async {
final List<String> logs = <String>[];
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Listener(
onPointerDown: (PointerDownEvent event) => logs.add('down ${event.buttons}'),
onPointerMove: (PointerMoveEvent event) => logs.add('move ${event.buttons}'),
onPointerUp: (PointerUpEvent event) => logs.add('up ${event.buttons}'),
child: const Text('test'),
),
),
);
await tester.fling(find.text('test'), const Offset(-10.0, 0.0), 1000.0, buttons: kSecondaryMouseButton);
await tester.pumpAndSettle();
const String b = '$kSecondaryMouseButton';
for(int i = 0; i < logs.length; i++) {
if (i == 0)
expect(logs[i], 'down $b');
else if (i != logs.length - 1)
expect(logs[i], 'move $b');
else
expect(logs[i], 'up 0');
}
},
);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment