diff --git a/packages/flutter_driver/lib/src/common/layer_tree.dart b/packages/flutter_driver/lib/src/common/layer_tree.dart new file mode 100644 index 00000000000..647903c75f2 --- /dev/null +++ b/packages/flutter_driver/lib/src/common/layer_tree.dart @@ -0,0 +1,37 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'message.dart'; + +/// A Flutter Driver command that requests a string representation of the layer tree. +class GetLayerTree extends Command { + /// Create a command to request a string representation of the layer tree. + const GetLayerTree({ Duration timeout }) : super(timeout: timeout); + + /// Deserializes this command from the value generated by [serialize]. + GetLayerTree.deserialize(Map json) : super.deserialize(json); + + @override + String get kind => 'get_layer_tree'; +} + +/// A string representation of the layer tree, the result of a +/// [FlutterDriver.getLayerTree] method. +class LayerTree extends Result { + /// Creates a [LayerTree] object with the given string representation. + const LayerTree(this.tree); + + /// String representation of the layer tree. + final String tree; + + /// Deserializes the result from JSON. + static LayerTree fromJson(Map json) { + return LayerTree(json['tree'] as String); + } + + @override + Map toJson() => { + 'tree': tree, + }; +} diff --git a/packages/flutter_driver/lib/src/driver/driver.dart b/packages/flutter_driver/lib/src/driver/driver.dart index 71a1d7d94b2..d5a48c2660e 100644 --- a/packages/flutter_driver/lib/src/driver/driver.dart +++ b/packages/flutter_driver/lib/src/driver/driver.dart @@ -23,6 +23,7 @@ import '../common/fuchsia_compat.dart'; import '../common/geometry.dart'; import '../common/gesture.dart'; import '../common/health.dart'; +import '../common/layer_tree.dart'; import '../common/message.dart'; import '../common/render_tree.dart'; import '../common/request_data.dart'; @@ -475,6 +476,11 @@ class FlutterDriver { return RenderTree.fromJson(await _sendCommand(GetRenderTree(timeout: timeout))); } + /// Returns a dump of the layer tree. + Future getLayerTree({ Duration timeout }) async { + return LayerTree.fromJson(await _sendCommand(GetLayerTree(timeout: timeout))); + } + /// Taps at the center of the widget located by [finder]. Future tap(SerializableFinder finder, { Duration timeout }) async { await _sendCommand(Tap(finder, timeout: timeout)); diff --git a/packages/flutter_driver/lib/src/extension/extension.dart b/packages/flutter_driver/lib/src/extension/extension.dart index f883b7b77a8..af1f8bec0af 100644 --- a/packages/flutter_driver/lib/src/extension/extension.dart +++ b/packages/flutter_driver/lib/src/extension/extension.dart @@ -24,6 +24,7 @@ import '../common/frame_sync.dart'; import '../common/geometry.dart'; import '../common/gesture.dart'; import '../common/health.dart'; +import '../common/layer_tree.dart'; import '../common/message.dart'; import '../common/render_tree.dart'; import '../common/request_data.dart'; @@ -107,6 +108,7 @@ class FlutterDriverExtension { _commandHandlers.addAll({ 'get_health': _getHealth, + 'get_layer_tree': _getLayerTree, 'get_render_tree': _getRenderTree, 'enter_text': _enterText, 'get_text': _getText, @@ -130,6 +132,7 @@ class FlutterDriverExtension { _commandDeserializers.addAll({ 'get_health': (Map params) => GetHealth.deserialize(params), + 'get_layer_tree': (Map params) => GetLayerTree.deserialize(params), 'get_render_tree': (Map params) => GetRenderTree.deserialize(params), 'enter_text': (Map params) => EnterText.deserialize(params), 'get_text': (Map params) => GetText.deserialize(params), @@ -229,6 +232,10 @@ class FlutterDriverExtension { Future _getHealth(Command command) async => const Health(HealthStatus.ok); + Future _getLayerTree(Command command) async { + return LayerTree(RendererBinding.instance?.renderView?.debugLayer?.toStringDeep()); + } + Future _getRenderTree(Command command) async { return RenderTree(RendererBinding.instance?.renderView?.toStringDeep()); } diff --git a/packages/flutter_driver/test/flutter_driver_test.dart b/packages/flutter_driver/test/flutter_driver_test.dart index afd96e30360..4ee125689ea 100644 --- a/packages/flutter_driver/test/flutter_driver_test.dart +++ b/packages/flutter_driver/test/flutter_driver_test.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'package:flutter_driver/src/common/error.dart'; import 'package:flutter_driver/src/common/health.dart'; +import 'package:flutter_driver/src/common/layer_tree.dart'; import 'package:flutter_driver/src/common/wait.dart'; import 'package:flutter_driver/src/driver/driver.dart'; import 'package:flutter_driver/src/driver/timeline.dart'; @@ -231,6 +232,25 @@ void main() { }); }); + group('getLayerTree', () { + test('sends the getLayerTree command', () async { + when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) { + expect(i.positionalArguments[1], { + 'command': 'get_layer_tree', + 'timeout': _kSerializedTestTimeout, + }); + return makeMockResponse({ + 'tree': 'hello', + }); + }); + final LayerTree result = await driver.getLayerTree(timeout: _kTestTimeout); + final LayerTree referenceTree = LayerTree.fromJson({ + 'tree': 'hello', + }); + expect(result.tree, referenceTree.tree); + }); + }); + group('waitFor', () { test('requires a target reference', () async { expect(driver.waitFor(null), throwsA(isInstanceOf()));