Add finders for enableFlutterDriverExtension (#64308)
This commit is contained in:
parent
02d0163244
commit
db4dda2052
@ -33,11 +33,11 @@ class GetDiagnosticsTree extends CommandWithTarget {
|
|||||||
super(finder, timeout: timeout);
|
super(finder, timeout: timeout);
|
||||||
|
|
||||||
/// Deserializes this command from the value generated by [serialize].
|
/// Deserializes this command from the value generated by [serialize].
|
||||||
GetDiagnosticsTree.deserialize(Map<String, String> json)
|
GetDiagnosticsTree.deserialize(Map<String, String> json, DeserializeFinderFactory finderFactory)
|
||||||
: subtreeDepth = int.parse(json['subtreeDepth']),
|
: subtreeDepth = int.parse(json['subtreeDepth']),
|
||||||
includeProperties = json['includeProperties'] == 'true',
|
includeProperties = json['includeProperties'] == 'true',
|
||||||
diagnosticsType = _diagnosticsTypeIndex.lookupBySimpleName(json['diagnosticsType']),
|
diagnosticsType = _diagnosticsTypeIndex.lookupBySimpleName(json['diagnosticsType']),
|
||||||
super.deserialize(json);
|
super.deserialize(json, finderFactory);
|
||||||
|
|
||||||
/// How many levels of children to include in the JSON result.
|
/// How many levels of children to include in the JSON result.
|
||||||
///
|
///
|
||||||
|
@ -9,6 +9,25 @@ import 'package:meta/meta.dart';
|
|||||||
import 'error.dart';
|
import 'error.dart';
|
||||||
import 'message.dart';
|
import 'message.dart';
|
||||||
|
|
||||||
|
/// A factory for deserializing [Finder]s.
|
||||||
|
mixin DeserializeFinderFactory {
|
||||||
|
/// Deserializes the finder from JSON generated by [SerializableFinder.serialize].
|
||||||
|
SerializableFinder deserializeFinder(Map<String, String> json) {
|
||||||
|
final String finderType = json['finderType'];
|
||||||
|
switch (finderType) {
|
||||||
|
case 'ByType': return ByType.deserialize(json);
|
||||||
|
case 'ByValueKey': return ByValueKey.deserialize(json);
|
||||||
|
case 'ByTooltipMessage': return ByTooltipMessage.deserialize(json);
|
||||||
|
case 'BySemanticsLabel': return BySemanticsLabel.deserialize(json);
|
||||||
|
case 'ByText': return ByText.deserialize(json);
|
||||||
|
case 'PageBack': return const PageBack();
|
||||||
|
case 'Descendant': return Descendant.deserialize(json, this);
|
||||||
|
case 'Ancestor': return Ancestor.deserialize(json, this);
|
||||||
|
}
|
||||||
|
throw DriverError('Unsupported search specification type $finderType');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const List<Type> _supportedKeyValueTypes = <Type>[String, int];
|
const List<Type> _supportedKeyValueTypes = <Type>[String, int];
|
||||||
|
|
||||||
DriverError _createInvalidKeyValueTypeError(String invalidType) {
|
DriverError _createInvalidKeyValueTypeError(String invalidType) {
|
||||||
@ -28,8 +47,8 @@ abstract class CommandWithTarget extends Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Deserializes this command from the value generated by [serialize].
|
/// Deserializes this command from the value generated by [serialize].
|
||||||
CommandWithTarget.deserialize(Map<String, String> json)
|
CommandWithTarget.deserialize(Map<String, String> json, DeserializeFinderFactory finderFactory)
|
||||||
: finder = SerializableFinder.deserialize(json),
|
: finder = finderFactory.deserializeFinder(json),
|
||||||
super.deserialize(json);
|
super.deserialize(json);
|
||||||
|
|
||||||
/// Locates the object or objects targeted by this command.
|
/// Locates the object or objects targeted by this command.
|
||||||
@ -58,7 +77,7 @@ class WaitFor extends CommandWithTarget {
|
|||||||
: super(finder, timeout: timeout);
|
: super(finder, timeout: timeout);
|
||||||
|
|
||||||
/// Deserializes this command from the value generated by [serialize].
|
/// Deserializes this command from the value generated by [serialize].
|
||||||
WaitFor.deserialize(Map<String, String> json) : super.deserialize(json);
|
WaitFor.deserialize(Map<String, String> json, DeserializeFinderFactory finderFactory) : super.deserialize(json, finderFactory);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get kind => 'waitFor';
|
String get kind => 'waitFor';
|
||||||
@ -88,7 +107,7 @@ class WaitForAbsent extends CommandWithTarget {
|
|||||||
: super(finder, timeout: timeout);
|
: super(finder, timeout: timeout);
|
||||||
|
|
||||||
/// Deserializes this command from the value generated by [serialize].
|
/// Deserializes this command from the value generated by [serialize].
|
||||||
WaitForAbsent.deserialize(Map<String, String> json) : super.deserialize(json);
|
WaitForAbsent.deserialize(Map<String, String> json, DeserializeFinderFactory finderFactory) : super.deserialize(json, finderFactory);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get kind => 'waitForAbsent';
|
String get kind => 'waitForAbsent';
|
||||||
@ -126,22 +145,6 @@ abstract class SerializableFinder {
|
|||||||
Map<String, String> serialize() => <String, String>{
|
Map<String, String> serialize() => <String, String>{
|
||||||
'finderType': finderType,
|
'finderType': finderType,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Deserializes a finder from JSON generated by [serialize].
|
|
||||||
static SerializableFinder deserialize(Map<String, String> json) {
|
|
||||||
final String finderType = json['finderType'];
|
|
||||||
switch (finderType) {
|
|
||||||
case 'ByType': return ByType.deserialize(json);
|
|
||||||
case 'ByValueKey': return ByValueKey.deserialize(json);
|
|
||||||
case 'ByTooltipMessage': return ByTooltipMessage.deserialize(json);
|
|
||||||
case 'BySemanticsLabel': return BySemanticsLabel.deserialize(json);
|
|
||||||
case 'ByText': return ByText.deserialize(json);
|
|
||||||
case 'PageBack': return const PageBack();
|
|
||||||
case 'Descendant': return Descendant.deserialize(json);
|
|
||||||
case 'Ancestor': return Ancestor.deserialize(json);
|
|
||||||
}
|
|
||||||
throw DriverError('Unsupported search specification type $finderType');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Flutter Driver finder that finds widgets by tooltip text.
|
/// A Flutter Driver finder that finds widgets by tooltip text.
|
||||||
@ -349,14 +352,14 @@ class Descendant extends SerializableFinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Deserializes the finder from JSON generated by [serialize].
|
/// Deserializes the finder from JSON generated by [serialize].
|
||||||
static Descendant deserialize(Map<String, String> json) {
|
static Descendant deserialize(Map<String, String> json, DeserializeFinderFactory finderFactory) {
|
||||||
final Map<String, String> jsonOfMatcher =
|
final Map<String, String> jsonOfMatcher =
|
||||||
Map<String, String>.from(jsonDecode(json['of']) as Map<String, dynamic>);
|
Map<String, String>.from(jsonDecode(json['of']) as Map<String, dynamic>);
|
||||||
final Map<String, String> jsonMatchingMatcher =
|
final Map<String, String> jsonMatchingMatcher =
|
||||||
Map<String, String>.from(jsonDecode(json['matching']) as Map<String, dynamic>);
|
Map<String, String>.from(jsonDecode(json['matching']) as Map<String, dynamic>);
|
||||||
return Descendant(
|
return Descendant(
|
||||||
of: SerializableFinder.deserialize(jsonOfMatcher),
|
of: finderFactory.deserializeFinder(jsonOfMatcher),
|
||||||
matching: SerializableFinder.deserialize(jsonMatchingMatcher),
|
matching: finderFactory.deserializeFinder(jsonMatchingMatcher),
|
||||||
matchRoot: json['matchRoot'] == 'true',
|
matchRoot: json['matchRoot'] == 'true',
|
||||||
firstMatchOnly: json['firstMatchOnly'] == 'true',
|
firstMatchOnly: json['firstMatchOnly'] == 'true',
|
||||||
);
|
);
|
||||||
@ -404,14 +407,14 @@ class Ancestor extends SerializableFinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Deserializes the finder from JSON generated by [serialize].
|
/// Deserializes the finder from JSON generated by [serialize].
|
||||||
static Ancestor deserialize(Map<String, String> json) {
|
static Ancestor deserialize(Map<String, String> json, DeserializeFinderFactory finderFactory) {
|
||||||
final Map<String, String> jsonOfMatcher =
|
final Map<String, String> jsonOfMatcher =
|
||||||
Map<String, String>.from(jsonDecode(json['of']) as Map<String, dynamic>);
|
Map<String, String>.from(jsonDecode(json['of']) as Map<String, dynamic>);
|
||||||
final Map<String, String> jsonMatchingMatcher =
|
final Map<String, String> jsonMatchingMatcher =
|
||||||
Map<String, String>.from(jsonDecode(json['matching']) as Map<String, dynamic>);
|
Map<String, String>.from(jsonDecode(json['matching']) as Map<String, dynamic>);
|
||||||
return Ancestor(
|
return Ancestor(
|
||||||
of: SerializableFinder.deserialize(jsonOfMatcher),
|
of: finderFactory.deserializeFinder(jsonOfMatcher),
|
||||||
matching: SerializableFinder.deserialize(jsonMatchingMatcher),
|
matching: finderFactory.deserializeFinder(jsonMatchingMatcher),
|
||||||
matchRoot: json['matchRoot'] == 'true',
|
matchRoot: json['matchRoot'] == 'true',
|
||||||
firstMatchOnly: json['firstMatchOnly'] == 'true',
|
firstMatchOnly: json['firstMatchOnly'] == 'true',
|
||||||
);
|
);
|
||||||
@ -436,8 +439,8 @@ class GetSemanticsId extends CommandWithTarget {
|
|||||||
GetSemanticsId(SerializableFinder finder, {Duration timeout}) : super(finder, timeout: timeout);
|
GetSemanticsId(SerializableFinder finder, {Duration timeout}) : super(finder, timeout: timeout);
|
||||||
|
|
||||||
/// Creates a command from a JSON map.
|
/// Creates a command from a JSON map.
|
||||||
GetSemanticsId.deserialize(Map<String, String> json)
|
GetSemanticsId.deserialize(Map<String, String> json, DeserializeFinderFactory finderFactory)
|
||||||
: super.deserialize(json);
|
: super.deserialize(json, finderFactory);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get kind => 'get_semantics_id';
|
String get kind => 'get_semantics_id';
|
||||||
|
@ -36,9 +36,9 @@ class GetOffset extends CommandWithTarget {
|
|||||||
GetOffset(SerializableFinder finder, this.offsetType, { Duration timeout }) : super(finder, timeout: timeout);
|
GetOffset(SerializableFinder finder, this.offsetType, { Duration timeout }) : super(finder, timeout: timeout);
|
||||||
|
|
||||||
/// Deserializes this command from the value generated by [serialize].
|
/// Deserializes this command from the value generated by [serialize].
|
||||||
GetOffset.deserialize(Map<String, String> json)
|
GetOffset.deserialize(Map<String, String> json, DeserializeFinderFactory finderFactory)
|
||||||
: offsetType = _offsetTypeIndex.lookupBySimpleName(json['offsetType']),
|
: offsetType = _offsetTypeIndex.lookupBySimpleName(json['offsetType']),
|
||||||
super.deserialize(json);
|
super.deserialize(json, finderFactory);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, String> serialize() => super.serialize()..addAll(<String, String>{
|
Map<String, String> serialize() => super.serialize()..addAll(<String, String>{
|
||||||
|
@ -11,7 +11,7 @@ class Tap extends CommandWithTarget {
|
|||||||
Tap(SerializableFinder finder, { Duration timeout }) : super(finder, timeout: timeout);
|
Tap(SerializableFinder finder, { Duration timeout }) : super(finder, timeout: timeout);
|
||||||
|
|
||||||
/// Deserializes this command from the value generated by [serialize].
|
/// Deserializes this command from the value generated by [serialize].
|
||||||
Tap.deserialize(Map<String, String> json) : super.deserialize(json);
|
Tap.deserialize(Map<String, String> json, DeserializeFinderFactory finderFactory) : super.deserialize(json, finderFactory);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get kind => 'tap';
|
String get kind => 'tap';
|
||||||
@ -46,12 +46,12 @@ class Scroll extends CommandWithTarget {
|
|||||||
}) : super(finder, timeout: timeout);
|
}) : super(finder, timeout: timeout);
|
||||||
|
|
||||||
/// Deserializes this command from the value generated by [serialize].
|
/// Deserializes this command from the value generated by [serialize].
|
||||||
Scroll.deserialize(Map<String, String> json)
|
Scroll.deserialize(Map<String, String> json, DeserializeFinderFactory finderFactory)
|
||||||
: dx = double.parse(json['dx']),
|
: dx = double.parse(json['dx']),
|
||||||
dy = double.parse(json['dy']),
|
dy = double.parse(json['dy']),
|
||||||
duration = Duration(microseconds: int.parse(json['duration'])),
|
duration = Duration(microseconds: int.parse(json['duration'])),
|
||||||
frequency = int.parse(json['frequency']),
|
frequency = int.parse(json['frequency']),
|
||||||
super.deserialize(json);
|
super.deserialize(json, finderFactory);
|
||||||
|
|
||||||
/// Delta X offset per move event.
|
/// Delta X offset per move event.
|
||||||
final double dx;
|
final double dx;
|
||||||
@ -99,9 +99,9 @@ class ScrollIntoView extends CommandWithTarget {
|
|||||||
ScrollIntoView(SerializableFinder finder, { this.alignment = 0.0, Duration timeout }) : super(finder, timeout: timeout);
|
ScrollIntoView(SerializableFinder finder, { this.alignment = 0.0, Duration timeout }) : super(finder, timeout: timeout);
|
||||||
|
|
||||||
/// Deserializes this command from the value generated by [serialize].
|
/// Deserializes this command from the value generated by [serialize].
|
||||||
ScrollIntoView.deserialize(Map<String, String> json)
|
ScrollIntoView.deserialize(Map<String, String> json, DeserializeFinderFactory finderFactory)
|
||||||
: alignment = double.parse(json['alignment']),
|
: alignment = double.parse(json['alignment']),
|
||||||
super.deserialize(json);
|
super.deserialize(json, finderFactory);
|
||||||
|
|
||||||
/// How the widget should be aligned.
|
/// How the widget should be aligned.
|
||||||
///
|
///
|
||||||
|
@ -11,7 +11,7 @@ class GetText extends CommandWithTarget {
|
|||||||
GetText(SerializableFinder finder, { Duration timeout }) : super(finder, timeout: timeout);
|
GetText(SerializableFinder finder, { Duration timeout }) : super(finder, timeout: timeout);
|
||||||
|
|
||||||
/// Deserializes this command from the value generated by [serialize].
|
/// Deserializes this command from the value generated by [serialize].
|
||||||
GetText.deserialize(Map<String, String> json) : super.deserialize(json);
|
GetText.deserialize(Map<String, String> json, DeserializeFinderFactory finderFactory) : super.deserialize(json, finderFactory);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get kind => 'get_text';
|
String get kind => 'get_text';
|
||||||
|
@ -43,15 +43,16 @@ const String _extensionMethod = 'ext.flutter.$_extensionMethodName';
|
|||||||
typedef DataHandler = Future<String> Function(String message);
|
typedef DataHandler = Future<String> Function(String message);
|
||||||
|
|
||||||
class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding, GestureBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
|
class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding, GestureBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
|
||||||
_DriverBinding(this._handler, this._silenceErrors);
|
_DriverBinding(this._handler, this._silenceErrors, this.finders);
|
||||||
|
|
||||||
final DataHandler _handler;
|
final DataHandler _handler;
|
||||||
final bool _silenceErrors;
|
final bool _silenceErrors;
|
||||||
|
final List<FinderExtension> finders;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initServiceExtensions() {
|
void initServiceExtensions() {
|
||||||
super.initServiceExtensions();
|
super.initServiceExtensions();
|
||||||
final FlutterDriverExtension extension = FlutterDriverExtension(_handler, _silenceErrors);
|
final FlutterDriverExtension extension = FlutterDriverExtension(_handler, _silenceErrors, finders);
|
||||||
registerServiceExtension(
|
registerServiceExtension(
|
||||||
name: _extensionMethodName,
|
name: _extensionMethodName,
|
||||||
callback: extension.call,
|
callback: extension.call,
|
||||||
@ -86,9 +87,59 @@ class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding,
|
|||||||
/// for tests where exceptions are expected. Defaults to false. Any errors
|
/// for tests where exceptions are expected. Defaults to false. Any errors
|
||||||
/// will still be returned in the `response` field of the result JSON along
|
/// will still be returned in the `response` field of the result JSON along
|
||||||
/// with an `isError` boolean.
|
/// with an `isError` boolean.
|
||||||
void enableFlutterDriverExtension({ DataHandler handler, bool silenceErrors = false }) {
|
///
|
||||||
|
/// The `finders` parameter are used to add custom finders, as in the following example.
|
||||||
|
///
|
||||||
|
/// ```dart main
|
||||||
|
/// void main() {
|
||||||
|
/// enableFlutterDriverExtension(finders: <FinderExtension>[ SomeFinderExtension() ]);
|
||||||
|
///
|
||||||
|
/// app.main();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// class Some extends SerializableFinder {
|
||||||
|
/// const Some(this.title);
|
||||||
|
///
|
||||||
|
/// final String title;
|
||||||
|
///
|
||||||
|
/// @override
|
||||||
|
/// String get finderType => 'Some';
|
||||||
|
///
|
||||||
|
/// @override
|
||||||
|
/// Map<String, String> serialize() => super.serialize()..addAll(<String, String>{
|
||||||
|
/// 'title': title,
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// class SomeFinderExtension extends FinderExtension {
|
||||||
|
///
|
||||||
|
/// String get finderType => 'Some';
|
||||||
|
///
|
||||||
|
/// SerializableFinder deserialize(Map<String, String> params, DeserializeFinderFactory finderFactory) {
|
||||||
|
/// return Some(json['title']);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// Finder createFinder(SerializableFinder finder) {
|
||||||
|
/// Some someFinder = finder as Some;
|
||||||
|
///
|
||||||
|
/// return find.byElementPredicate((Element element) {
|
||||||
|
/// final Widget widget = element.widget;
|
||||||
|
/// if (element.widget is SomeWidget) {
|
||||||
|
/// return element.widget.title == someFinder.title;
|
||||||
|
/// }
|
||||||
|
/// return false;
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
void enableFlutterDriverExtension({ DataHandler handler, bool silenceErrors = false, List<FinderExtension> finders}) {
|
||||||
assert(WidgetsBinding.instance == null);
|
assert(WidgetsBinding.instance == null);
|
||||||
_DriverBinding(handler, silenceErrors);
|
_DriverBinding(handler, silenceErrors, finders ?? <FinderExtension>[]);
|
||||||
assert(WidgetsBinding.instance is _DriverBinding);
|
assert(WidgetsBinding.instance is _DriverBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,9 +149,19 @@ typedef CommandHandlerCallback = Future<Result> Function(Command c);
|
|||||||
/// Signature for functions that deserialize a JSON map to a command object.
|
/// Signature for functions that deserialize a JSON map to a command object.
|
||||||
typedef CommandDeserializerCallback = Command Function(Map<String, String> params);
|
typedef CommandDeserializerCallback = Command Function(Map<String, String> params);
|
||||||
|
|
||||||
/// Signature for functions that run the given finder and return the [Element]
|
/// Used to expand the new Finder
|
||||||
/// found, if any, or null otherwise.
|
abstract class FinderExtension {
|
||||||
typedef FinderConstructor = Finder Function(SerializableFinder finder);
|
|
||||||
|
/// Identifies the type of finder to be used by the driver extension.
|
||||||
|
String get finderType;
|
||||||
|
|
||||||
|
/// Deserializes the finder from JSON generated by [SerializableFinder.serialize].
|
||||||
|
SerializableFinder deserialize(Map<String, String> params, DeserializeFinderFactory finderFactory);
|
||||||
|
|
||||||
|
/// Signature for functions that run the given finder and return the [Element]
|
||||||
|
/// found, if any, or null otherwise.
|
||||||
|
Finder createFinder(SerializableFinder finder);
|
||||||
|
}
|
||||||
|
|
||||||
/// The class that manages communication between a Flutter Driver test and the
|
/// The class that manages communication between a Flutter Driver test and the
|
||||||
/// application being remote-controlled, on the application side.
|
/// application being remote-controlled, on the application side.
|
||||||
@ -108,9 +169,9 @@ typedef FinderConstructor = Finder Function(SerializableFinder finder);
|
|||||||
/// This is not normally used directly. It is instantiated automatically when
|
/// This is not normally used directly. It is instantiated automatically when
|
||||||
/// calling [enableFlutterDriverExtension].
|
/// calling [enableFlutterDriverExtension].
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
class FlutterDriverExtension {
|
class FlutterDriverExtension with DeserializeFinderFactory {
|
||||||
/// Creates an object to manage a Flutter Driver connection.
|
/// Creates an object to manage a Flutter Driver connection.
|
||||||
FlutterDriverExtension(this._requestDataHandler, this._silenceErrors) {
|
FlutterDriverExtension(this._requestDataHandler, this._silenceErrors, List<FinderExtension> finders) {
|
||||||
_testTextInput.register();
|
_testTextInput.register();
|
||||||
|
|
||||||
_commandHandlers.addAll(<String, CommandHandlerCallback>{
|
_commandHandlers.addAll(<String, CommandHandlerCallback>{
|
||||||
@ -142,35 +203,28 @@ class FlutterDriverExtension {
|
|||||||
'get_layer_tree': (Map<String, String> params) => GetLayerTree.deserialize(params),
|
'get_layer_tree': (Map<String, String> params) => GetLayerTree.deserialize(params),
|
||||||
'get_render_tree': (Map<String, String> params) => GetRenderTree.deserialize(params),
|
'get_render_tree': (Map<String, String> params) => GetRenderTree.deserialize(params),
|
||||||
'enter_text': (Map<String, String> params) => EnterText.deserialize(params),
|
'enter_text': (Map<String, String> params) => EnterText.deserialize(params),
|
||||||
'get_text': (Map<String, String> params) => GetText.deserialize(params),
|
'get_text': (Map<String, String> params) => GetText.deserialize(params, this),
|
||||||
'request_data': (Map<String, String> params) => RequestData.deserialize(params),
|
'request_data': (Map<String, String> params) => RequestData.deserialize(params),
|
||||||
'scroll': (Map<String, String> params) => Scroll.deserialize(params),
|
'scroll': (Map<String, String> params) => Scroll.deserialize(params, this),
|
||||||
'scrollIntoView': (Map<String, String> params) => ScrollIntoView.deserialize(params),
|
'scrollIntoView': (Map<String, String> params) => ScrollIntoView.deserialize(params, this),
|
||||||
'set_frame_sync': (Map<String, String> params) => SetFrameSync.deserialize(params),
|
'set_frame_sync': (Map<String, String> params) => SetFrameSync.deserialize(params),
|
||||||
'set_semantics': (Map<String, String> params) => SetSemantics.deserialize(params),
|
'set_semantics': (Map<String, String> params) => SetSemantics.deserialize(params),
|
||||||
'set_text_entry_emulation': (Map<String, String> params) => SetTextEntryEmulation.deserialize(params),
|
'set_text_entry_emulation': (Map<String, String> params) => SetTextEntryEmulation.deserialize(params),
|
||||||
'tap': (Map<String, String> params) => Tap.deserialize(params),
|
'tap': (Map<String, String> params) => Tap.deserialize(params, this),
|
||||||
'waitFor': (Map<String, String> params) => WaitFor.deserialize(params),
|
'waitFor': (Map<String, String> params) => WaitFor.deserialize(params, this),
|
||||||
'waitForAbsent': (Map<String, String> params) => WaitForAbsent.deserialize(params),
|
'waitForAbsent': (Map<String, String> params) => WaitForAbsent.deserialize(params, this),
|
||||||
'waitForCondition': (Map<String, String> params) => WaitForCondition.deserialize(params),
|
'waitForCondition': (Map<String, String> params) => WaitForCondition.deserialize(params),
|
||||||
'waitUntilNoTransientCallbacks': (Map<String, String> params) => WaitUntilNoTransientCallbacks.deserialize(params),
|
'waitUntilNoTransientCallbacks': (Map<String, String> params) => WaitUntilNoTransientCallbacks.deserialize(params),
|
||||||
'waitUntilNoPendingFrame': (Map<String, String> params) => WaitUntilNoPendingFrame.deserialize(params),
|
'waitUntilNoPendingFrame': (Map<String, String> params) => WaitUntilNoPendingFrame.deserialize(params),
|
||||||
'waitUntilFirstFrameRasterized': (Map<String, String> params) => WaitUntilFirstFrameRasterized.deserialize(params),
|
'waitUntilFirstFrameRasterized': (Map<String, String> params) => WaitUntilFirstFrameRasterized.deserialize(params),
|
||||||
'get_semantics_id': (Map<String, String> params) => GetSemanticsId.deserialize(params),
|
'get_semantics_id': (Map<String, String> params) => GetSemanticsId.deserialize(params, this),
|
||||||
'get_offset': (Map<String, String> params) => GetOffset.deserialize(params),
|
'get_offset': (Map<String, String> params) => GetOffset.deserialize(params, this),
|
||||||
'get_diagnostics_tree': (Map<String, String> params) => GetDiagnosticsTree.deserialize(params),
|
'get_diagnostics_tree': (Map<String, String> params) => GetDiagnosticsTree.deserialize(params, this),
|
||||||
});
|
});
|
||||||
|
|
||||||
_finders.addAll(<String, FinderConstructor>{
|
for(final FinderExtension finder in finders) {
|
||||||
'ByText': (SerializableFinder finder) => _createByTextFinder(finder as ByText),
|
_finderExtensions[finder.finderType] = finder;
|
||||||
'ByTooltipMessage': (SerializableFinder finder) => _createByTooltipMessageFinder(finder as ByTooltipMessage),
|
}
|
||||||
'BySemanticsLabel': (SerializableFinder finder) => _createBySemanticsLabelFinder(finder as BySemanticsLabel),
|
|
||||||
'ByValueKey': (SerializableFinder finder) => _createByValueKeyFinder(finder as ByValueKey),
|
|
||||||
'ByType': (SerializableFinder finder) => _createByTypeFinder(finder as ByType),
|
|
||||||
'PageBack': (SerializableFinder finder) => _createPageBackFinder(),
|
|
||||||
'Ancestor': (SerializableFinder finder) => _createAncestorFinder(finder as Ancestor),
|
|
||||||
'Descendant': (SerializableFinder finder) => _createDescendantFinder(finder as Descendant),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final TestTextInput _testTextInput = TestTextInput();
|
final TestTextInput _testTextInput = TestTextInput();
|
||||||
@ -185,7 +239,7 @@ class FlutterDriverExtension {
|
|||||||
final WidgetController _prober = LiveWidgetController(WidgetsBinding.instance);
|
final WidgetController _prober = LiveWidgetController(WidgetsBinding.instance);
|
||||||
final Map<String, CommandHandlerCallback> _commandHandlers = <String, CommandHandlerCallback>{};
|
final Map<String, CommandHandlerCallback> _commandHandlers = <String, CommandHandlerCallback>{};
|
||||||
final Map<String, CommandDeserializerCallback> _commandDeserializers = <String, CommandDeserializerCallback>{};
|
final Map<String, CommandDeserializerCallback> _commandDeserializers = <String, CommandDeserializerCallback>{};
|
||||||
final Map<String, FinderConstructor> _finders = <String, FinderConstructor>{};
|
final Map<String, FinderExtension> _finderExtensions = <String, FinderExtension>{};
|
||||||
|
|
||||||
/// With [_frameSync] enabled, Flutter Driver will wait to perform an action
|
/// With [_frameSync] enabled, Flutter Driver will wait to perform an action
|
||||||
/// until there are no pending frames in the app under test.
|
/// until there are no pending frames in the app under test.
|
||||||
@ -372,12 +426,30 @@ class FlutterDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Finder _createFinder(SerializableFinder finder) {
|
Finder _createFinder(SerializableFinder finder) {
|
||||||
final FinderConstructor constructor = _finders[finder.finderType];
|
switch (finder.finderType) {
|
||||||
|
case 'ByText':
|
||||||
if (constructor == null)
|
return _createByTextFinder(finder as ByText);
|
||||||
|
case 'ByTooltipMessage':
|
||||||
|
return _createByTooltipMessageFinder(finder as ByTooltipMessage);
|
||||||
|
case 'BySemanticsLabel':
|
||||||
|
return _createBySemanticsLabelFinder(finder as BySemanticsLabel);
|
||||||
|
case 'ByValueKey':
|
||||||
|
return _createByValueKeyFinder(finder as ByValueKey);
|
||||||
|
case 'ByType':
|
||||||
|
return _createByTypeFinder(finder as ByType);
|
||||||
|
case 'PageBack':
|
||||||
|
return _createPageBackFinder();
|
||||||
|
case 'Ancestor':
|
||||||
|
return _createAncestorFinder(finder as Ancestor);
|
||||||
|
case 'Descendant':
|
||||||
|
return _createDescendantFinder(finder as Descendant);
|
||||||
|
default:
|
||||||
|
if (_finderExtensions.containsKey(finder.finderType)) {
|
||||||
|
return _finderExtensions[finder.finderType].createFinder(finder);
|
||||||
|
} else {
|
||||||
throw 'Unsupported finder type: ${finder.finderType}';
|
throw 'Unsupported finder type: ${finder.finderType}';
|
||||||
|
}
|
||||||
return constructor(finder);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<TapResult> _tap(Command command) async {
|
Future<TapResult> _tap(Command command) async {
|
||||||
|
@ -37,7 +37,7 @@ void main() {
|
|||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
result = null;
|
result = null;
|
||||||
extension = FlutterDriverExtension((String message) async { log.add(message); return (messageId += 1).toString(); }, false);
|
extension = FlutterDriverExtension((String message) async { log.add(message); return (messageId += 1).toString(); }, false, <FinderExtension>[]);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('returns immediately when transient callback queue is empty', (WidgetTester tester) async {
|
testWidgets('returns immediately when transient callback queue is empty', (WidgetTester tester) async {
|
||||||
@ -98,7 +98,7 @@ void main() {
|
|||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
result = null;
|
result = null;
|
||||||
extension = FlutterDriverExtension((String message) async { log.add(message); return (messageId += 1).toString(); }, false);
|
extension = FlutterDriverExtension((String message) async { log.add(message); return (messageId += 1).toString(); }, false, <FinderExtension>[]);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('waiting for NoTransientCallbacks returns immediately when transient callback queue is empty', (WidgetTester tester) async {
|
testWidgets('waiting for NoTransientCallbacks returns immediately when transient callback queue is empty', (WidgetTester tester) async {
|
||||||
@ -464,7 +464,7 @@ void main() {
|
|||||||
group('getSemanticsId', () {
|
group('getSemanticsId', () {
|
||||||
FlutterDriverExtension extension;
|
FlutterDriverExtension extension;
|
||||||
setUp(() {
|
setUp(() {
|
||||||
extension = FlutterDriverExtension((String arg) async => '', true);
|
extension = FlutterDriverExtension((String arg) async => '', true, <FinderExtension>[]);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('works when semantics are enabled', (WidgetTester tester) async {
|
testWidgets('works when semantics are enabled', (WidgetTester tester) async {
|
||||||
@ -513,7 +513,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('getOffset', (WidgetTester tester) async {
|
testWidgets('getOffset', (WidgetTester tester) async {
|
||||||
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true);
|
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true, <FinderExtension>[]);
|
||||||
|
|
||||||
Future<Offset> getOffset(OffsetType offset) async {
|
Future<Offset> getOffset(OffsetType offset) async {
|
||||||
final Map<String, String> arguments = GetOffset(ByValueKey(1), offset).serialize();
|
final Map<String, String> arguments = GetOffset(ByValueKey(1), offset).serialize();
|
||||||
@ -545,7 +545,7 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('getText', (WidgetTester tester) async {
|
testWidgets('getText', (WidgetTester tester) async {
|
||||||
await silenceDriverLogger(() async {
|
await silenceDriverLogger(() async {
|
||||||
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true);
|
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true, <FinderExtension>[]);
|
||||||
|
|
||||||
Future<String> getTextInternal(SerializableFinder search) async {
|
Future<String> getTextInternal(SerializableFinder search) async {
|
||||||
final Map<String, String> arguments = GetText(search, timeout: const Duration(seconds: 1)).serialize();
|
final Map<String, String> arguments = GetText(search, timeout: const Duration(seconds: 1)).serialize();
|
||||||
@ -615,7 +615,7 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('descendant finder', (WidgetTester tester) async {
|
testWidgets('descendant finder', (WidgetTester tester) async {
|
||||||
await silenceDriverLogger(() async {
|
await silenceDriverLogger(() async {
|
||||||
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true);
|
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true, <FinderExtension>[]);
|
||||||
|
|
||||||
Future<String> getDescendantText({ String of, bool matchRoot = false}) async {
|
Future<String> getDescendantText({ String of, bool matchRoot = false}) async {
|
||||||
final Map<String, String> arguments = GetText(Descendant(
|
final Map<String, String> arguments = GetText(Descendant(
|
||||||
@ -660,7 +660,7 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('descendant finder firstMatchOnly', (WidgetTester tester) async {
|
testWidgets('descendant finder firstMatchOnly', (WidgetTester tester) async {
|
||||||
await silenceDriverLogger(() async {
|
await silenceDriverLogger(() async {
|
||||||
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true);
|
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true, <FinderExtension>[]);
|
||||||
|
|
||||||
Future<String> getDescendantText() async {
|
Future<String> getDescendantText() async {
|
||||||
final Map<String, String> arguments = GetText(Descendant(
|
final Map<String, String> arguments = GetText(Descendant(
|
||||||
@ -694,7 +694,7 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('ancestor finder', (WidgetTester tester) async {
|
testWidgets('ancestor finder', (WidgetTester tester) async {
|
||||||
await silenceDriverLogger(() async {
|
await silenceDriverLogger(() async {
|
||||||
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true);
|
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true, <FinderExtension>[]);
|
||||||
|
|
||||||
Future<Offset> getAncestorTopLeft({ String of, String matching, bool matchRoot = false}) async {
|
Future<Offset> getAncestorTopLeft({ String of, String matching, bool matchRoot = false}) async {
|
||||||
final Map<String, String> arguments = GetOffset(Ancestor(
|
final Map<String, String> arguments = GetOffset(Ancestor(
|
||||||
@ -764,7 +764,7 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('ancestor finder firstMatchOnly', (WidgetTester tester) async {
|
testWidgets('ancestor finder firstMatchOnly', (WidgetTester tester) async {
|
||||||
await silenceDriverLogger(() async {
|
await silenceDriverLogger(() async {
|
||||||
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true);
|
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true, <FinderExtension>[]);
|
||||||
|
|
||||||
Future<Offset> getAncestorTopLeft() async {
|
Future<Offset> getAncestorTopLeft() async {
|
||||||
final Map<String, String> arguments = GetOffset(Ancestor(
|
final Map<String, String> arguments = GetOffset(Ancestor(
|
||||||
@ -812,7 +812,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('GetDiagnosticsTree', (WidgetTester tester) async {
|
testWidgets('GetDiagnosticsTree', (WidgetTester tester) async {
|
||||||
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true);
|
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true, <FinderExtension>[]);
|
||||||
|
|
||||||
Future<Map<String, Object>> getDiagnosticsTree(DiagnosticsType type, SerializableFinder finder, { int depth = 0, bool properties = true }) async {
|
Future<Map<String, Object>> getDiagnosticsTree(DiagnosticsType type, SerializableFinder finder, { int depth = 0, bool properties = true }) async {
|
||||||
final Map<String, String> arguments = GetDiagnosticsTree(finder, type, subtreeDepth: depth, includeProperties: properties).serialize();
|
final Map<String, String> arguments = GetDiagnosticsTree(finder, type, subtreeDepth: depth, includeProperties: properties).serialize();
|
||||||
@ -882,7 +882,7 @@ void main() {
|
|||||||
Map<String, dynamic> result;
|
Map<String, dynamic> result;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
extension = FlutterDriverExtension((String arg) async => '', true);
|
extension = FlutterDriverExtension((String arg) async => '', true, <FinderExtension>[]);
|
||||||
result = null;
|
result = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,11 +2,15 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter_driver/flutter_driver.dart';
|
||||||
import 'package:flutter_driver/src/common/find.dart';
|
import 'package:flutter_driver/src/common/find.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
MockDeserialize mockDeserialize;
|
||||||
|
|
||||||
test('Ancestor finder serialize', () {
|
test('Ancestor finder serialize', () {
|
||||||
const SerializableFinder of = ByType('Text');
|
const SerializableFinder of = ByType('Text');
|
||||||
final SerializableFinder matching = ByValueKey('hello');
|
final SerializableFinder matching = ByValueKey('hello');
|
||||||
@ -35,7 +39,7 @@ void main() {
|
|||||||
'firstMatchOnly': 'true',
|
'firstMatchOnly': 'true',
|
||||||
};
|
};
|
||||||
|
|
||||||
final Ancestor a = Ancestor.deserialize(serialized);
|
final Ancestor a = Ancestor.deserialize(serialized, mockDeserialize);
|
||||||
expect(a.of, isA<ByType>());
|
expect(a.of, isA<ByType>());
|
||||||
expect(a.matching, isA<ByValueKey>());
|
expect(a.matching, isA<ByValueKey>());
|
||||||
expect(a.matchRoot, isTrue);
|
expect(a.matchRoot, isTrue);
|
||||||
@ -70,10 +74,12 @@ void main() {
|
|||||||
'firstMatchOnly': 'true',
|
'firstMatchOnly': 'true',
|
||||||
};
|
};
|
||||||
|
|
||||||
final Descendant a = Descendant.deserialize(serialized);
|
final Descendant a = Descendant.deserialize(serialized, mockDeserialize);
|
||||||
expect(a.of, isA<ByType>());
|
expect(a.of, isA<ByType>());
|
||||||
expect(a.matching, isA<ByValueKey>());
|
expect(a.matching, isA<ByValueKey>());
|
||||||
expect(a.matchRoot, isTrue);
|
expect(a.matchRoot, isTrue);
|
||||||
expect(a.firstMatchOnly, isTrue);
|
expect(a.firstMatchOnly, isTrue);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MockDeserialize with Mock, DeserializeFinderFactory { }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user