mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
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
This commit is contained in:
parent
f545f47d8f
commit
20299a2c17
@ -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');
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user