diff --git a/packages/flutter/lib/src/widgets/widget_inspector.dart b/packages/flutter/lib/src/widgets/widget_inspector.dart index 62dc21c58e0b..4624f895be92 100644 --- a/packages/flutter/lib/src/widgets/widget_inspector.dart +++ b/packages/flutter/lib/src/widgets/widget_inspector.dart @@ -1089,9 +1089,19 @@ mixin WidgetInspectorService { name: 'getRootWidgetSummaryTree', callback: _getRootWidgetSummaryTree, ); - _registerServiceExtensionWithArg( + registerServiceExtension( name: 'getDetailsSubtree', - callback: _getDetailsSubtree, + callback: (Map parameters) async { + assert(parameters.containsKey('objectGroup')); + final String subtreeDepth = parameters['subtreeDepth']; + return { + 'result': _getDetailsSubtree( + parameters['arg'], + parameters['objectGroup'], + subtreeDepth != null ? int.parse(subtreeDepth) : 2, + ), + }; + }, ); _registerServiceExtensionWithArg( name: 'getSelectedRenderObject', @@ -1603,15 +1613,27 @@ mixin WidgetInspectorService { /// [DiagnosticsNode] object that `diagnosticsNodeId` references providing /// information needed for the details subtree view. /// + /// The number of levels of the subtree that should be returned is specified + /// by the [subtreeDepth] parameter. This value defaults to 2 for backwards + /// compatibility. + /// /// See also: /// /// * [getChildrenDetailsSubtree], a method to get children of a node /// in the details subtree. - String getDetailsSubtree(String id, String groupName) { - return _safeJsonEncode(_getDetailsSubtree( id, groupName)); - } - - Map _getDetailsSubtree(String id, String groupName) { + String getDetailsSubtree( + String id, + String groupName, { + int subtreeDepth = 2, + }) { + return _safeJsonEncode(_getDetailsSubtree( id, groupName, subtreeDepth)); + } + + Map _getDetailsSubtree( + String id, + String groupName, + int subtreeDepth, + ) { final DiagnosticsNode root = toObject(id); if (root == null) { return null; @@ -1621,7 +1643,7 @@ mixin WidgetInspectorService { _SerializationDelegate( groupName: groupName, summaryTree: false, - subtreeDepth: 2, // TODO(jacobr): make subtreeDepth configurable. + subtreeDepth: subtreeDepth, includeProperties: true, service: this, ), diff --git a/packages/flutter/test/widgets/widget_inspector_test.dart b/packages/flutter/test/widgets/widget_inspector_test.dart index 70e64a4fa01c..a9654733df29 100644 --- a/packages/flutter/test/widgets/widget_inspector_test.dart +++ b/packages/flutter/test/widgets/widget_inspector_test.dart @@ -253,14 +253,14 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { } Future testExtension(String name, Map arguments) async { - expect(extensions.containsKey(name), isTrue); + expect(extensions, contains(name)); // Encode and decode to JSON to match behavior using a real service // extension where only JSON is allowed. return json.decode(json.encode(await extensions[name](arguments)))['result']; } Future testBoolExtension(String name, Map arguments) async { - expect(extensions.containsKey(name), isTrue); + expect(extensions, contains(name)); // Encode and decode to JSON to match behavior using a real service // extension where only JSON is allowed. return json.decode(json.encode(await extensions[name](arguments)))['enabled']; @@ -371,7 +371,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { final InspectorSelection selection = getInspectorState().selection; expect(paragraphText(selection.current), equals('TOP')); final RenderObject topButton = find.byKey(topButtonKey).evaluate().first.renderObject; - expect(selection.candidates.contains(topButton), isTrue); + expect(selection.candidates, contains(topButton)); await tester.tap(find.text('TOP')); expect(log, equals(['top'])); @@ -906,7 +906,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { final List children = node.getChildren(); expect(children.length, 1); final ErrorDescription child = children[0]; - expect(child.valueToString().contains(Uri.parse(pubRootTest).path), true); + expect(child.valueToString(), contains(Uri.parse(pubRootTest).path)); } else { expect(nodes[2].runtimeType, ErrorDescription); final ErrorDescription node = nodes[2]; @@ -967,7 +967,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { final List children = node.getChildren(); expect(children.length, 1); final ErrorDescription child = children[0]; - expect(child.valueToString().contains(Uri.parse(pubRootTest).path), true); + expect(child.valueToString(), contains(Uri.parse(pubRootTest).path)); } else { expect(nodes[1].runtimeType, ErrorDescription); final ErrorDescription node = nodes[1]; @@ -1264,7 +1264,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { for (Map propertyJson in propertiesJson) { final Object property = service.toObject(propertyJson['objectId']); expect(property, isInstanceOf()); - expect(expectedProperties.contains(property), isTrue); + expect(expectedProperties, contains(property)); } } }); @@ -1297,12 +1297,27 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { expect(service.toObject(childJson['valueId']), equals(children[i].value)); expect(service.toObject(childJson['objectId']), isInstanceOf()); final List propertiesJson = childJson['properties']; + for (Map propertyJson in propertiesJson) { + expect(propertyJson, isNot(contains('children'))); + } final DiagnosticsNode diagnosticsNode = service.toObject(childJson['objectId']); final List expectedProperties = diagnosticsNode.getProperties(); for (Map propertyJson in propertiesJson) { final Object property = service.toObject(propertyJson['objectId']); expect(property, isInstanceOf()); - expect(expectedProperties.contains(property), isTrue); + expect(expectedProperties, contains(property)); + } + } + + final Map deepSubtreeJson = await service.testExtension( + 'getDetailsSubtree', + {'arg': id, 'objectGroup': group, 'subtreeDepth': '3'}, + ); + final List deepChildrenJson = deepSubtreeJson['children']; + for(Map childJson in deepChildrenJson) { + final List propertiesJson = childJson['properties']; + for (Map propertyJson in propertiesJson) { + expect(propertyJson, contains('children')); } } }); @@ -1319,15 +1334,15 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { final String id = service.toId(diagnostic, group); final Map subtreeJson = await service.testExtension('getDetailsSubtree', {'arg': id, 'objectGroup': group}); expect(subtreeJson['objectId'], equals(id)); - expect(subtreeJson.containsKey('children'), isTrue); + expect(subtreeJson, contains('children')); final List propertiesJson = subtreeJson['properties']; expect(propertiesJson.length, equals(1)); final Map relatedProperty = propertiesJson.first; expect(relatedProperty['name'], equals('related')); expect(relatedProperty['description'], equals('CyclicDiagnostic-b')); - expect(relatedProperty.containsKey('isDiagnosticableValue'), isTrue); - expect(relatedProperty.containsKey('children'), isFalse); - expect(relatedProperty.containsKey('properties'), isTrue); + expect(relatedProperty, contains('isDiagnosticableValue')); + expect(relatedProperty, isNot(contains('children'))); + expect(relatedProperty, contains('properties')); final List relatedWidgetProperties = relatedProperty['properties']; expect(relatedWidgetProperties.length, equals(1)); final Map nestedRelatedProperty = relatedWidgetProperties.first; @@ -1336,9 +1351,9 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { // which we already included as the root node as that would indicate a // cycle. expect(nestedRelatedProperty['description'], equals('CyclicDiagnostic-a')); - expect(nestedRelatedProperty.containsKey('isDiagnosticableValue'), isTrue); - expect(nestedRelatedProperty.containsKey('properties'), isFalse); - expect(nestedRelatedProperty.containsKey('children'), isFalse); + expect(nestedRelatedProperty, contains('isDiagnosticableValue')); + expect(nestedRelatedProperty, isNot(contains('properties'))); + expect(nestedRelatedProperty, isNot(contains('children'))); }); testWidgets('ext.flutter.inspector.getRootWidgetSummaryTree', (WidgetTester tester) async { @@ -1682,7 +1697,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { final int count = data[i + 1]; totalCount += count; maxCount = max(maxCount, count); - expect(knownLocations.containsKey(id), isTrue); + expect(knownLocations, contains(id)); } expect(totalCount, equals(27)); // The creation locations that were rebuilt the most were rebuilt 6 times @@ -1701,7 +1716,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { expect(event['startTime'], isInstanceOf()); data = event['events']; // No new locations were rebuilt. - expect(event.containsKey('newLocations'), isFalse); + expect(event, isNot(contains('newLocations'))); // There were two rebuilds: one for the ClockText element itself and one // for its child. @@ -1737,7 +1752,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { expect(event['startTime'], isInstanceOf()); data = event['events']; // No new locations were rebuilt. - expect(event.containsKey('newLocations'), isFalse); + expect(event, isNot(contains('newLocations'))); expect(data.length, equals(4)); id = data[0]; @@ -1771,7 +1786,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { expect(event['startTime'], isInstanceOf()); data = event['events']; // No new locations were rebuilt. - expect(event.containsKey('newLocations'), isFalse); + expect(event, isNot(contains('newLocations'))); expect(data.length, equals(4)); id = data[0]; @@ -1795,13 +1810,13 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { count = data[3]; expect(count, equals(1)); // Verify the rebuild location is new. - expect(knownLocations.containsKey(id), isFalse); + expect(knownLocations, isNot(contains(id))); addToKnownLocationsMap( knownLocations: knownLocations, newLocations: newLocations, ); // Verify the rebuild location was included in the newLocations data. - expect(knownLocations.containsKey(id), isTrue); + expect(knownLocations, contains(id)); // Turn off rebuild counts. expect( @@ -1884,7 +1899,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { final int count = data[i + 1]; totalCount += count; maxCount = max(maxCount, count); - expect(knownLocations.containsKey(id), isTrue); + expect(knownLocations, contains(id)); } expect(totalCount, equals(34)); // The creation locations that were rebuilt the most were rebuilt 6 times @@ -1903,7 +1918,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { expect(event['startTime'], isInstanceOf()); data = event['events']; // No new locations were rebuilt. - expect(event.containsKey('newLocations'), isFalse); + expect(event, isNot(contains('newLocations'))); // Triggering a a rebuild of one widget in this app causes the whole app // to repaint. @@ -2657,7 +2672,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { } } visitChildren(detailedChildren); - expect(appBars.single.containsKey('children'), isFalse); + expect(appBars.single, isNot(contains('children'))); }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // Test requires --track-widget-creation flag. } }