diff --git a/packages/flutter_driver/lib/src/extension/extension.dart b/packages/flutter_driver/lib/src/extension/extension.dart index 324decc9d4..d0219174b3 100644 --- a/packages/flutter_driver/lib/src/extension/extension.dart +++ b/packages/flutter_driver/lib/src/extension/extension.dart @@ -32,17 +32,18 @@ const String _extensionMethodName = 'driver'; typedef DataHandler = Future Function(String? message); class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding, GestureBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding { - _DriverBinding(this._handler, this._silenceErrors, this.finders, this.commands); + _DriverBinding(this._handler, this._silenceErrors, this._enableTextEntryEmulation, this.finders, this.commands); final DataHandler? _handler; final bool _silenceErrors; + final bool _enableTextEntryEmulation; final List? finders; final List? commands; @override void initServiceExtensions() { super.initServiceExtensions(); - final FlutterDriverExtension extension = FlutterDriverExtension(_handler, _silenceErrors, finders: finders ?? const [], commands: commands ?? const []); + final FlutterDriverExtension extension = FlutterDriverExtension(_handler, _silenceErrors, _enableTextEntryEmulation, finders: finders ?? const [], commands: commands ?? const []); registerServiceExtension( name: _extensionMethodName, callback: extension.call, @@ -78,6 +79,12 @@ class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding, /// will still be returned in the `response` field of the result JSON along /// with an `isError` boolean. /// +/// The `enableTextEntryEmulation` parameter controls whether the application interacts +/// with the system's text entry methods or a mocked out version used by Flutter Driver. +/// If it is set to false, [FlutterDriver.enterText] will fail, +/// but testing the application with real keyboard input is possible. +/// This value may be updated during a test by calling [FlutterDriver.setTextEntryEmulation]. +/// /// The `finders` and `commands` parameters are optional and used to add custom /// finders or commands, as in the following example. /// @@ -217,9 +224,9 @@ class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding, /// } /// ``` /// -void enableFlutterDriverExtension({ DataHandler? handler, bool silenceErrors = false, List? finders, List? commands}) { +void enableFlutterDriverExtension({ DataHandler? handler, bool silenceErrors = false, bool enableTextEntryEmulation = true, List? finders, List? commands}) { assert(WidgetsBinding.instance == null); - _DriverBinding(handler, silenceErrors, finders ?? [], commands ?? []); + _DriverBinding(handler, silenceErrors, enableTextEntryEmulation, finders ?? [], commands ?? []); assert(WidgetsBinding.instance is _DriverBinding); } @@ -315,11 +322,14 @@ class FlutterDriverExtension with DeserializeFinderFactory, CreateFinderFactory, /// Creates an object to manage a Flutter Driver connection. FlutterDriverExtension( this._requestDataHandler, - this._silenceErrors, { + this._silenceErrors, + this._enableTextEntryEmulation, { List finders = const [], List commands = const [], }) : assert(finders != null) { - registerTextInput(); + if (_enableTextEntryEmulation) { + registerTextInput(); + } for(final FinderExtension finder in finders) { _finderExtensions[finder.finderType] = finder; @@ -336,6 +346,8 @@ class FlutterDriverExtension with DeserializeFinderFactory, CreateFinderFactory, final bool _silenceErrors; + final bool _enableTextEntryEmulation; + void _log(String message) { driverLog('FlutterDriverExtension', message); } diff --git a/packages/flutter_driver/test/src/real_tests/extension_test.dart b/packages/flutter_driver/test/src/real_tests/extension_test.dart index 899bc16128..8b75fb0894 100644 --- a/packages/flutter_driver/test/src/real_tests/extension_test.dart +++ b/packages/flutter_driver/test/src/real_tests/extension_test.dart @@ -44,7 +44,7 @@ void main() { setUp(() { result = null; - driverExtension = FlutterDriverExtension((String message) async { log.add(message); return (messageId += 1).toString(); }, false); + driverExtension = FlutterDriverExtension((String message) async { log.add(message); return (messageId += 1).toString(); }, false, true); }); testWidgets('returns immediately when transient callback queue is empty', (WidgetTester tester) async { @@ -105,7 +105,7 @@ void main() { setUp(() { result = null; - driverExtension = FlutterDriverExtension((String message) async { log.add(message); return (messageId += 1).toString(); }, false); + driverExtension = FlutterDriverExtension((String message) async { log.add(message); return (messageId += 1).toString(); }, false, true); }); testWidgets('waiting for NoTransientCallbacks returns immediately when transient callback queue is empty', (WidgetTester tester) async { @@ -471,7 +471,7 @@ void main() { group('getSemanticsId', () { FlutterDriverExtension driverExtension; setUp(() { - driverExtension = FlutterDriverExtension((String arg) async => '', true); + driverExtension = FlutterDriverExtension((String arg) async => '', true, true); }); testWidgets('works when semantics are enabled', (WidgetTester tester) async { @@ -520,7 +520,7 @@ void main() { }); testWidgets('getOffset', (WidgetTester tester) async { - final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true); + final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true, true); Future getOffset(OffsetType offset) async { final Map arguments = GetOffset(ByValueKey(1), offset).serialize(); @@ -552,7 +552,7 @@ void main() { testWidgets('getText', (WidgetTester tester) async { await silenceDriverLogger(() async { - final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true); + final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true, true); Future getTextInternal(SerializableFinder search) async { final Map arguments = GetText(search, timeout: const Duration(seconds: 1)).serialize(); @@ -622,7 +622,7 @@ void main() { testWidgets('descendant finder', (WidgetTester tester) async { await silenceDriverLogger(() async { - final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true); + final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true, true); Future getDescendantText({ String of, bool matchRoot = false}) async { final Map arguments = GetText(Descendant( @@ -667,7 +667,7 @@ void main() { testWidgets('descendant finder firstMatchOnly', (WidgetTester tester) async { await silenceDriverLogger(() async { - final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true); + final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true, true); Future getDescendantText() async { final Map arguments = GetText(Descendant( @@ -701,7 +701,7 @@ void main() { testWidgets('ancestor finder', (WidgetTester tester) async { await silenceDriverLogger(() async { - final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true); + final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true, true); Future getAncestorTopLeft({ String of, String matching, bool matchRoot = false}) async { final Map arguments = GetOffset(Ancestor( @@ -771,7 +771,7 @@ void main() { testWidgets('ancestor finder firstMatchOnly', (WidgetTester tester) async { await silenceDriverLogger(() async { - final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true); + final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true, true); Future getAncestorTopLeft() async { final Map arguments = GetOffset(Ancestor( @@ -819,7 +819,7 @@ void main() { }); testWidgets('GetDiagnosticsTree', (WidgetTester tester) async { - final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true); + final FlutterDriverExtension driverExtension = FlutterDriverExtension((String arg) async => '', true, true); Future> getDiagnosticsTree(DiagnosticsType type, SerializableFinder finder, { int depth = 0, bool properties = true }) async { final Map arguments = GetDiagnosticsTree(finder, type, subtreeDepth: depth, includeProperties: properties).serialize(); @@ -884,6 +884,45 @@ void main() { expect(children.single['children'], isEmpty); }); + group('enableTextEntryEmulation', () { + FlutterDriverExtension driverExtension; + + Future> enterText() async { + final Map arguments = const EnterText('foo').serialize(); + final Map result = await driverExtension.call(arguments); + return result; + } + + const Widget testWidget = MaterialApp( + home: Material( + child: Center( + child: TextField( + key: ValueKey('foo'), + autofocus: true, + ), + ), + ), + ); + + testWidgets('enableTextEntryEmulation false', (WidgetTester tester) async { + driverExtension = FlutterDriverExtension((String arg) async => '', true, false); + + await tester.pumpWidget(testWidget); + + final Map enterTextResult = await enterText(); + expect(enterTextResult['isError'], isTrue); + }); + + testWidgets('enableTextEntryEmulation true', (WidgetTester tester) async { + driverExtension = FlutterDriverExtension((String arg) async => '', true, true); + + await tester.pumpWidget(testWidget); + + final Map enterTextResult = await enterText(); + expect(enterTextResult['isError'], isFalse); + }); + }); + group('extension finders', () { final Widget debugTree = Directionality( textDirection: TextDirection.ltr, @@ -907,6 +946,7 @@ void main() { final FlutterDriverExtension driverExtension = FlutterDriverExtension( (String arg) async => '', true, + true, finders: [], ); @@ -927,6 +967,7 @@ void main() { final FlutterDriverExtension driverExtension = FlutterDriverExtension( (String arg) async => '', true, + true, finders: [ StubFinderExtension(), ], @@ -948,6 +989,7 @@ void main() { final FlutterDriverExtension driverExtension = FlutterDriverExtension( (String arg) async => '', true, + true, finders: [ StubFinderExtension(), ], @@ -969,6 +1011,7 @@ void main() { final FlutterDriverExtension driverExtension = FlutterDriverExtension( (String arg) async => '', true, + true, finders: [ StubFinderExtension(), ], @@ -1013,6 +1056,7 @@ void main() { final FlutterDriverExtension driverExtension = FlutterDriverExtension( (String arg) async => '', true, + true, commands: [], ); @@ -1033,6 +1077,7 @@ void main() { final FlutterDriverExtension driverExtension = FlutterDriverExtension( (String arg) async => '', true, + true, commands: [ StubNestedCommandExtension(), ], @@ -1058,6 +1103,7 @@ void main() { final FlutterDriverExtension driverExtension = FlutterDriverExtension( (String arg) async => '', true, + true, commands: [ StubProberCommandExtension(), ], @@ -1085,7 +1131,7 @@ void main() { Map result; setUp(() { - driverExtension = FlutterDriverExtension((String arg) async => '', true); + driverExtension = FlutterDriverExtension((String arg) async => '', true, true); result = null; });