mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Add getSemanticsId command to flutter_driver (#19047)
This commit is contained in:
parent
41646c9579
commit
7dd265ff18
@ -252,3 +252,46 @@ class ByType extends SerializableFinder {
|
||||
return new ByType(json['type']);
|
||||
}
|
||||
}
|
||||
|
||||
/// A Flutter driver command that retrieves a semantics id using a specified finder.
|
||||
///
|
||||
/// This command requires assertions to be enabled on the device.
|
||||
///
|
||||
/// If the object returned by the finder does not have its own semantics node,
|
||||
/// then the semantics node of the first ancestor is returned instead.
|
||||
///
|
||||
/// Throws an error if a finder returns multiple objects or if there are no
|
||||
/// semantics nodes.
|
||||
///
|
||||
/// Semantics must be enabled to use this method, either using a platform
|
||||
/// specific shell command or [FlutterDriver.setSemantics].
|
||||
class GetSemanticsId extends CommandWithTarget {
|
||||
|
||||
/// Creates a command which finds a Widget and then looks up the semantic id.
|
||||
GetSemanticsId(SerializableFinder finder, {Duration timeout}) : super(finder, timeout: timeout);
|
||||
|
||||
/// Creates a command from a json map.
|
||||
GetSemanticsId.deserialize(Map<String, String> json)
|
||||
: super.deserialize(json);
|
||||
|
||||
@override
|
||||
String get kind => 'get_semantics_id';
|
||||
}
|
||||
|
||||
/// The result of a [GetSemanticsId] command.
|
||||
class GetSemanticsIdResult extends Result {
|
||||
|
||||
/// Creates a new [GetSemanticsId] result.
|
||||
GetSemanticsIdResult(this.id);
|
||||
|
||||
/// The semantics id of the node;
|
||||
final int id;
|
||||
|
||||
/// Deserializes this result from JSON.
|
||||
static GetSemanticsIdResult fromJson(Map<String, dynamic> json) {
|
||||
return new GetSemanticsIdResult(json['id']);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{'id': id};
|
||||
}
|
||||
|
@ -571,6 +571,20 @@ class FlutterDriver {
|
||||
return result.changedState;
|
||||
}
|
||||
|
||||
/// Retrieves the semantics node id for the object returned by `finder`, or
|
||||
/// the nearest ancestor with a semantics node.
|
||||
///
|
||||
/// Throws an error if `finder` returns multiple elements or a semantics
|
||||
/// node is not found.
|
||||
///
|
||||
/// Semantics must be enabled to use this method, either using a platform
|
||||
/// specific shell command or [setSemantics].
|
||||
Future<int> getSemanticsId(SerializableFinder finder, { Duration timeout = _kShortTimeout}) async {
|
||||
final Map<String, dynamic> jsonResponse = await _sendCommand(new GetSemanticsId(finder, timeout: timeout));
|
||||
final GetSemanticsIdResult result = GetSemanticsIdResult.fromJson(jsonResponse);
|
||||
return result.id;
|
||||
}
|
||||
|
||||
/// Take a screenshot. The image will be returned as a PNG.
|
||||
Future<List<int>> screenshot({ Duration timeout }) async {
|
||||
timeout ??= _kLongTimeout;
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/semantics.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
@ -105,6 +106,7 @@ class FlutterDriverExtension {
|
||||
'waitFor': _waitFor,
|
||||
'waitForAbsent': _waitForAbsent,
|
||||
'waitUntilNoTransientCallbacks': _waitUntilNoTransientCallbacks,
|
||||
'get_semantics_id': _getSemanticsId,
|
||||
});
|
||||
|
||||
_commandDeserializers.addAll(<String, CommandDeserializerCallback>{
|
||||
@ -122,6 +124,7 @@ class FlutterDriverExtension {
|
||||
'waitFor': (Map<String, String> params) => new WaitFor.deserialize(params),
|
||||
'waitForAbsent': (Map<String, String> params) => new WaitForAbsent.deserialize(params),
|
||||
'waitUntilNoTransientCallbacks': (Map<String, String> params) => new WaitUntilNoTransientCallbacks.deserialize(params),
|
||||
'get_semantics_id': (Map<String, String> params) => new GetSemanticsId.deserialize(params),
|
||||
});
|
||||
|
||||
_finders.addAll(<String, FinderConstructor>{
|
||||
@ -298,6 +301,21 @@ class FlutterDriverExtension {
|
||||
await _waitUntilFrame(() => SchedulerBinding.instance.transientCallbackCount == 0);
|
||||
}
|
||||
|
||||
Future<GetSemanticsIdResult> _getSemanticsId(Command command) async {
|
||||
final GetSemanticsId semanticsCommand = command;
|
||||
final Finder target = await _waitForElement(_createFinder(semanticsCommand.finder));
|
||||
final Element element = target.evaluate().single;
|
||||
RenderObject renderObject = element.renderObject;
|
||||
SemanticsNode node;
|
||||
while (renderObject != null && node == null) {
|
||||
node = renderObject.debugSemantics;
|
||||
renderObject = renderObject.parent;
|
||||
}
|
||||
if (node == null)
|
||||
throw new StateError('No semantics data found');
|
||||
return new GetSemanticsIdResult(node.id);
|
||||
}
|
||||
|
||||
Future<ScrollResult> _scroll(Command command) async {
|
||||
final Scroll scrollCommand = command;
|
||||
final Finder target = await _waitForElement(_createFinder(scrollCommand.finder));
|
||||
|
@ -2,7 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_driver/src/common/find.dart';
|
||||
import 'package:flutter_driver/src/common/request_data.dart';
|
||||
import 'package:flutter_driver/src/extension/extension.dart';
|
||||
@ -68,4 +70,54 @@ void main() {
|
||||
expect(result.message, '1');
|
||||
});
|
||||
});
|
||||
|
||||
group('getSemanticsId', () {
|
||||
FlutterDriverExtension extension;
|
||||
setUp(() {
|
||||
extension = new FlutterDriverExtension((String arg) async {});
|
||||
});
|
||||
|
||||
testWidgets('works when semantics are enabled', (WidgetTester tester) async {
|
||||
final SemanticsHandle semantics = RendererBinding.instance.pipelineOwner.ensureSemantics();
|
||||
await tester.pumpWidget(
|
||||
const Text('hello', textDirection: TextDirection.ltr));
|
||||
|
||||
final Map<String, Object> arguments = new GetSemanticsId(new ByText('hello')).serialize();
|
||||
final GetSemanticsIdResult result = GetSemanticsIdResult.fromJson((await extension.call(arguments))['response']);
|
||||
|
||||
expect(result.id, 1);
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
testWidgets('throws state error if no data is found', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const Text('hello', textDirection: TextDirection.ltr));
|
||||
|
||||
final Map<String, Object> arguments = new GetSemanticsId(new ByText('hello')).serialize();
|
||||
final Map<String, Object> response = await extension.call(arguments);
|
||||
|
||||
expect(response['isError'], true);
|
||||
expect(response['response'], contains('Bad state: No semantics data found'));
|
||||
});
|
||||
|
||||
testWidgets('throws state error multiple matches are found', (WidgetTester tester) async {
|
||||
final SemanticsHandle semantics = RendererBinding.instance.pipelineOwner.ensureSemantics();
|
||||
await tester.pumpWidget(
|
||||
new Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: new ListView(children: const <Widget>[
|
||||
const SizedBox(width: 100.0, height: 100.0, child: const Text('hello')),
|
||||
const SizedBox(width: 100.0, height: 100.0, child: const Text('hello')),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
final Map<String, Object> arguments = new GetSemanticsId(new ByText('hello')).serialize();
|
||||
final Map<String, Object> response = await extension.call(arguments);
|
||||
|
||||
expect(response['isError'], true);
|
||||
expect(response['response'], contains('Bad state: Too many elements'));
|
||||
semantics.dispose();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user