[flutter_test] Add flag to send device pointer events to the framework (#108430)
This commit is contained in:
parent
dbadee003f
commit
4d73448b52
@ -494,9 +494,25 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
///
|
||||
/// When [handlePointerEvent] is called directly, [pointerEventSource]
|
||||
/// is [TestBindingEventSource.device].
|
||||
///
|
||||
/// This means that pointer events triggered by the [WidgetController] (e.g.
|
||||
/// via [WidgetController.tap]) will result in actual interactions with the
|
||||
/// UI, but other pointer events such as those from physical taps will be
|
||||
/// dropped. See also [shouldPropagateDevicePointerEvents] if this is
|
||||
/// undesired.
|
||||
TestBindingEventSource get pointerEventSource => _pointerEventSource;
|
||||
TestBindingEventSource _pointerEventSource = TestBindingEventSource.device;
|
||||
|
||||
/// Whether pointer events from [TestBindingEventSource.device] will be
|
||||
/// propagated to the framework, or dropped.
|
||||
///
|
||||
/// Setting this can be useful to interact with the app in some other way
|
||||
/// besides through the [WidgetController], such as with `adb shell input tap`
|
||||
/// on Android.
|
||||
///
|
||||
/// See also [pointerEventSource].
|
||||
bool shouldPropagateDevicePointerEvents = false;
|
||||
|
||||
/// Dispatch an event to the targets found by a hit test on its position,
|
||||
/// and remember its source as [pointerEventSource].
|
||||
///
|
||||
@ -836,6 +852,7 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
final bool autoUpdateGoldensBeforeTest = autoUpdateGoldenFiles && !isBrowser;
|
||||
final TestExceptionReporter reportTestExceptionBeforeTest = reportTestException;
|
||||
final ErrorWidgetBuilder errorWidgetBuilderBeforeTest = ErrorWidget.builder;
|
||||
final bool shouldPropagateDevicePointerEventsBeforeTest = shouldPropagateDevicePointerEvents;
|
||||
|
||||
// run the test
|
||||
await testBody();
|
||||
@ -854,6 +871,7 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
_verifyAutoUpdateGoldensUnset(autoUpdateGoldensBeforeTest && !isBrowser);
|
||||
_verifyReportTestExceptionUnset(reportTestExceptionBeforeTest);
|
||||
_verifyErrorWidgetBuilderUnset(errorWidgetBuilderBeforeTest);
|
||||
_verifyShouldPropagateDevicePointerEventsUnset(shouldPropagateDevicePointerEventsBeforeTest);
|
||||
_verifyInvariants();
|
||||
}
|
||||
|
||||
@ -943,6 +961,21 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
}());
|
||||
}
|
||||
|
||||
void _verifyShouldPropagateDevicePointerEventsUnset(bool valueBeforeTest) {
|
||||
assert(() {
|
||||
if (shouldPropagateDevicePointerEvents != valueBeforeTest) {
|
||||
FlutterError.reportError(FlutterErrorDetails(
|
||||
exception: FlutterError(
|
||||
'The value of shouldPropagateDevicePointerEvents was changed by the test.',
|
||||
),
|
||||
stack: StackTrace.current,
|
||||
library: 'Flutter test framework',
|
||||
));
|
||||
}
|
||||
return true;
|
||||
}());
|
||||
}
|
||||
|
||||
/// Called by the [testWidgets] function after a test is executed.
|
||||
void postTest() {
|
||||
assert(inTest);
|
||||
@ -1595,7 +1628,8 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
///
|
||||
/// Normally, device events are silently dropped. However, if this property is
|
||||
/// set to a non-null value, then the events will be routed to its
|
||||
/// [HitTestDispatcher.dispatchEvent] method instead.
|
||||
/// [HitTestDispatcher.dispatchEvent] method instead, unless
|
||||
/// [shouldPropagateDevicePointerEvents] is true.
|
||||
///
|
||||
/// Events dispatched by [TestGesture] are not affected by this.
|
||||
HitTestDispatcher? deviceEventDispatcher;
|
||||
@ -1630,6 +1664,10 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
super.handlePointerEvent(event);
|
||||
break;
|
||||
case TestBindingEventSource.device:
|
||||
if (shouldPropagateDevicePointerEvents) {
|
||||
super.handlePointerEvent(event);
|
||||
break;
|
||||
}
|
||||
if (deviceEventDispatcher != null) {
|
||||
// The pointer events received with this source has a global position
|
||||
// (see [handlePointerEventForSource]). Transform it to the local
|
||||
@ -1651,6 +1689,10 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
break;
|
||||
case TestBindingEventSource.device:
|
||||
assert(hitTestResult != null || event is PointerAddedEvent || event is PointerRemovedEvent);
|
||||
if (shouldPropagateDevicePointerEvents) {
|
||||
super.dispatchEvent(event, hitTestResult);
|
||||
break;
|
||||
}
|
||||
assert(deviceEventDispatcher != null);
|
||||
if (hitTestResult != null) {
|
||||
deviceEventDispatcher!.dispatchEvent(event, hitTestResult);
|
||||
|
@ -502,6 +502,10 @@ Future<void> expectLater(
|
||||
///
|
||||
/// For convenience, instances of this class (such as the one provided by
|
||||
/// `testWidgets`) can be used as the `vsync` for `AnimationController` objects.
|
||||
///
|
||||
/// When the binding is [LiveTestWidgetsFlutterBinding], events from
|
||||
/// [LiveTestWidgetsFlutterBinding.deviceEventDispatcher] will be handled in
|
||||
/// [dispatchEvent].
|
||||
class WidgetTester extends WidgetController implements HitTestDispatcher, TickerProvider {
|
||||
WidgetTester._(super.binding) {
|
||||
if (binding is LiveTestWidgetsFlutterBinding) {
|
||||
@ -817,6 +821,10 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
|
||||
}
|
||||
|
||||
/// Handler for device events caught by the binding in live test mode.
|
||||
///
|
||||
/// [PointerDownEvent]s received here will only print a diagnostic message
|
||||
/// showing possible [Finder]s that can be used to interact with the widget at
|
||||
/// the location of [result].
|
||||
@override
|
||||
void dispatchEvent(PointerEvent event, HitTestResult result) {
|
||||
if (event is PointerDownEvent) {
|
||||
|
@ -8,7 +8,7 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
// This file is for testings that require a `LiveTestWidgetsFlutterBinding`
|
||||
void main() {
|
||||
LiveTestWidgetsFlutterBinding();
|
||||
final LiveTestWidgetsFlutterBinding binding = LiveTestWidgetsFlutterBinding();
|
||||
testWidgets('Input PointerAddedEvent', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const MaterialApp(home: Text('Test')));
|
||||
await tester.pump();
|
||||
@ -99,4 +99,57 @@ void main() {
|
||||
|
||||
await expectLater(tester.binding.reassembleApplication(), completes);
|
||||
}, timeout: const Timeout(Duration(seconds: 30)));
|
||||
|
||||
testWidgets('shouldPropagateDevicePointerEvents can override events from ${TestBindingEventSource.device}', (WidgetTester tester) async {
|
||||
binding.shouldPropagateDevicePointerEvents = true;
|
||||
|
||||
await tester.pumpWidget(_ShowNumTaps());
|
||||
|
||||
final Offset position = tester.getCenter(find.text('0'));
|
||||
|
||||
// Simulates a real device tap.
|
||||
//
|
||||
// `handlePointerEventForSource defaults to sending events using
|
||||
// TestBindingEventSource.device. This will not be forwarded to the actual
|
||||
// gesture handlers, unless `shouldPropagateDevicePointerEvents` is true.
|
||||
binding.handlePointerEventForSource(
|
||||
PointerDownEvent(position: position),
|
||||
);
|
||||
binding.handlePointerEventForSource(
|
||||
PointerUpEvent(position: position),
|
||||
);
|
||||
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
|
||||
// Reset the value, otherwise the test will fail when it checks that this
|
||||
// has not been changed as an invariant.
|
||||
binding.shouldPropagateDevicePointerEvents = false;
|
||||
});
|
||||
}
|
||||
|
||||
/// A widget that shows the number of times it has been tapped.
|
||||
class _ShowNumTaps extends StatefulWidget {
|
||||
@override
|
||||
_ShowNumTapsState createState() => _ShowNumTapsState();
|
||||
}
|
||||
|
||||
class _ShowNumTapsState extends State<_ShowNumTaps> {
|
||||
int _counter = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_counter++;
|
||||
});
|
||||
},
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Text(_counter.toString()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user