| // 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 'package:flutter/foundation.dart'; |
| import 'package:flutter/material.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| void main() { |
| test('Element diagnostics json includes widgetRuntimeType', () async { |
| final Element element = _TestElement(); |
| |
| final Map<String, Object?> json = element.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate()); |
| expect(json['widgetRuntimeType'], 'Placeholder'); |
| expect(json['stateful'], isFalse); |
| }); |
| |
| test('StatefulElement diagnostics are stateful', () { |
| final Element element = StatefulElement(const Tooltip(message: 'foo')); |
| |
| final Map<String, Object?> json = element.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate()); |
| expect(json['widgetRuntimeType'], 'Tooltip'); |
| expect(json['stateful'], isTrue); |
| }); |
| |
| group('Serialization', () { |
| final TestTree testTree = TestTree( |
| properties: <DiagnosticsNode>[ |
| StringProperty('stringProperty1', 'value1', quoted: false), |
| DoubleProperty('doubleProperty1', 42.5), |
| DoubleProperty('roundedProperty', 1.0 / 3.0), |
| StringProperty('DO_NOT_SHOW', 'DO_NOT_SHOW', level: DiagnosticLevel.hidden, quoted: false), |
| DiagnosticsProperty<Object>('DO_NOT_SHOW_NULL', null, defaultValue: null), |
| DiagnosticsProperty<Object>('nullProperty', null), |
| StringProperty('node_type', '<root node>', showName: false, quoted: false), |
| ], |
| children: <TestTree>[ |
| TestTree(name: 'node A'), |
| TestTree( |
| name: 'node B', |
| properties: <DiagnosticsNode>[ |
| StringProperty('p1', 'v1', quoted: false), |
| StringProperty('p2', 'v2', quoted: false), |
| ], |
| children: <TestTree>[ |
| TestTree(name: 'node B1'), |
| TestTree( |
| name: 'node B2', |
| properties: <DiagnosticsNode>[StringProperty('property1', 'value1', quoted: false)], |
| ), |
| TestTree( |
| name: 'node B3', |
| properties: <DiagnosticsNode>[ |
| StringProperty('node_type', '<leaf node>', showName: false, quoted: false), |
| IntProperty('foo', 42), |
| ], |
| ), |
| ], |
| ), |
| TestTree( |
| name: 'node C', |
| properties: <DiagnosticsNode>[ |
| StringProperty('foo', 'multi\nline\nvalue!', quoted: false), |
| ], |
| ), |
| ], |
| ); |
| |
| test('default', () { |
| final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate()); |
| expect(result.containsKey('properties'), isFalse); |
| expect(result.containsKey('children'), isFalse); |
| }); |
| |
| test('subtreeDepth 1', () { |
| final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate(subtreeDepth: 1)); |
| expect(result.containsKey('properties'), isFalse); |
| final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>; |
| expect(children[0].containsKey('children'), isFalse); |
| expect(children[1].containsKey('children'), isFalse); |
| expect(children[2].containsKey('children'), isFalse); |
| }); |
| |
| test('subtreeDepth 5', () { |
| final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate(subtreeDepth: 5)); |
| expect(result.containsKey('properties'), isFalse); |
| final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>; |
| expect(children[0]['children'], hasLength(0)); |
| expect(children[1]['children'], hasLength(3)); |
| expect(children[2]['children'], hasLength(0)); |
| }); |
| |
| test('includeProperties', () { |
| final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate(includeProperties: true)); |
| expect(result.containsKey('children'), isFalse); |
| expect(result['properties'], hasLength(7)); |
| }); |
| |
| test('includeProperties with subtreedepth 1', () { |
| final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate( |
| includeProperties: true, |
| subtreeDepth: 1, |
| )); |
| expect(result['properties'], hasLength(7)); |
| final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>; |
| expect(children, hasLength(3)); |
| expect(children[0]['properties'], hasLength(0)); |
| expect(children[1]['properties'], hasLength(2)); |
| expect(children[2]['properties'], hasLength(1)); |
| }); |
| |
| test('additionalNodeProperties', () { |
| final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(const TestDiagnosticsSerializationDelegate( |
| includeProperties: true, |
| subtreeDepth: 1, |
| additionalNodePropertiesMap: <String, Object>{ |
| 'foo': true, |
| }, |
| )); |
| expect(result['foo'], isTrue); |
| final List<Map<String, Object?>> properties = result['properties']! as List<Map<String, Object?>>; |
| expect(properties, hasLength(7)); |
| expect(properties.every((Map<String, Object?> property) => property['foo'] == true), isTrue); |
| |
| final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>; |
| expect(children, hasLength(3)); |
| expect(children.every((Map<String, Object?> child) => child['foo'] == true), isTrue); |
| }); |
| |
| test('filterProperties - sublist', () { |
| final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(TestDiagnosticsSerializationDelegate( |
| includeProperties: true, |
| propertyFilter: (List<DiagnosticsNode> nodes, DiagnosticsNode owner) { |
| return nodes.whereType<StringProperty>().toList(); |
| }, |
| )); |
| final List<Map<String, Object?>> properties = result['properties']! as List<Map<String, Object?>>; |
| expect(properties, hasLength(3)); |
| expect(properties.every((Map<String, Object?> property) => property['type'] == 'StringProperty'), isTrue); |
| }); |
| |
| test('filterProperties - replace', () { |
| bool replaced = false; |
| final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(TestDiagnosticsSerializationDelegate( |
| includeProperties: true, |
| propertyFilter: (List<DiagnosticsNode> nodes, DiagnosticsNode owner) { |
| if (replaced) { |
| return nodes; |
| } |
| replaced = true; |
| return <DiagnosticsNode>[ |
| StringProperty('foo', 'bar'), |
| ]; |
| }, |
| )); |
| final List<Map<String, Object?>> properties = result['properties']! as List<Map<String, Object?>>; |
| expect(properties, hasLength(1)); |
| expect(properties.single['name'], 'foo'); |
| }); |
| |
| test('filterChildren - sublist', () { |
| final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(TestDiagnosticsSerializationDelegate( |
| subtreeDepth: 1, |
| childFilter: (List<DiagnosticsNode> nodes, DiagnosticsNode owner) { |
| return nodes.where((DiagnosticsNode node) => node.getProperties().isEmpty).toList(); |
| }, |
| )); |
| final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>; |
| expect(children, hasLength(1)); |
| }); |
| |
| test('filterChildren - replace', () { |
| final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(TestDiagnosticsSerializationDelegate( |
| subtreeDepth: 1, |
| childFilter: (List<DiagnosticsNode> nodes, DiagnosticsNode owner) { |
| return nodes.expand((DiagnosticsNode node) => node.getChildren()).toList(); |
| }, |
| )); |
| final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>; |
| expect(children, hasLength(3)); |
| expect(children.first['name'], 'child node B1'); |
| }); |
| |
| test('nodeTruncator', () { |
| final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(TestDiagnosticsSerializationDelegate( |
| subtreeDepth: 5, |
| includeProperties: true, |
| nodeTruncator: (List<DiagnosticsNode> nodes, DiagnosticsNode? owner) { |
| return nodes.take(2).toList(); |
| }, |
| )); |
| final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>; |
| expect(children, hasLength(3)); |
| expect(children.last['truncated'], isTrue); |
| |
| final List<Map<String, Object?>> properties = result['properties']! as List<Map<String, Object?>>; |
| expect(properties, hasLength(3)); |
| expect(properties.last['truncated'], isTrue); |
| }); |
| |
| test('delegateForAddingNodes', () { |
| final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(TestDiagnosticsSerializationDelegate( |
| subtreeDepth: 5, |
| includeProperties: true, |
| nodeDelegator: (DiagnosticsNode node, DiagnosticsSerializationDelegate delegate) { |
| return delegate.copyWith(includeProperties: false); |
| }, |
| )); |
| final List<Map<String, Object?>> properties = result['properties']! as List<Map<String, Object?>>; |
| expect(properties, hasLength(7)); |
| expect(properties.every((Map<String, Object?> property) => !property.containsKey('properties')), isTrue); |
| |
| final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>; |
| expect(children, hasLength(3)); |
| expect(children.every((Map<String, Object?> child) => !child.containsKey('properties')), isTrue); |
| }); |
| }); |
| } |
| |
| class _TestElement extends Element { |
| _TestElement() : super(const Placeholder()); |
| |
| @override |
| bool get debugDoingBuild => throw UnimplementedError(); |
| } |
| |
| class TestTree extends Object with DiagnosticableTreeMixin { |
| TestTree({ |
| this.name = '', |
| this.style, |
| this.children = const <TestTree>[], |
| this.properties = const <DiagnosticsNode>[], |
| }); |
| |
| final String name; |
| final List<TestTree> children; |
| final List<DiagnosticsNode> properties; |
| final DiagnosticsTreeStyle? style; |
| |
| @override |
| List<DiagnosticsNode> debugDescribeChildren() => <DiagnosticsNode>[ |
| for (final TestTree child in children) |
| child.toDiagnosticsNode( |
| name: 'child ${child.name}', |
| style: child.style, |
| ), |
| ]; |
| |
| @override |
| void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
| super.debugFillProperties(properties); |
| if (style != null) { |
| properties.defaultDiagnosticsTreeStyle = style!; |
| } |
| |
| this.properties.forEach(properties.add); |
| } |
| } |
| |
| typedef NodeDelegator = DiagnosticsSerializationDelegate Function(DiagnosticsNode node, TestDiagnosticsSerializationDelegate delegate); |
| typedef NodeTruncator = List<DiagnosticsNode> Function(List<DiagnosticsNode> nodes, DiagnosticsNode? owner); |
| typedef NodeFilter = List<DiagnosticsNode> Function(List<DiagnosticsNode> nodes, DiagnosticsNode owner); |
| |
| class TestDiagnosticsSerializationDelegate implements DiagnosticsSerializationDelegate { |
| const TestDiagnosticsSerializationDelegate({ |
| this.includeProperties = false, |
| this.subtreeDepth = 0, |
| this.additionalNodePropertiesMap = const <String, Object>{}, |
| this.childFilter, |
| this.propertyFilter, |
| this.nodeTruncator, |
| this.nodeDelegator, |
| }); |
| |
| final Map<String, Object> additionalNodePropertiesMap; |
| final NodeFilter? childFilter; |
| final NodeFilter? propertyFilter; |
| final NodeTruncator? nodeTruncator; |
| final NodeDelegator? nodeDelegator; |
| |
| @override |
| Map<String, Object> additionalNodeProperties(DiagnosticsNode node) { |
| return additionalNodePropertiesMap; |
| } |
| |
| @override |
| DiagnosticsSerializationDelegate delegateForNode(DiagnosticsNode node) { |
| if (nodeDelegator != null) { |
| return nodeDelegator!(node, this); |
| } |
| return subtreeDepth > 0 ? copyWith(subtreeDepth: subtreeDepth - 1) : this; |
| } |
| |
| @override |
| bool get expandPropertyValues => false; |
| |
| @override |
| List<DiagnosticsNode> filterChildren(List<DiagnosticsNode> nodes, DiagnosticsNode owner) { |
| return childFilter?.call(nodes, owner) ?? nodes; |
| } |
| |
| @override |
| List<DiagnosticsNode> filterProperties(List<DiagnosticsNode> nodes, DiagnosticsNode owner) { |
| return propertyFilter?.call(nodes, owner) ?? nodes; |
| } |
| |
| @override |
| final bool includeProperties; |
| |
| @override |
| final int subtreeDepth; |
| |
| @override |
| List<DiagnosticsNode> truncateNodesList(List<DiagnosticsNode> nodes, DiagnosticsNode? owner) { |
| return nodeTruncator?.call(nodes, owner) ?? nodes; |
| } |
| |
| @override |
| DiagnosticsSerializationDelegate copyWith({int? subtreeDepth, bool? includeProperties}) { |
| return TestDiagnosticsSerializationDelegate( |
| includeProperties: includeProperties ?? this.includeProperties, |
| subtreeDepth: subtreeDepth ?? this.subtreeDepth, |
| additionalNodePropertiesMap: additionalNodePropertiesMap, |
| childFilter: childFilter, |
| propertyFilter: propertyFilter, |
| nodeTruncator: nodeTruncator, |
| nodeDelegator: nodeDelegator, |
| ); |
| } |
| } |