diff --git a/packages/flutter/test/widgets/semantics_tester_generate_test_semantics_expression_for_current_semantics_tree_test.dart b/packages/flutter/test/widgets/semantics_tester_generate_test_semantics_expression_for_current_semantics_tree_test.dart index 5349eef02c..43d092ebf6 100644 --- a/packages/flutter/test/widgets/semantics_tester_generate_test_semantics_expression_for_current_semantics_tree_test.dart +++ b/packages/flutter/test/widgets/semantics_tester_generate_test_semantics_expression_for_current_semantics_tree_test.dart @@ -25,25 +25,27 @@ void _tests() { }); Future pumpTestWidget(WidgetTester tester) async { - await tester.pumpWidget(MaterialApp( - home: ListView( - children: [ - const Text('Plain text'), - Semantics( - selected: true, - checked: true, - onTap: () { }, - onDecrease: () { }, - value: 'test-value', - increasedValue: 'test-increasedValue', - decreasedValue: 'test-decreasedValue', - hint: 'test-hint', - textDirection: TextDirection.rtl, - child: const Text('Interactive text'), - ), - ], + await tester.pumpWidget( + MaterialApp( + home: ListView( + children: [ + const Text('Plain text'), + Semantics( + selected: true, + checked: true, + onTap: () {}, + onDecrease: () {}, + value: 'test-value', + increasedValue: 'test-increasedValue', + decreasedValue: 'test-decreasedValue', + hint: 'test-hint', + textDirection: TextDirection.rtl, + child: const Text('Interactive text'), + ), + ], + ), ), - )); + ); } // This test generates code using generateTestSemanticsExpressionForCurrentSemanticsTree @@ -55,12 +57,15 @@ void _tests() { testWidgets('generates code', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await pumpTestWidget(tester); - final String code = semantics - .generateTestSemanticsExpressionForCurrentSemanticsTree(DebugSemanticsDumpOrder.inverseHitTest) - .split('\n') - .map((String line) => line.trim()) - .join('\n') - .trim(); + final String code = + semantics + .generateTestSemanticsExpressionForCurrentSemanticsTree( + DebugSemanticsDumpOrder.inverseHitTest, + ) + .split('\n') + .map((String line) => line.trim()) + .join('\n') + .trim(); File? findThisTestFile(Directory directory) { for (final FileSystemEntity entity in directory.listSync()) { @@ -69,7 +74,10 @@ void _tests() { if (childSearch != null) { return childSearch; } - } else if (entity is File && entity.path.endsWith('semantics_tester_generate_test_semantics_expression_for_current_semantics_tree_test.dart')) { + } else if (entity is File && + entity.path.endsWith( + 'semantics_tester_generate_test_semantics_expression_for_current_semantics_tree_test.dart', + )) { return entity; } } @@ -79,14 +87,13 @@ void _tests() { final File thisTestFile = findThisTestFile(Directory.current)!; expect(thisTestFile, isNotNull); String expectedCode = thisTestFile.readAsStringSync(); - expectedCode = expectedCode.substring( - expectedCode.indexOf('v' * 12) + 12, - expectedCode.indexOf('^' * 12) - 3, - ) - .split('\n') - .map((String line) => line.trim()) - .join('\n') - .trim(); + expectedCode = + expectedCode + .substring(expectedCode.indexOf('v' * 12) + 12, expectedCode.indexOf('^' * 12) - 3) + .split('\n') + .map((String line) => line.trim()) + .join('\n') + .trim(); semantics.dispose(); expect('$code,', expectedCode); }); @@ -102,6 +109,7 @@ void _tests() { // You must update it when changing the output generated by // generateTestSemanticsExpressionForCurrentSemanticsTree. Otherwise, // the test 'generates code', defined above, will fail. + // dart format off // vvvvvvvvvvvv TestSemantics.root( children: [ @@ -154,6 +162,7 @@ void _tests() { ], ), // ^^^^^^^^^^^^ + // dart format on ignoreRect: true, ignoreTransform: true, ignoreId: true, diff --git a/packages/flutter/test/widgets/widget_inspector_test.dart b/packages/flutter/test/widgets/widget_inspector_test.dart index 150aa895ca..c26dddc2ba 100644 --- a/packages/flutter/test/widgets/widget_inspector_test.dart +++ b/packages/flutter/test/widgets/widget_inspector_test.dart @@ -28,7 +28,7 @@ import 'widget_inspector_test_utils.dart'; // columns will impact whether tests pass. class ClockDemo extends StatelessWidget { - const ClockDemo({ super.key }); + const ClockDemo({super.key}); @override Widget build(BuildContext context) { @@ -51,20 +51,13 @@ class ClockDemo extends StatelessWidget { Widget makeClock(String label, int utcOffset) { return Stack( - children: [ - const Icon(Icons.watch), - Text(label), - ClockText(utcOffset: utcOffset), - ], + children: [const Icon(Icons.watch), Text(label), ClockText(utcOffset: utcOffset)], ); } } class ClockText extends StatefulWidget { - const ClockText({ - super.key, - this.utcOffset = 0, - }); + const ClockText({super.key, this.utcOffset = 0}); final int utcOffset; @@ -92,12 +85,7 @@ class _ClockTextState extends State { if (currentTime == null) { return const Text('stopped'); } - return Text( - currentTime! - .toUtc() - .add(Duration(hours: widget.utcOffset)) - .toIso8601String(), - ); + return Text(currentTime!.toUtc().add(Duration(hours: widget.utcOffset)).toIso8601String()); } } @@ -123,7 +111,7 @@ class CyclicDiagnostic extends DiagnosticableTree { // We have to override toString to avoid the toString call itself triggering a // stack overflow. @override - String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) { + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { return toStringShort(); } @@ -160,10 +148,11 @@ class RenderRepaintBoundaryWithDebugPaint extends RenderRepaintBoundary { assert(() { // Draw some debug paint UI interleaving creating layers and drawing // directly to the context's canvas. - final Paint paint = Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1.0 - ..color = Colors.red; + final Paint paint = + Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1.0 + ..color = Colors.red; { final PictureLayer pictureLayer = PictureLayer(Offset.zero & size); final ui.PictureRecorder recorder = ui.PictureRecorder(); @@ -176,11 +165,7 @@ class RenderRepaintBoundaryWithDebugPaint extends RenderRepaintBoundary { ..append(pictureLayer), ); } - context.canvas.drawLine( - offset, - offset.translate(size.width, size.height), - paint, - ); + context.canvas.drawLine(offset, offset.translate(size.width, size.height), paint); { final PictureLayer pictureLayer = PictureLayer(Offset.zero & size); final ui.PictureRecorder recorder = ui.PictureRecorder(); @@ -194,11 +179,7 @@ class RenderRepaintBoundaryWithDebugPaint extends RenderRepaintBoundary { ); } paint.color = Colors.blue; - context.canvas.drawLine( - offset, - offset.translate(size.width * 0.5, size.height * 0.5), - paint, - ); + context.canvas.drawLine(offset, offset.translate(size.width * 0.5, size.height * 0.5), paint); return true; }()); } @@ -206,10 +187,7 @@ class RenderRepaintBoundaryWithDebugPaint extends RenderRepaintBoundary { class RepaintBoundaryWithDebugPaint extends RepaintBoundary { /// Creates a widget that isolates repaints. - const RepaintBoundaryWithDebugPaint({ - super.key, - super.child, - }); + const RepaintBoundaryWithDebugPaint({super.key, super.child}); @override RenderRepaintBoundary createRenderObject(BuildContext context) { @@ -242,10 +220,19 @@ extension TextFromString on String { } } -final List _weakValueTests = [1, 1.0, 'hello', true, false, Object(), [3, 4], DateTime(2023)]; +final List _weakValueTests = [ + 1, + 1.0, + 'hello', + true, + false, + Object(), + [3, 4], + DateTime(2023), +]; void main() { - group('$InspectorReferenceData', (){ + group('$InspectorReferenceData', () { for (final Object item in _weakValueTests) { test('can be created for any type but $Record, $item', () async { final InspectorReferenceData weakValue = InspectorReferenceData(item, 'id'); @@ -254,11 +241,11 @@ void main() { } test('throws for $Record', () async { - expect(()=> InspectorReferenceData((1, 2), 'id'), throwsA(isA())); + expect(() => InspectorReferenceData((1, 2), 'id'), throwsA(isA())); }); }); - group('$WeakMap', (){ + group('$WeakMap', () { for (final Object item in _weakValueTests) { test('assigns and removes value, $item', () async { final WeakMap weakMap = WeakMap(); @@ -316,7 +303,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { return find.text(letter).evaluate().first; } - test ('objectToDiagnosticsNode returns null for non-diagnosticable', () { + test('objectToDiagnosticsNode returns null for non-diagnosticable', () { expect(WidgetInspectorService.objectToDiagnosticsNode(Alignment.bottomCenter), isNull); }); @@ -386,8 +373,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { required GlobalKey key, }) { exitWidgetSelectionButtonKey = key; - return Material( - child: ElevatedButton(onPressed: onPressed, key: key, child: null)); + return Material(child: ElevatedButton(onPressed: onPressed, key: key, child: null)); } String paragraphText(RenderParagraph paragraph) { @@ -395,7 +381,6 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { return textSpan.text!; } - Future tapAndVerifyWidgetSelection( Finder widgetFinder, { required bool isSelected, @@ -406,12 +391,8 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { await tester.pump(); // Verify the tap was intercepted by the Widget Inspector. - final RenderObject renderObject = - find.byKey(widgetKey).evaluate().first.renderObject!; - expect( - WidgetInspectorService.instance.selection.candidates, - contains(renderObject), - ); + final RenderObject renderObject = find.byKey(widgetKey).evaluate().first.renderObject!; + expect(WidgetInspectorService.instance.selection.candidates, contains(renderObject)); } Future panAndVerifyWidgetSelection({ @@ -430,12 +411,8 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { await tester.pump(); // Verify the pan end was intercepted by the Widget Inspector. - final RenderObject renderObject = - find.byKey(widgetKey).evaluate().first.renderObject!; - expect( - WidgetInspectorService.instance.selection.candidates, - contains(renderObject), - ); + final RenderObject renderObject = find.byKey(widgetKey).evaluate().first.renderObject!; + expect(WidgetInspectorService.instance.selection.candidates, contains(renderObject)); } await tester.pumpWidget( @@ -479,9 +456,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { ); expect(log, equals([])); expect( - paragraphText( - WidgetInspectorService.instance.selection.current! as RenderParagraph, - ), + paragraphText(WidgetInspectorService.instance.selection.current! as RenderParagraph), equals('TOP'), ); @@ -492,14 +467,11 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { widgetKey: bottomButtonKey, ); expect( - paragraphText( - WidgetInspectorService.instance.selection.current! as RenderParagraph, - ), + paragraphText(WidgetInspectorService.instance.selection.current! as RenderParagraph), equals('BOTTOM'), ); expect(log, equals([])); - // Now pan to the top button and verify it's selected in the Inspector. await panAndVerifyWidgetSelection( startAt: find.text('BOTTOM'), @@ -508,9 +480,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { widgetKey: topButtonKey, ); expect( - paragraphText( - WidgetInspectorService.instance.selection.current! as RenderParagraph, - ), + paragraphText(WidgetInspectorService.instance.selection.current! as RenderParagraph), equals('TOP'), ); expect(log, equals([])); @@ -523,9 +493,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { widgetKey: bottomButtonKey, ); expect( - paragraphText( - WidgetInspectorService.instance.selection.current! as RenderParagraph, - ), + paragraphText(WidgetInspectorService.instance.selection.current! as RenderParagraph), equals('BOTTOM'), ); expect(log, equals([])); @@ -540,14 +508,14 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { // Ensure the inspector selection is still BOTTOM (not TOP). expect( - paragraphText( - WidgetInspectorService.instance.selection.current! as RenderParagraph, - ), + paragraphText(WidgetInspectorService.instance.selection.current! as RenderParagraph), equals('BOTTOM'), ); }); - testWidgets('WidgetInspector non-invertible transform regression test', (WidgetTester tester) async { + testWidgets('WidgetInspector non-invertible transform regression test', ( + WidgetTester tester, + ) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -587,8 +555,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { required GlobalKey key, }) { exitWidgetSelectionButtonKey = key; - return Material( - child: ElevatedButton(onPressed: onPressed, key: key, child: null)); + return Material(child: ElevatedButton(onPressed: onPressed, key: key, child: null)); } await tester.pumpWidget( @@ -600,25 +567,30 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { moveExitWidgetSelectionButtonBuilder: null, child: ListView( dragStartBehavior: DragStartBehavior.down, - children: [ - Container( - key: childKey, - height: 5000.0, - ), - ], + children: [Container(key: childKey, height: 5000.0)], ), ), ), ); expect(tester.getTopLeft(find.byKey(childKey)).dy, equals(0.0)); - await tester.fling(find.byType(ListView), const Offset(0.0, -200.0), 200.0, warnIfMissed: false); + await tester.fling( + find.byType(ListView), + const Offset(0.0, -200.0), + 200.0, + warnIfMissed: false, + ); await tester.pump(); // Fling does nothing as are in inspect mode. expect(tester.getTopLeft(find.byKey(childKey)).dy, equals(0.0)); - await tester.fling(find.byType(ListView), const Offset(200.0, 0.0), 200.0, warnIfMissed: false); + await tester.fling( + find.byType(ListView), + const Offset(200.0, 0.0), + 200.0, + warnIfMissed: false, + ); await tester.pump(); // Fling still does nothing as are in inspect mode. @@ -679,7 +651,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { final GlobalKey inspectorKey = GlobalKey(); final GlobalKey clickTarget = GlobalKey(); - Widget createSubtree({ double? width, Key? key }) { + Widget createSubtree({double? width, Key? key}) { return Stack( children: [ Positioned( @@ -695,11 +667,26 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { } late final OverlayEntry entry1; - addTearDown(() => entry1..remove()..dispose()); + addTearDown( + () => + entry1 + ..remove() + ..dispose(), + ); late final OverlayEntry entry2; - addTearDown(() => entry2..remove()..dispose()); + addTearDown( + () => + entry2 + ..remove() + ..dispose(), + ); late final OverlayEntry entry3; - addTearDown(() => entry3..remove()..dispose()); + addTearDown( + () => + entry3 + ..remove() + ..dispose(), + ); await tester.pumpWidget( Directionality( @@ -732,17 +719,12 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { await tester.longPress(find.byKey(clickTarget), warnIfMissed: false); // The object with width 95.0 wins over the object with width 94.0 because // the subtree with width 94.0 is offstage. - expect( - WidgetInspectorService.instance.selection.current?.semanticBounds.width, - equals(95.0), - ); + expect(WidgetInspectorService.instance.selection.current?.semanticBounds.width, equals(95.0)); // Exactly 2 out of the 3 text elements should be in the candidate list of // objects to select as only 2 are onstage. expect( - WidgetInspectorService.instance.selection.candidates - .whereType() - .length, + WidgetInspectorService.instance.selection.candidates.whereType().length, equals(2), ); }); @@ -754,10 +736,11 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { final GlobalKey childKey = GlobalKey(); final GlobalKey repaintBoundaryKey = GlobalKey(); - final Matrix4 mainTransform = Matrix4.identity() - ..translate(50.0, 30.0) - ..scale(0.8, 0.8) - ..translate(100.0, 50.0); + final Matrix4 mainTransform = + Matrix4.identity() + ..translate(50.0, 30.0) + ..scale(0.8, 0.8) + ..translate(100.0, 50.0); await tester.pumpWidget( RepaintBoundary( @@ -814,13 +797,8 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { final GlobalKey child1Key = GlobalKey(); final GlobalKey child2Key = GlobalKey(); - ExitWidgetSelectionButtonBuilder exitWidgetSelectionButtonBuilder( - Key key) { - return ( - BuildContext context, { - required VoidCallback onPressed, - required GlobalKey key, - }) { + ExitWidgetSelectionButtonBuilder exitWidgetSelectionButtonBuilder(Key key) { + return (BuildContext context, {required VoidCallback onPressed, required GlobalKey key}) { return Material(child: ElevatedButton(onPressed: onPressed, key: key, child: null)); }; } @@ -838,25 +816,21 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { Flexible( child: WidgetInspector( key: inspector1Key, - exitWidgetSelectionButtonBuilder: - exitWidgetSelectionButtonBuilder(selectButton1Key), - moveExitWidgetSelectionButtonBuilder: null, - child: Container( - key: child1Key, - child: const Text('Child 1'), + exitWidgetSelectionButtonBuilder: exitWidgetSelectionButtonBuilder( + selectButton1Key, ), + moveExitWidgetSelectionButtonBuilder: null, + child: Container(key: child1Key, child: const Text('Child 1')), ), ), Flexible( child: WidgetInspector( key: inspector2Key, - exitWidgetSelectionButtonBuilder: - exitWidgetSelectionButtonBuilder(selectButton2Key), - moveExitWidgetSelectionButtonBuilder: null, - child: Container( - key: child2Key, - child: const Text('Child 2'), + exitWidgetSelectionButtonBuilder: exitWidgetSelectionButtonBuilder( + selectButton2Key, ), + moveExitWidgetSelectionButtonBuilder: null, + child: Container(key: child2Key, child: const Text('Child 2')), ), ), ], @@ -867,18 +841,14 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { await tester.tap(find.text('Child 1'), warnIfMissed: false); await tester.pump(); expect( - paragraphText( - WidgetInspectorService.instance.selection.current! as RenderParagraph, - ), + paragraphText(WidgetInspectorService.instance.selection.current! as RenderParagraph), equals('Child 1'), ); await tester.tap(find.text('Child 2'), warnIfMissed: false); await tester.pump(); expect( - paragraphText( - WidgetInspectorService.instance.selection.current! as RenderParagraph, - ), + paragraphText(WidgetInspectorService.instance.selection.current! as RenderParagraph), equals('Child 2'), ); }); @@ -886,17 +856,17 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { testWidgets( 'WidgetInspector Exit Selection Mode button', (WidgetTester tester) async { - // Enable widget selection mode. - WidgetInspectorService.instance.isSelectMode = true; + // Enable widget selection mode. + WidgetInspectorService.instance.isSelectMode = true; final GlobalKey inspectorKey = GlobalKey(); setupDefaultPubRootDirectory(service); - Widget exitWidgetSelectionButtonBuilder( - BuildContext context, { - required VoidCallback onPressed, - required GlobalKey key, - }) { + Widget exitWidgetSelectionButtonBuilder( + BuildContext context, { + required VoidCallback onPressed, + required GlobalKey key, + }) { return Material(child: ElevatedButton(onPressed: onPressed, key: key, child: null)); } @@ -923,10 +893,12 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(objectsInspected, equals([childElement.renderObject])); // ensure that a navigate event was sent for the renderObject - final List> navigateEventsPosted - = service.dispatchedEvents('navigate', stream: 'ToolEvent',); + final List> navigateEventsPosted = service.dispatchedEvents( + 'navigate', + stream: 'ToolEvent', + ); expect(navigateEventsPosted.length, equals(1)); - final Map event = navigateEventsPosted[0]; + final Map event = navigateEventsPosted[0]; final String file = event['fileUri']! as String; final int line = event['line']! as int; final int column = event['column']! as int; @@ -937,12 +909,12 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { // Column numbers are more stable than line numbers. expect(column, equals(28)); }, - skip: !WidgetInspectorService.instance.isWidgetCreationTracked() // [intended] Test requires --track-widget-creation flag. + skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. ); testWidgets( - 'WidgetInspector Move Exit Selection Mode button to the right / left', - (WidgetTester tester) async { + 'WidgetInspector Move Exit Selection Mode button to the right / left', + (WidgetTester tester) async { // Enable widget selection mode. WidgetInspectorService.instance.isSelectMode = true; @@ -977,10 +949,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { } Finder buttonFinder(String buttonText) { - return find.ancestor( - of: find.text(buttonText), - matching: find.byType(ElevatedButton), - ); + return find.ancestor(of: find.text(buttonText), matching: find.byType(ElevatedButton)); } await tester.pumpWidget( @@ -989,8 +958,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { child: WidgetInspector( key: inspectorKey, exitWidgetSelectionButtonBuilder: exitWidgetSelectionButtonBuilder, - moveExitWidgetSelectionButtonBuilder: - moveWidgetSelectionButtonBuilder, + moveExitWidgetSelectionButtonBuilder: moveWidgetSelectionButtonBuilder, child: const Text('APP'), ), ), @@ -1011,8 +979,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(moveRightButton, findsNothing); final Finder moveLeftButton = buttonFinder('MOVE LEFT'); expect(moveLeftButton, findsOneWidget); - final double exitButtonXAfterMovingRight = - tester.getCenter(exitButton).dx; + final double exitButtonXAfterMovingRight = tester.getCenter(exitButton).dx; expect(initialExitButtonX, lessThan(exitButtonXAfterMovingRight)); // Move the button to the left again. @@ -1025,10 +992,12 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { final double exitButtonXAfterMovingLeft = tester.getCenter(exitButton).dx; expect(exitButtonXAfterMovingLeft, equals(initialExitButtonX)); }, - skip: !WidgetInspectorService.instance.isWidgetCreationTracked() // [intended] Test requires --track-widget-creation flag. + skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. ); - testWidgets('test transformDebugCreator will re-order if after stack trace', (WidgetTester tester) async { + testWidgets('test transformDebugCreator will re-order if after stack trace', ( + WidgetTester tester, + ) async { final bool widgetTracked = WidgetInspectorService.instance.isWidgetCreationTracked(); await tester.pumpWidget( const Directionality( @@ -1046,17 +1015,15 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { service.setSelection(elementA, 'my-group'); late String pubRootTest; if (widgetTracked) { - final Map jsonObject = json.decode( - service.getSelectedWidget(null, 'my-group'), - ) as Map; - final Map creationLocation = jsonObject['creationLocation']! as Map; + final Map jsonObject = + json.decode(service.getSelectedWidget(null, 'my-group')) as Map; + final Map creationLocation = + jsonObject['creationLocation']! as Map; expect(creationLocation, isNotNull); final String fileA = creationLocation['file']! as String; expect(fileA, endsWith('widget_inspector_test.dart')); expect(jsonObject, isNot(contains('createdByLocalProject'))); - final List segments = Uri - .parse(fileA) - .pathSegments; + final List segments = Uri.parse(fileA).pathSegments; // Strip a couple subdirectories away to generate a plausible pub root // directory. pubRootTest = '/${segments.take(segments.length - 2).join('/')}'; @@ -1069,7 +1036,9 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { builder.add(DiagnosticsStackTrace('When the exception was thrown, this was the stack', null)); builder.add(DiagnosticsDebugCreator(DebugCreator(elementA))); - final List nodes = List.from(debugTransformDebugCreator(builder.properties)); + final List nodes = List.from( + debugTransformDebugCreator(builder.properties), + ); expect(nodes.length, 5); expect(nodes[0].runtimeType, StringProperty); expect(nodes[0].name, 'dummy1'); @@ -1086,173 +1055,203 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { } else { expect(nodes[2].runtimeType, ErrorDescription); final ErrorDescription node = nodes[2] as ErrorDescription; - expect(node.valueToString().startsWith('Widget creation tracking is currently disabled.'), true); + expect( + node.valueToString().startsWith('Widget creation tracking is currently disabled.'), + true, + ); } expect(nodes[3].runtimeType, ErrorSpacer); expect(nodes[4].runtimeType, DiagnosticsStackTrace); }); - testWidgets('test transformDebugCreator will not re-order if before stack trace', (WidgetTester tester) async { - final bool widgetTracked = WidgetInspectorService.instance.isWidgetCreationTracked(); - await tester.pumpWidget( - const Directionality( - textDirection: TextDirection.ltr, - child: Stack( - children: [ - Text('a'), - Text('b', textDirection: TextDirection.ltr), - Text('c', textDirection: TextDirection.ltr), - ], + testWidgets( + 'test transformDebugCreator will not re-order if before stack trace', + (WidgetTester tester) async { + final bool widgetTracked = WidgetInspectorService.instance.isWidgetCreationTracked(); + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: Stack( + children: [ + Text('a'), + Text('b', textDirection: TextDirection.ltr), + Text('c', textDirection: TextDirection.ltr), + ], + ), ), - ), - ); - final Element elementA = find.text('a').evaluate().first; - late String pubRootTest; - if (widgetTracked) { - final Map jsonObject = json.decode( - service.getSelectedWidget(null, 'my-group'), - ) as Map; - final Map creationLocation = jsonObject['creationLocation']! as Map; - expect(creationLocation, isNotNull); - final String fileA = creationLocation['file']! as String; - expect(fileA, endsWith('widget_inspector_test.dart')); - expect(jsonObject, isNot(contains('createdByLocalProject'))); - final List segments = Uri - .parse(fileA) - .pathSegments; - // Strip a couple subdirectories away to generate a plausible pub root - // directory. - pubRootTest = '/${segments.take(segments.length - 2).join('/')}'; - service.resetPubRootDirectories(); - service.addPubRootDirectories([pubRootTest]); - } - final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); - builder.add(StringProperty('dummy1', 'value')); - builder.add(DiagnosticsDebugCreator(DebugCreator(elementA))); - builder.add(StringProperty('dummy2', 'value')); - builder.add(DiagnosticsStackTrace('When the exception was thrown, this was the stack', null)); + ); + final Element elementA = find.text('a').evaluate().first; + late String pubRootTest; + if (widgetTracked) { + final Map jsonObject = + json.decode(service.getSelectedWidget(null, 'my-group')) as Map; + final Map creationLocation = + jsonObject['creationLocation']! as Map; + expect(creationLocation, isNotNull); + final String fileA = creationLocation['file']! as String; + expect(fileA, endsWith('widget_inspector_test.dart')); + expect(jsonObject, isNot(contains('createdByLocalProject'))); + final List segments = Uri.parse(fileA).pathSegments; + // Strip a couple subdirectories away to generate a plausible pub root + // directory. + pubRootTest = '/${segments.take(segments.length - 2).join('/')}'; + service.resetPubRootDirectories(); + service.addPubRootDirectories([pubRootTest]); + } + final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); + builder.add(StringProperty('dummy1', 'value')); + builder.add(DiagnosticsDebugCreator(DebugCreator(elementA))); + builder.add(StringProperty('dummy2', 'value')); + builder.add( + DiagnosticsStackTrace('When the exception was thrown, this was the stack', null), + ); - final List nodes = List.from(debugTransformDebugCreator(builder.properties)); - expect(nodes.length, 5); - expect(nodes[0].runtimeType, StringProperty); - expect(nodes[0].name, 'dummy1'); - // transformed node stays at original place. - if (widgetTracked) { + final List nodes = List.from( + debugTransformDebugCreator(builder.properties), + ); + expect(nodes.length, 5); + expect(nodes[0].runtimeType, StringProperty); + expect(nodes[0].name, 'dummy1'); + // transformed node stays at original place. + if (widgetTracked) { + expect(nodes[1].runtimeType, DiagnosticsBlock); + final DiagnosticsBlock node = nodes[1] as DiagnosticsBlock; + final List children = node.getChildren(); + expect(children.length, 1); + final ErrorDescription child = children[0] as ErrorDescription; + expect(child.valueToString(), contains(Uri.parse(pubRootTest).path)); + } else { + expect(nodes[1].runtimeType, ErrorDescription); + final ErrorDescription node = nodes[1] as ErrorDescription; + expect( + node.valueToString().startsWith('Widget creation tracking is currently disabled.'), + true, + ); + } + expect(nodes[2].runtimeType, ErrorSpacer); + expect(nodes[3].runtimeType, StringProperty); + expect(nodes[3].name, 'dummy2'); + expect(nodes[4].runtimeType, DiagnosticsStackTrace); + }, + skip: WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --no-track-widget-creation flag. + ); + + testWidgets( + 'test transformDebugCreator will add DevToolsDeepLinkProperty for overflow errors', + (WidgetTester tester) async { + activeDevToolsServerAddress = 'http://127.0.0.1:9100'; + connectedVmServiceUri = 'http://127.0.0.1:55269/798ay5al_FM=/'; + + setupDefaultPubRootDirectory(service); + + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: Stack( + children: [ + Text('a'), + Text('b', textDirection: TextDirection.ltr), + Text('c', textDirection: TextDirection.ltr), + ], + ), + ), + ); + final Element elementA = find.text('a').evaluate().first; + + final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); + builder.add(ErrorSummary('A RenderFlex overflowed by 273 pixels on the bottom')); + builder.add(DiagnosticsDebugCreator(DebugCreator(elementA))); + builder.add(StringProperty('dummy2', 'value')); + + final List nodes = List.from( + debugTransformDebugCreator(builder.properties), + ); + expect(nodes.length, 6); + expect(nodes[0].runtimeType, ErrorSummary); expect(nodes[1].runtimeType, DiagnosticsBlock); - final DiagnosticsBlock node = nodes[1] as DiagnosticsBlock; - final List children = node.getChildren(); - expect(children.length, 1); - final ErrorDescription child = children[0] as ErrorDescription; - expect(child.valueToString(), contains(Uri.parse(pubRootTest).path)); - } else { - expect(nodes[1].runtimeType, ErrorDescription); - final ErrorDescription node = nodes[1] as ErrorDescription; - expect(node.valueToString().startsWith('Widget creation tracking is currently disabled.'), true); - } - expect(nodes[2].runtimeType, ErrorSpacer); - expect(nodes[3].runtimeType, StringProperty); - expect(nodes[3].name, 'dummy2'); - expect(nodes[4].runtimeType, DiagnosticsStackTrace); - }, skip: WidgetInspectorService.instance.isWidgetCreationTracked()); // [intended] Test requires --no-track-widget-creation flag. + expect(nodes[2].runtimeType, ErrorSpacer); + expect(nodes[3].runtimeType, DevToolsDeepLinkProperty); + expect(nodes[4].runtimeType, ErrorSpacer); + expect(nodes[5].runtimeType, StringProperty); + }, + skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. + ); - testWidgets('test transformDebugCreator will add DevToolsDeepLinkProperty for overflow errors', (WidgetTester tester) async { - activeDevToolsServerAddress = 'http://127.0.0.1:9100'; - connectedVmServiceUri = 'http://127.0.0.1:55269/798ay5al_FM=/'; + testWidgets( + 'test transformDebugCreator will not add DevToolsDeepLinkProperty for non-overflow errors', + (WidgetTester tester) async { + activeDevToolsServerAddress = 'http://127.0.0.1:9100'; + connectedVmServiceUri = 'http://127.0.0.1:55269/798ay5al_FM=/'; + setupDefaultPubRootDirectory(service); - setupDefaultPubRootDirectory(service); - - await tester.pumpWidget( - const Directionality( - textDirection: TextDirection.ltr, - child: Stack( - children: [ - Text('a'), - Text('b', textDirection: TextDirection.ltr), - Text('c', textDirection: TextDirection.ltr), - ], + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: Stack( + children: [ + Text('a'), + Text('b', textDirection: TextDirection.ltr), + Text('c', textDirection: TextDirection.ltr), + ], + ), ), - ), - ); - final Element elementA = find.text('a').evaluate().first; + ); + final Element elementA = find.text('a').evaluate().first; - final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); - builder.add(ErrorSummary('A RenderFlex overflowed by 273 pixels on the bottom')); - builder.add(DiagnosticsDebugCreator(DebugCreator(elementA))); - builder.add(StringProperty('dummy2', 'value')); + final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); + builder.add(ErrorSummary('some other error')); + builder.add(DiagnosticsDebugCreator(DebugCreator(elementA))); + builder.add(StringProperty('dummy2', 'value')); - final List nodes = List.from(debugTransformDebugCreator(builder.properties)); - expect(nodes.length, 6); - expect(nodes[0].runtimeType, ErrorSummary); - expect(nodes[1].runtimeType, DiagnosticsBlock); - expect(nodes[2].runtimeType, ErrorSpacer); - expect(nodes[3].runtimeType, DevToolsDeepLinkProperty); - expect(nodes[4].runtimeType, ErrorSpacer); - expect(nodes[5].runtimeType, StringProperty); - }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // [intended] Test requires --track-widget-creation flag. + final List nodes = List.from( + debugTransformDebugCreator(builder.properties), + ); + expect(nodes.length, 4); + expect(nodes[0].runtimeType, ErrorSummary); + expect(nodes[1].runtimeType, DiagnosticsBlock); + expect(nodes[2].runtimeType, ErrorSpacer); + expect(nodes[3].runtimeType, StringProperty); + }, + skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. + ); - testWidgets('test transformDebugCreator will not add DevToolsDeepLinkProperty for non-overflow errors', (WidgetTester tester) async { - activeDevToolsServerAddress = 'http://127.0.0.1:9100'; - connectedVmServiceUri = 'http://127.0.0.1:55269/798ay5al_FM=/'; - setupDefaultPubRootDirectory(service); + testWidgets( + 'test transformDebugCreator will not add DevToolsDeepLinkProperty if devtoolsServerAddress is unavailable', + (WidgetTester tester) async { + activeDevToolsServerAddress = null; + connectedVmServiceUri = 'http://127.0.0.1:55269/798ay5al_FM=/'; + setupDefaultPubRootDirectory(service); - await tester.pumpWidget( - const Directionality( - textDirection: TextDirection.ltr, - child: Stack( - children: [ - Text('a'), - Text('b', textDirection: TextDirection.ltr), - Text('c', textDirection: TextDirection.ltr), - ], + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: Stack( + children: [ + Text('a'), + Text('b', textDirection: TextDirection.ltr), + Text('c', textDirection: TextDirection.ltr), + ], + ), ), - ), - ); - final Element elementA = find.text('a').evaluate().first; + ); + final Element elementA = find.text('a').evaluate().first; - final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); - builder.add(ErrorSummary('some other error')); - builder.add(DiagnosticsDebugCreator(DebugCreator(elementA))); - builder.add(StringProperty('dummy2', 'value')); + final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); + builder.add(ErrorSummary('A RenderFlex overflowed by 273 pixels on the bottom')); + builder.add(DiagnosticsDebugCreator(DebugCreator(elementA))); + builder.add(StringProperty('dummy2', 'value')); - final List nodes = List.from(debugTransformDebugCreator(builder.properties)); - expect(nodes.length, 4); - expect(nodes[0].runtimeType, ErrorSummary); - expect(nodes[1].runtimeType, DiagnosticsBlock); - expect(nodes[2].runtimeType, ErrorSpacer); - expect(nodes[3].runtimeType, StringProperty); - }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // [intended] Test requires --track-widget-creation flag. - - testWidgets('test transformDebugCreator will not add DevToolsDeepLinkProperty if devtoolsServerAddress is unavailable', (WidgetTester tester) async { - activeDevToolsServerAddress = null; - connectedVmServiceUri = 'http://127.0.0.1:55269/798ay5al_FM=/'; - setupDefaultPubRootDirectory(service); - - await tester.pumpWidget( - const Directionality( - textDirection: TextDirection.ltr, - child: Stack( - children: [ - Text('a'), - Text('b', textDirection: TextDirection.ltr), - Text('c', textDirection: TextDirection.ltr), - ], - ), - ), - ); - final Element elementA = find.text('a').evaluate().first; - - final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); - builder.add(ErrorSummary('A RenderFlex overflowed by 273 pixels on the bottom')); - builder.add(DiagnosticsDebugCreator(DebugCreator(elementA))); - builder.add(StringProperty('dummy2', 'value')); - - final List nodes = List.from(debugTransformDebugCreator(builder.properties)); - expect(nodes.length, 4); - expect(nodes[0].runtimeType, ErrorSummary); - expect(nodes[1].runtimeType, DiagnosticsBlock); - expect(nodes[2].runtimeType, ErrorSpacer); - expect(nodes[3].runtimeType, StringProperty); - }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // [intended] Test requires --track-widget-creation flag. + final List nodes = List.from( + debugTransformDebugCreator(builder.properties), + ); + expect(nodes.length, 4); + expect(nodes[0].runtimeType, ErrorSummary); + expect(nodes[1].runtimeType, DiagnosticsBlock); + expect(nodes[2].runtimeType, ErrorSpacer); + expect(nodes[3].runtimeType, StringProperty); + }, + skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. + ); // TODO(CoderDake): Clean up pubRootDirectory tests https://github.com/flutter/flutter/issues/107186 group('pubRootDirectory', () { @@ -1279,10 +1278,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { service.addPubRootDirectories([]); final List pubRoots = await service.currentPubRootDirectories; - expect(pubRoots, unorderedEquals([ - directoryA, - directoryB, - ])); + expect(pubRoots, unorderedEquals([directoryA, directoryB])); }); test('handles duplicates', () async { @@ -1290,15 +1286,12 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { directoryA, 'file://$directoryA', directoryB, - directoryB + directoryB, ]; service.addPubRootDirectories(directories); final List pubRoots = await service.currentPubRootDirectories; - expect(pubRoots, unorderedEquals([ - directoryA, - directoryB, - ])); + expect(pubRoots, unorderedEquals([directoryA, directoryB])); }); }); @@ -1309,7 +1302,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { }); test('removes multiple directories', () async { - service.removePubRootDirectories([directoryA, directoryB,]); + service.removePubRootDirectories([directoryA, directoryB]); final List pubRoots = await service.currentPubRootDirectories; expect(pubRoots, equals([directoryC])); @@ -1340,11 +1333,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { service.removePubRootDirectories(['/x/y/z']); final List pubRoots = await service.currentPubRootDirectories; - expect(pubRoots, unorderedEquals([ - directoryA, - directoryB, - directoryC, - ])); + expect(pubRoots, unorderedEquals([directoryA, directoryB, directoryC])); }); }); }); @@ -1401,8 +1390,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(service.toObjectForSourceLocation(elementId), equals(widget)); expect(element, isNot(equals(widget))); service.disposeGroup(group); - expect(() => service.toObjectForSourceLocation(elementId), - throwsFlutterError); + expect(() => service.toObjectForSourceLocation(elementId), throwsFlutterError); }); test('object id test', () { @@ -1458,9 +1446,9 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(selectionChangedCount, equals(2)); expect(service.selection.current, equals(elementB.renderObject)); expect( - service.selection.currentElement, - equals((elementB.renderObject!.debugCreator! as DebugCreator) - .element)); + service.selection.currentElement, + equals((elementB.renderObject!.debugCreator! as DebugCreator).element), + ); service.setSelection('invalid selection'); expect(selectionChangedCount, equals(2)); @@ -1476,16 +1464,11 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(service.selection.currentElement, equals(elementA)); }); - testWidgets('defunct selection regression test', - (WidgetTester tester) async { + testWidgets('defunct selection regression test', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, - child: Stack( - children: [ - Text('a', textDirection: TextDirection.ltr), - ], - ), + child: Stack(children: [Text('a', textDirection: TextDirection.ltr)]), ), ); final Element elementA = find.text('a').evaluate().first; @@ -1495,9 +1478,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(service.selection.current, equals(elementA.renderObject)); await tester.pumpWidget( - const SizedBox( - child: Text('b', textDirection: TextDirection.ltr), - ), + const SizedBox(child: Text('b', textDirection: TextDirection.ltr)), ); // Selection is now empty as the element is defunct. expect(service.selection.currentElement, equals(null)); @@ -1510,11 +1491,8 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { // Verify that generating json for a defunct element does not crash. expect( elementA.toDiagnosticsNode().toJsonMap( - InspectorSerializationDelegate( - service: service, - includeProperties: true, - ), - ), + InspectorSerializationDelegate(service: service, includeProperties: true), + ), isNotNull, ); @@ -1538,52 +1516,48 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { service.disposeAllGroups(); final Element elementB = findElementABC('b'); final String bId = service.toId(elementB, group)!; - final Object? jsonList = - json.decode(service.getParentChain(bId, group)); + final Object? jsonList = json.decode(service.getParentChain(bId, group)); expect(jsonList, isList); final List chainElements = jsonList! as List; - final List expectedChain = - elementB.debugGetDiagnosticChain().reversed.toList(); + final List expectedChain = elementB.debugGetDiagnosticChain().reversed.toList(); // Sanity check that the chain goes back to the root. expect(expectedChain.first, tester.binding.rootElement); expect(chainElements.length, equals(expectedChain.length)); for (int i = 0; i < expectedChain.length; i += 1) { expect(chainElements[i], isMap); - final Map chainNode = - chainElements[i]! as Map; + final Map chainNode = chainElements[i]! as Map; final Element element = expectedChain[i]; expect(chainNode['node'], isMap); - final Map jsonNode = - chainNode['node']! as Map; - expect(service.toObject(jsonNode['valueId']! as String), - equals(element)); + final Map jsonNode = chainNode['node']! as Map; + expect(service.toObject(jsonNode['valueId']! as String), equals(element)); expect(chainNode['children'], isList); - final List jsonChildren = - chainNode['children']! as List; + final List jsonChildren = chainNode['children']! as List; final List childrenElements = []; element.visitChildren(childrenElements.add); expect(jsonChildren.length, equals(childrenElements.length)); if (i + 1 == expectedChain.length) { expect(chainNode['childIndex'], isNull); } else { - expect(chainNode['childIndex'], - equals(childrenElements.indexOf(expectedChain[i + 1]))); + expect( + chainNode['childIndex'], + equals(childrenElements.indexOf(expectedChain[i + 1])), + ); } for (int j = 0; j < childrenElements.length; j += 1) { expect(jsonChildren[j], isMap); - final Map childJson = - jsonChildren[j]! as Map; - expect(service.toObject(childJson['valueId']! as String), - equals(childrenElements[j])); + final Map childJson = jsonChildren[j]! as Map; + expect( + service.toObject(childJson['valueId']! as String), + equals(childrenElements[j]), + ); } } }); test('getProperties', () { - const Diagnosticable diagnosticable = - Text('a', textDirection: TextDirection.ltr); + const Diagnosticable diagnosticable = Text('a', textDirection: TextDirection.ltr); const String group = 'group'; service.disposeAllGroups(); final String id = service.toId(diagnosticable, group)!; @@ -1594,10 +1568,11 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(properties, isNotEmpty); expect(propertiesJson.length, equals(properties.length)); for (int i = 0; i < propertiesJson.length; ++i) { - final Map propertyJson = - propertiesJson[i]! as Map; - expect(service.toObject(propertyJson['valueId'] as String?), - equals(properties[i].value)); + final Map propertyJson = propertiesJson[i]! as Map; + expect( + service.toObject(propertyJson['valueId'] as String?), + equals(properties[i].value), + ); } }); @@ -1616,197 +1591,190 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(children.length, equals(3)); expect(propertiesJson.length, equals(children.length)); for (int i = 0; i < propertiesJson.length; ++i) { - final Map propertyJson = - propertiesJson[i]! as Map; - expect(service.toObject(propertyJson['valueId']! as String), - equals(children[i].value)); + final Map propertyJson = propertiesJson[i]! as Map; + expect(service.toObject(propertyJson['valueId']! as String), equals(children[i].value)); } }); }); - group('Requires flag --track-widget-creation', () { + group( + 'Requires flag --track-widget-creation', + () { + late final String pubRootTest; - late final String pubRootTest; + setUpAll(() { + pubRootTest = generateTestPubRootDirectory(service); + }); - setUpAll(() { - pubRootTest = generateTestPubRootDirectory(service); - }); - - setUp(() { - service.disposeAllGroups(); - service.resetPubRootDirectories(); - }); - - group('addPubRootDirectories', () { - testWidgets( - 'does not have createdByLocalProject when there are no pubRootDirectories', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); - - final Map jsonObject = - json.decode(service.getSelectedWidget(null, 'my-group')) - as Map; - final Map creationLocation = - jsonObject['creationLocation']! as Map; - - expect(creationLocation, isNotNull); - final String fileA = creationLocation['file']! as String; - expect(fileA, endsWith('widget_inspector_test.dart')); - expect(jsonObject, isNot(contains('createdByLocalProject'))); - }, - ); - - testWidgets( - 'has createdByLocalProject when the element is part of the pubRootDirectory', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - - service.addPubRootDirectories([pubRootTest]); - - service.setSelection(elementA, 'my-group'); - expect( - json.decode(service.getSelectedWidget(null, 'my-group')), - contains('createdByLocalProject'), - ); - }, - ); - - testWidgets( - 'does not have createdByLocalProject when widget package directory is a suffix of a pubRootDirectory', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); - - service.addPubRootDirectories(['/invalid/$pubRootTest']); - expect( - json.decode(service.getSelectedWidget(null, 'my-group')), - isNot(contains('createdByLocalProject')), - ); - }, - ); - - testWidgets( - 'has createdByLocalProject when the pubRootDirectory is prefixed with file://', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); - - service.addPubRootDirectories(['file://$pubRootTest']); - expect( - json.decode(service.getSelectedWidget(null, 'my-group')), - contains('createdByLocalProject'), - ); - }, - ); - - testWidgets( - 'does not have createdByLocalProject when thePubRootDirectory has a different suffix', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); - - service.addPubRootDirectories(['$pubRootTest/different']); - expect( - json.decode(service.getSelectedWidget(null, 'my-group')), - isNot(contains('createdByLocalProject')), - ); - }, - ); - - testWidgets( - 'has createdByLocalProject even if another pubRootDirectory does not match', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); - - service.addPubRootDirectories([ - '/invalid/$pubRootTest', - pubRootTest, - ]); - expect( - json.decode(service.getSelectedWidget(null, 'my-group')), - contains('createdByLocalProject'), - ); - }, - ); - - testWidgets( - 'widget is part of core framework and is the child of a widget in the package pubRootDirectories', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - final Element richText = find - .descendant( - of: find.text('a'), - matching: find.byType(RichText), - ) - .evaluate() - .first; - service.setSelection(richText, 'my-group'); - service.addPubRootDirectories([pubRootTest]); - - final Map jsonObject = - json.decode(service.getSelectedWidget(null, 'my-group')) - as Map; - expect(jsonObject, isNot(contains('createdByLocalProject'))); - final Map creationLocation = - jsonObject['creationLocation']! as Map; - expect(creationLocation, isNotNull); - // This RichText widget is created by the build method of the Text widget - // thus the creation location is in text.dart not basic.dart - final List pathSegmentsFramework = - Uri.parse(creationLocation['file']! as String).pathSegments; - expect( - pathSegmentsFramework.join('/'), - endsWith('/flutter/lib/src/widgets/text.dart'), - ); - - // Strip off /src/widgets/text.dart. - final String pubRootFramework = - '/${pathSegmentsFramework.take(pathSegmentsFramework.length - 3).join('/')}'; - service.resetPubRootDirectories(); - service.addPubRootDirectories([pubRootFramework]); - expect( - json.decode(service.getSelectedWidget(null, 'my-group')), - contains('createdByLocalProject'), - ); - service.setSelection(elementA, 'my-group'); - expect( - json.decode(service.getSelectedWidget(null, 'my-group')), - isNot(contains('createdByLocalProject')), - ); - - service - .setPubRootDirectories([pubRootFramework, pubRootTest]); - service.setSelection(elementA, 'my-group'); - expect( - json.decode(service.getSelectedWidget(null, 'my-group')), - contains('createdByLocalProject'), - ); - service.setSelection(richText, 'my-group'); - expect( - json.decode(service.getSelectedWidget(null, 'my-group')), - contains('createdByLocalProject'), - ); - }, - ); - }); - - group('createdByLocalProject', () { setUp(() { + service.disposeAllGroups(); service.resetPubRootDirectories(); }); - testWidgets( - 'reacts to add and removing pubRootDirectories', - (WidgetTester tester) async { + group('addPubRootDirectories', () { + testWidgets( + 'does not have createdByLocalProject when there are no pubRootDirectories', + (WidgetTester tester) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + + final Map jsonObject = + json.decode(service.getSelectedWidget(null, 'my-group')) + as Map; + final Map creationLocation = + jsonObject['creationLocation']! as Map; + + expect(creationLocation, isNotNull); + final String fileA = creationLocation['file']! as String; + expect(fileA, endsWith('widget_inspector_test.dart')); + expect(jsonObject, isNot(contains('createdByLocalProject'))); + }, + ); + + testWidgets( + 'has createdByLocalProject when the element is part of the pubRootDirectory', + (WidgetTester tester) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + + service.addPubRootDirectories([pubRootTest]); + + service.setSelection(elementA, 'my-group'); + expect( + json.decode(service.getSelectedWidget(null, 'my-group')), + contains('createdByLocalProject'), + ); + }, + ); + + testWidgets( + 'does not have createdByLocalProject when widget package directory is a suffix of a pubRootDirectory', + (WidgetTester tester) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + + service.addPubRootDirectories(['/invalid/$pubRootTest']); + expect( + json.decode(service.getSelectedWidget(null, 'my-group')), + isNot(contains('createdByLocalProject')), + ); + }, + ); + + testWidgets( + 'has createdByLocalProject when the pubRootDirectory is prefixed with file://', + (WidgetTester tester) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + + service.addPubRootDirectories(['file://$pubRootTest']); + expect( + json.decode(service.getSelectedWidget(null, 'my-group')), + contains('createdByLocalProject'), + ); + }, + ); + + testWidgets( + 'does not have createdByLocalProject when thePubRootDirectory has a different suffix', + (WidgetTester tester) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + + service.addPubRootDirectories(['$pubRootTest/different']); + expect( + json.decode(service.getSelectedWidget(null, 'my-group')), + isNot(contains('createdByLocalProject')), + ); + }, + ); + + testWidgets( + 'has createdByLocalProject even if another pubRootDirectory does not match', + (WidgetTester tester) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + + service.addPubRootDirectories(['/invalid/$pubRootTest', pubRootTest]); + expect( + json.decode(service.getSelectedWidget(null, 'my-group')), + contains('createdByLocalProject'), + ); + }, + ); + + testWidgets( + 'widget is part of core framework and is the child of a widget in the package pubRootDirectories', + (WidgetTester tester) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + final Element richText = + find + .descendant(of: find.text('a'), matching: find.byType(RichText)) + .evaluate() + .first; + service.setSelection(richText, 'my-group'); + service.addPubRootDirectories([pubRootTest]); + + final Map jsonObject = + json.decode(service.getSelectedWidget(null, 'my-group')) + as Map; + expect(jsonObject, isNot(contains('createdByLocalProject'))); + final Map creationLocation = + jsonObject['creationLocation']! as Map; + expect(creationLocation, isNotNull); + // This RichText widget is created by the build method of the Text widget + // thus the creation location is in text.dart not basic.dart + final List pathSegmentsFramework = + Uri.parse(creationLocation['file']! as String).pathSegments; + expect( + pathSegmentsFramework.join('/'), + endsWith('/flutter/lib/src/widgets/text.dart'), + ); + + // Strip off /src/widgets/text.dart. + final String pubRootFramework = + '/${pathSegmentsFramework.take(pathSegmentsFramework.length - 3).join('/')}'; + service.resetPubRootDirectories(); + service.addPubRootDirectories([pubRootFramework]); + expect( + json.decode(service.getSelectedWidget(null, 'my-group')), + contains('createdByLocalProject'), + ); + service.setSelection(elementA, 'my-group'); + expect( + json.decode(service.getSelectedWidget(null, 'my-group')), + isNot(contains('createdByLocalProject')), + ); + + service.setPubRootDirectories([pubRootFramework, pubRootTest]); + service.setSelection(elementA, 'my-group'); + expect( + json.decode(service.getSelectedWidget(null, 'my-group')), + contains('createdByLocalProject'), + ); + service.setSelection(richText, 'my-group'); + expect( + json.decode(service.getSelectedWidget(null, 'my-group')), + contains('createdByLocalProject'), + ); + }, + ); + }); + + group('createdByLocalProject', () { + setUp(() { + service.resetPubRootDirectories(); + }); + + testWidgets('reacts to add and removing pubRootDirectories', ( + WidgetTester tester, + ) async { await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); @@ -1829,12 +1797,11 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { json.decode(service.getSelectedWidget(null, 'my-group')), isNot(contains('createdByLocalProject')), ); - }, - ); + }); - testWidgets( - 'does not match when the package directory does not match', - (WidgetTester tester) async { + testWidgets('does not match when the package directory does not match', ( + WidgetTester tester, + ) async { await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); service.setSelection(elementA, 'my-group'); @@ -1847,87 +1814,70 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { json.decode(service.getSelectedWidget(null, 'my-group')), isNot(contains('createdByLocalProject')), ); - }, - ); + }); - testWidgets( - 'has createdByLocalProject when the pubRootDirectory is prefixed with file://', - (WidgetTester tester) async { + testWidgets( + 'has createdByLocalProject when the pubRootDirectory is prefixed with file://', + (WidgetTester tester) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + + service.addPubRootDirectories(['file://$pubRootTest']); + expect( + json.decode(service.getSelectedWidget(null, 'my-group')), + contains('createdByLocalProject'), + ); + }, + ); + + testWidgets('can handle consecutive calls to add', (WidgetTester tester) async { await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); service.setSelection(elementA, 'my-group'); - service.addPubRootDirectories(['file://$pubRootTest']); + service.addPubRootDirectories([pubRootTest]); + service.addPubRootDirectories(['/invalid/$pubRootTest']); expect( json.decode(service.getSelectedWidget(null, 'my-group')), contains('createdByLocalProject'), ); - }, - ); - - testWidgets( - 'can handle consecutive calls to add', - (WidgetTester tester) async { + }); + testWidgets('can handle removing an unrelated pubRootDirectory', ( + WidgetTester tester, + ) async { await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); service.setSelection(elementA, 'my-group'); - service.addPubRootDirectories([ - pubRootTest, - ]); - service.addPubRootDirectories([ - '/invalid/$pubRootTest', - ]); + service.addPubRootDirectories([pubRootTest, '/invalid/$pubRootTest']); expect( json.decode(service.getSelectedWidget(null, 'my-group')), contains('createdByLocalProject'), ); - }, - ); - testWidgets( - 'can handle removing an unrelated pubRootDirectory', - (WidgetTester tester) async { + + service.removePubRootDirectories(['/invalid/$pubRootTest']); + expect( + json.decode(service.getSelectedWidget(null, 'my-group')), + contains('createdByLocalProject'), + ); + }); + + testWidgets('can handle parent widget being part of a separate package', ( + WidgetTester tester, + ) async { await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); - - service.addPubRootDirectories([ - pubRootTest, - '/invalid/$pubRootTest', - ]); - expect( - json.decode(service.getSelectedWidget(null, 'my-group')), - contains('createdByLocalProject'), - ); - - service.removePubRootDirectories([ - '/invalid/$pubRootTest', - ]); - expect( - json.decode(service.getSelectedWidget(null, 'my-group')), - contains('createdByLocalProject'), - ); - }, - ); - - testWidgets( - 'can handle parent widget being part of a separate package', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - final Element richText = find - .descendant( - of: find.text('a'), - matching: find.byType(RichText), - ) - .evaluate() - .first; + final Element richText = + find + .descendant(of: find.text('a'), matching: find.byType(RichText)) + .evaluate() + .first; service.setSelection(richText, 'my-group'); service.addPubRootDirectories([pubRootTest]); final Map jsonObject = - json.decode(service.getSelectedWidget(null, 'my-group')) - as Map; + json.decode(service.getSelectedWidget(null, 'my-group')) as Map; expect(jsonObject, isNot(contains('createdByLocalProject'))); final Map creationLocation = jsonObject['creationLocation']! as Map; @@ -1957,8 +1907,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { ); service.resetPubRootDirectories(); - service.addPubRootDirectories( - [pubRootFramework, pubRootTest]); + service.addPubRootDirectories([pubRootFramework, pubRootTest]); service.setSelection(elementA, 'my-group'); expect( json.decode(service.getSelectedWidget(null, 'my-group')), @@ -1969,889 +1918,883 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { json.decode(service.getSelectedWidget(null, 'my-group')), contains('createdByLocalProject'), ); - }, - ); - }); - - testWidgets('creationLocation', (WidgetTester tester) async { - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Stack( - children: [ - const Text('a'), - const Text('b', textDirection: TextDirection.ltr), - 'c'.text(), - ], - ), - ), - ); - final Element elementA = find.text('a').evaluate().first; - final Element elementB = find.text('b').evaluate().first; - final Element elementC = find.text('c').evaluate().first; - - service.disposeAllGroups(); - service.resetPubRootDirectories(); - service.setSelection(elementA, 'my-group'); - final Map jsonA = - json.decode(service.getSelectedWidget(null, 'my-group')) - as Map; - final Map creationLocationA = - jsonA['creationLocation']! as Map; - expect(creationLocationA, isNotNull); - final String fileA = creationLocationA['file']! as String; - final int lineA = creationLocationA['line']! as int; - final int columnA = creationLocationA['column']! as int; - final String nameA = creationLocationA['name']! as String; - expect(nameA, equals('Text')); - - service.setSelection(elementB, 'my-group'); - final Map jsonB = - json.decode(service.getSelectedWidget(null, 'my-group')) - as Map; - final Map creationLocationB = - jsonB['creationLocation']! as Map; - expect(creationLocationB, isNotNull); - final String fileB = creationLocationB['file']! as String; - final int lineB = creationLocationB['line']! as int; - final int columnB = creationLocationB['column']! as int; - final String? nameB = creationLocationB['name'] as String?; - expect(nameB, equals('Text')); - - service.setSelection(elementC, 'my-group'); - final Map jsonC = - json.decode(service.getSelectedWidget(null, 'my-group')) - as Map; - final Map creationLocationC = - jsonC['creationLocation']! as Map; - expect(creationLocationC, isNotNull); - final String fileC = creationLocationC['file']! as String; - final int lineC = creationLocationC['line']! as int; - final int columnC = creationLocationC['column']! as int; - final String? nameC = creationLocationC['name'] as String?; - expect(nameC, equals('TextFromString|text')); - - expect(fileA, endsWith('widget_inspector_test.dart')); - expect(fileA, equals(fileB)); - expect(fileA, equals(fileC)); - // We don't hardcode the actual lines the widgets are created on as that - // would make this test fragile. - expect(lineA + 1, equals(lineB)); - expect(lineB + 1, equals(lineC)); - // Column numbers are more stable than line numbers. - expect(columnA, equals(25)); - expect(columnA, equals(columnB)); - expect(columnC, equals(23)); - }); - - testWidgets('setSelection notifiers for an Element', - (WidgetTester tester) async { - await tester.pumpWidget( - const Directionality( - textDirection: TextDirection.ltr, - child: Stack( - children: [ - Text('a'), - Text('b', textDirection: TextDirection.ltr), - Text('c', textDirection: TextDirection.ltr), - ], - ), - ), - ); - final Element elementA = find.text('a').evaluate().first; - - service.disposeAllGroups(); - - setupDefaultPubRootDirectory(service); - - // Select the widget - service.setSelection(elementA, 'my-group'); - - // ensure that developer.inspect was called on the widget - final List objectsInspected = service.inspectedObjects(); - expect(objectsInspected, equals([elementA])); - - // ensure that a navigate event was sent for the element - final List> navigateEventsPosted = - service.dispatchedEvents( - 'navigate', - stream: 'ToolEvent', - ); - expect(navigateEventsPosted.length, equals(1)); - final Map event = navigateEventsPosted[0]; - final String file = event['fileUri']! as String; - final int line = event['line']! as int; - final int column = event['column']! as int; - expect(file, endsWith('widget_inspector_test.dart')); - // We don't hardcode the actual lines the widgets are created on as that - // would make this test fragile. - expect(line, isNotNull); - // Column numbers are more stable than line numbers. - expect(column, equals(19)); - }); - - testWidgets('setSelection notifiers for a RenderObject', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - - service.disposeAllGroups(); - - setupDefaultPubRootDirectory(service); - - // Select the render object for the widget. - service.setSelection(elementA.renderObject, 'my-group'); - - // ensure that developer.inspect was called on the widget - final List objectsInspected = service.inspectedObjects(); - expect( - objectsInspected, equals([elementA.renderObject])); - - // ensure that a navigate event was sent for the renderObject - final List> navigateEventsPosted = - service.dispatchedEvents( - 'navigate', - stream: 'ToolEvent', - ); - expect(navigateEventsPosted.length, equals(1)); - final Map event = navigateEventsPosted[0]; - final String file = event['fileUri']! as String; - final int line = event['line']! as int; - final int column = event['column']! as int; - expect(file, endsWith('widget_inspector_test.dart')); - // We don't hardcode the actual lines the widgets are created on as that - // would make this test fragile. - expect(line, isNotNull); - // Column numbers are more stable than line numbers. - expect(column, equals(15)); - }); - - - group('Widget Tree APIs', () { - - /// Gets the widget using [WidgetInspectorServiceExtensions.getSelectedWidget] - /// for the given [element]. - Future> selectedWidgetResponseForElement( - Element element) async { - service - ..disposeAllGroups() - ..resetPubRootDirectories() - ..setSelection(element, 'my-group'); - - return (await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ))! as Map; - } - - /// Verifies the creation location is expected for the given - /// [responseJson]. - Map verifyAndReturnCreationLocation( - Map responseJson) { - final Map creationLocation = - responseJson['creationLocation']! as Map; - expect(creationLocation, isNotNull); - return creationLocation; - } - - /// Verifies the test file is expected for the given - /// [creationLocation]. - String verifyAndReturnTestFile( - Map creationLocation) { - final String testFile = creationLocation['file']! as String; - expect(testFile, endsWith('widget_inspector_test.dart')); - return testFile; - } - - /// Adds a pub root directory for the given [testFile]. - void addPubRootDirectoryFor(String testFile) { - final List segments = Uri.parse(testFile).pathSegments; - // Strip a couple subdirectories away to generate a plausible pub - // root directory. - final String pubRootTest = - '/${segments.take(segments.length - 2).join('/')}'; - service - ..resetPubRootDirectories() - ..addPubRootDirectories([pubRootTest]); - } - - /// Gets the children nodes from the JSON response. - List childrenFromJsonResponse(Map json) { - return (json['children'] as List?) ?? []; - } - - /// Gets the children nodes using a call to - /// [WidgetInspectorServiceExtensions.getChildrenSummaryTree]. - Future> childrenFromGetChildrenSummaryTree( - String valueId, String group) async { - return (await service.testExtension( - WidgetInspectorServiceExtensions.getChildrenSummaryTree.name, - {'arg': valueId, 'objectGroup': group}, - ))! as List; - } - - /// Returns whether the child was created by the local project. - bool wasCreatedByLocalProject(Map childJson) { - return childJson['createdByLocalProject'] == true; - } - - /// Returns whether the child is missing the "type" field. - /// - /// This should always be true for nodes in the widget tree without - /// full details. - bool isMissingType(Map childJson) { - return childJson['type'] == null; - } - - /// Returns whether the child has a description matching [description]. - bool hasDescription( - Map childJson, { - required String description, - }) { - return childJson['description'] == description; - } - - /// Returns whether the child has a text preview matching [preview]. - bool hasTextPreview( - Map childJson, { - required String preview, - }) { - return childJson['textPreview'] == preview; - } - - /// Verifies that the children from the JSON response are identical to - /// those from [WidgetInspectorServiceExtensions.getChildrenSummaryTree]. - Future verifyChildrenMatchOtherApi(Map jsonResponse, - {required String group, bool checkForPreviews = false}) async { - List children = childrenFromJsonResponse(jsonResponse); - List childrenFromOtherApi = - await childrenFromGetChildrenSummaryTree( - jsonResponse['valueId']! as String, group); - - // Verify that the number of children are the same, - expect(children.length, equals(1)); - expect(children.length, equals(childrenFromOtherApi.length)); - - // Get the first child. - Map child = - children[0]! as Map; - Map childFromOtherApi = - childrenFromOtherApi[0]! as Map; - - // Verify the first child is the same. - expect(child['description'], startsWith('Directionality')); - expect(child['description'], equals(childFromOtherApi['description'])); - expect(child['valueId'], equals(childFromOtherApi['valueId'])); - - // Get the first child's children. - children = childrenFromJsonResponse(child); - childrenFromOtherApi = await childrenFromGetChildrenSummaryTree( - childFromOtherApi['valueId']! as String, group); - - // Verify the first child's children are the same length. - expect(children.length, equals(1)); - expect(children.length, equals(childrenFromOtherApi.length)); - - // Get the first child's first child. - child = children[0]! as Map; - childFromOtherApi = - childrenFromOtherApi[0]! as Map; - - // Verify the first child's first child is the same. - expect(child['description'], startsWith('Stack')); - expect(child['description'], - equals(childFromOtherApi['description'])); - expect(child['valueId'], equals(childFromOtherApi['valueId'])); - - // Get the first child's first child's children. - children = childrenFromJsonResponse(child); - childrenFromOtherApi = await childrenFromGetChildrenSummaryTree( - childFromOtherApi['valueId']! as String, group); - - // Verify the first child's first child's children are the same - // length. - expect(children.length, equals(3)); - expect(children.length, equals(childrenFromOtherApi.length)); - - // Get the first child's first child's third child. - child = children[2]! as Map; - childFromOtherApi = - childrenFromOtherApi[2]! as Map; - - // Verify the first child's first child's third child are the same. - expect(child['description'], startsWith('Text')); - expect(child['description'], childFromOtherApi['description']); - expect(child['valueId'], equals(childFromOtherApi['valueId'])); - - // If the tree was requested with previews, then check that the - // child has the `textPreview` key: - if (checkForPreviews) { - expect(hasTextPreview(child, preview: 'c'), isTrue); - } - - // Get the first child's first child's third child's children. - children = childrenFromJsonResponse(child); - childrenFromOtherApi = await childrenFromGetChildrenSummaryTree( - childFromOtherApi['valueId']! as String, group); - - // Verify first child's first child's third child's has no children. - expect(children.length, equals(0)); - expect(childrenFromOtherApi.length, equals(children.length)); - } - - bool allChildrenSatisfyCondition(Map treeRoot, - { - required bool Function(Map child) condition, - }) { - final List children = childrenFromJsonResponse(treeRoot); - for (int childIdx = 0; childIdx < children.length; childIdx++) { - final Map child = - children[childIdx]! as Map; - if (!condition(child)) { - return false; - } - if (!allChildrenSatisfyCondition(child, condition: condition)) { - return false; - } - } - - return true; - } - - bool oneChildSatisfiesCondition(Map treeRoot, - { - required bool Function(Map child) condition, - }) { - final List children = childrenFromJsonResponse(treeRoot); - for (int childIdx = 0; childIdx < children.length; childIdx++) { - final Map child = - children[childIdx]! as Map; - if (condition(child)) { - return true; - } - if (oneChildSatisfiesCondition(child, condition: condition)) { - return true; - } - } - - return false; - } - - /// Determines which API to call to get the summary tree. - String getExtensionApiToCall({ - required bool useGetRootWidgetTreeApi, - required bool withPreviews, - }) { - if (useGetRootWidgetTreeApi) { - return WidgetInspectorServiceExtensions.getRootWidgetTree.name; - } else if (withPreviews) { - return WidgetInspectorServiceExtensions - .getRootWidgetSummaryTreeWithPreviews.name; - } else { - return WidgetInspectorServiceExtensions - .getRootWidgetSummaryTree.name; - } - } - - /// Determines which parameters to use for the summary tree API call. - Map getExtensionApiParams({ - required bool useGetRootWidgetTreeApi, - required String groupName, - required bool withPreviews, - }) { - if (useGetRootWidgetTreeApi) { - return { - 'groupName': groupName, - 'isSummaryTree': 'true', - 'withPreviews': '$withPreviews', - }; - } else if (withPreviews) { - return {'groupName': groupName}; - } else { - return {'objectGroup': groupName}; - } - } - - for (final bool useGetRootWidgetTreeApi in [true, false]) { - final String extensionApiNoPreviews = getExtensionApiToCall( - useGetRootWidgetTreeApi: useGetRootWidgetTreeApi, - withPreviews: false, - ); - final String extensionApiWithPreviews = getExtensionApiToCall( - useGetRootWidgetTreeApi: useGetRootWidgetTreeApi, - withPreviews: true, - ); - - testWidgets( - 'summary tree using ext.flutter.inspector.$extensionApiNoPreviews', - (WidgetTester tester) async { - const String group = 'test-group'; - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - final Map jsonA = - await selectedWidgetResponseForElement(elementA); - - service.resetPubRootDirectories(); - - Map rootJson = (await service.testExtension( - extensionApiNoPreviews, - getExtensionApiParams( - useGetRootWidgetTreeApi: useGetRootWidgetTreeApi, - groupName: group, - withPreviews: false, - ), - ))! as Map; - - // We haven't yet properly specified which directories are summary tree - // directories so we get an empty tree other than the root that is always - // included. - final Object? rootWidget = - service.toObject(rootJson['valueId']! as String); - expect(rootWidget, equals(WidgetsBinding.instance.rootElement)); - final List childrenJson = - rootJson['children']! as List; - // There are no summary tree children. - expect(childrenJson.length, equals(0)); - - final Map creationLocation = - verifyAndReturnCreationLocation(jsonA); - final String testFile = verifyAndReturnTestFile(creationLocation); - addPubRootDirectoryFor(testFile); - - rootJson = (await service.testExtension( - extensionApiNoPreviews, - getExtensionApiParams( - useGetRootWidgetTreeApi: useGetRootWidgetTreeApi, - groupName: group, - withPreviews: false, - ), - ))! as Map; - - expect( - allChildrenSatisfyCondition(rootJson, - condition: wasCreatedByLocalProject, - ), - isTrue, - ); - await verifyChildrenMatchOtherApi(rootJson, group: group); }); + }); - testWidgets( + testWidgets('creationLocation', (WidgetTester tester) async { + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: Stack( + children: [ + const Text('a'), + const Text('b', textDirection: TextDirection.ltr), + 'c'.text(), + ], + ), + ), + ); + final Element elementA = find.text('a').evaluate().first; + final Element elementB = find.text('b').evaluate().first; + final Element elementC = find.text('c').evaluate().first; + + service.disposeAllGroups(); + service.resetPubRootDirectories(); + service.setSelection(elementA, 'my-group'); + final Map jsonA = + json.decode(service.getSelectedWidget(null, 'my-group')) as Map; + final Map creationLocationA = + jsonA['creationLocation']! as Map; + expect(creationLocationA, isNotNull); + final String fileA = creationLocationA['file']! as String; + final int lineA = creationLocationA['line']! as int; + final int columnA = creationLocationA['column']! as int; + final String nameA = creationLocationA['name']! as String; + expect(nameA, equals('Text')); + + service.setSelection(elementB, 'my-group'); + final Map jsonB = + json.decode(service.getSelectedWidget(null, 'my-group')) as Map; + final Map creationLocationB = + jsonB['creationLocation']! as Map; + expect(creationLocationB, isNotNull); + final String fileB = creationLocationB['file']! as String; + final int lineB = creationLocationB['line']! as int; + final int columnB = creationLocationB['column']! as int; + final String? nameB = creationLocationB['name'] as String?; + expect(nameB, equals('Text')); + + service.setSelection(elementC, 'my-group'); + final Map jsonC = + json.decode(service.getSelectedWidget(null, 'my-group')) as Map; + final Map creationLocationC = + jsonC['creationLocation']! as Map; + expect(creationLocationC, isNotNull); + final String fileC = creationLocationC['file']! as String; + final int lineC = creationLocationC['line']! as int; + final int columnC = creationLocationC['column']! as int; + final String? nameC = creationLocationC['name'] as String?; + expect(nameC, equals('TextFromString|text')); + + expect(fileA, endsWith('widget_inspector_test.dart')); + expect(fileA, equals(fileB)); + expect(fileA, equals(fileC)); + // We don't hardcode the actual lines the widgets are created on as that + // would make this test fragile. + expect(lineA + 1, equals(lineB)); + expect(lineB + 1, equals(lineC)); + // Column numbers are more stable than line numbers. + expect(columnA, equals(27)); + expect(columnA, equals(columnB)); + expect(columnC, equals(25)); + }); + + testWidgets('setSelection notifiers for an Element', (WidgetTester tester) async { + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: Stack( + children: [ + Text('a'), + Text('b', textDirection: TextDirection.ltr), + Text('c', textDirection: TextDirection.ltr), + ], + ), + ), + ); + final Element elementA = find.text('a').evaluate().first; + + service.disposeAllGroups(); + + setupDefaultPubRootDirectory(service); + + // Select the widget + service.setSelection(elementA, 'my-group'); + + // ensure that developer.inspect was called on the widget + final List objectsInspected = service.inspectedObjects(); + expect(objectsInspected, equals([elementA])); + + // ensure that a navigate event was sent for the element + final List> navigateEventsPosted = service.dispatchedEvents( + 'navigate', + stream: 'ToolEvent', + ); + expect(navigateEventsPosted.length, equals(1)); + final Map event = navigateEventsPosted[0]; + final String file = event['fileUri']! as String; + final int line = event['line']! as int; + final int column = event['column']! as int; + expect(file, endsWith('widget_inspector_test.dart')); + // We don't hardcode the actual lines the widgets are created on as that + // would make this test fragile. + expect(line, isNotNull); + // Column numbers are more stable than line numbers. + expect(column, equals(21)); + }); + + testWidgets('setSelection notifiers for a RenderObject', (WidgetTester tester) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + + service.disposeAllGroups(); + + setupDefaultPubRootDirectory(service); + + // Select the render object for the widget. + service.setSelection(elementA.renderObject, 'my-group'); + + // ensure that developer.inspect was called on the widget + final List objectsInspected = service.inspectedObjects(); + expect(objectsInspected, equals([elementA.renderObject])); + + // ensure that a navigate event was sent for the renderObject + final List> navigateEventsPosted = service.dispatchedEvents( + 'navigate', + stream: 'ToolEvent', + ); + expect(navigateEventsPosted.length, equals(1)); + final Map event = navigateEventsPosted[0]; + final String file = event['fileUri']! as String; + final int line = event['line']! as int; + final int column = event['column']! as int; + expect(file, endsWith('widget_inspector_test.dart')); + // We don't hardcode the actual lines the widgets are created on as that + // would make this test fragile. + expect(line, isNotNull); + // Column numbers are more stable than line numbers. + expect(column, equals(15)); + }); + + group('Widget Tree APIs', () { + /// Gets the widget using [WidgetInspectorServiceExtensions.getSelectedWidget] + /// for the given [element]. + Future> selectedWidgetResponseForElement(Element element) async { + service + ..disposeAllGroups() + ..resetPubRootDirectories() + ..setSelection(element, 'my-group'); + + return (await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ))! + as Map; + } + + /// Verifies the creation location is expected for the given + /// [responseJson]. + Map verifyAndReturnCreationLocation( + Map responseJson, + ) { + final Map creationLocation = + responseJson['creationLocation']! as Map; + expect(creationLocation, isNotNull); + return creationLocation; + } + + /// Verifies the test file is expected for the given + /// [creationLocation]. + String verifyAndReturnTestFile(Map creationLocation) { + final String testFile = creationLocation['file']! as String; + expect(testFile, endsWith('widget_inspector_test.dart')); + return testFile; + } + + /// Adds a pub root directory for the given [testFile]. + void addPubRootDirectoryFor(String testFile) { + final List segments = Uri.parse(testFile).pathSegments; + // Strip a couple subdirectories away to generate a plausible pub + // root directory. + final String pubRootTest = '/${segments.take(segments.length - 2).join('/')}'; + service + ..resetPubRootDirectories() + ..addPubRootDirectories([pubRootTest]); + } + + /// Gets the children nodes from the JSON response. + List childrenFromJsonResponse(Map json) { + return (json['children'] as List?) ?? []; + } + + /// Gets the children nodes using a call to + /// [WidgetInspectorServiceExtensions.getChildrenSummaryTree]. + Future> childrenFromGetChildrenSummaryTree( + String valueId, + String group, + ) async { + return (await service.testExtension( + WidgetInspectorServiceExtensions.getChildrenSummaryTree.name, + {'arg': valueId, 'objectGroup': group}, + ))! + as List; + } + + /// Returns whether the child was created by the local project. + bool wasCreatedByLocalProject(Map childJson) { + return childJson['createdByLocalProject'] == true; + } + + /// Returns whether the child is missing the "type" field. + /// + /// This should always be true for nodes in the widget tree without + /// full details. + bool isMissingType(Map childJson) { + return childJson['type'] == null; + } + + /// Returns whether the child has a description matching [description]. + bool hasDescription(Map childJson, {required String description}) { + return childJson['description'] == description; + } + + /// Returns whether the child has a text preview matching [preview]. + bool hasTextPreview(Map childJson, {required String preview}) { + return childJson['textPreview'] == preview; + } + + /// Verifies that the children from the JSON response are identical to + /// those from [WidgetInspectorServiceExtensions.getChildrenSummaryTree]. + Future verifyChildrenMatchOtherApi( + Map jsonResponse, { + required String group, + bool checkForPreviews = false, + }) async { + List children = childrenFromJsonResponse(jsonResponse); + List childrenFromOtherApi = await childrenFromGetChildrenSummaryTree( + jsonResponse['valueId']! as String, + group, + ); + + // Verify that the number of children are the same, + expect(children.length, equals(1)); + expect(children.length, equals(childrenFromOtherApi.length)); + + // Get the first child. + Map child = children[0]! as Map; + Map childFromOtherApi = + childrenFromOtherApi[0]! as Map; + + // Verify the first child is the same. + expect(child['description'], startsWith('Directionality')); + expect(child['description'], equals(childFromOtherApi['description'])); + expect(child['valueId'], equals(childFromOtherApi['valueId'])); + + // Get the first child's children. + children = childrenFromJsonResponse(child); + childrenFromOtherApi = await childrenFromGetChildrenSummaryTree( + childFromOtherApi['valueId']! as String, + group, + ); + + // Verify the first child's children are the same length. + expect(children.length, equals(1)); + expect(children.length, equals(childrenFromOtherApi.length)); + + // Get the first child's first child. + child = children[0]! as Map; + childFromOtherApi = childrenFromOtherApi[0]! as Map; + + // Verify the first child's first child is the same. + expect(child['description'], startsWith('Stack')); + expect(child['description'], equals(childFromOtherApi['description'])); + expect(child['valueId'], equals(childFromOtherApi['valueId'])); + + // Get the first child's first child's children. + children = childrenFromJsonResponse(child); + childrenFromOtherApi = await childrenFromGetChildrenSummaryTree( + childFromOtherApi['valueId']! as String, + group, + ); + + // Verify the first child's first child's children are the same + // length. + expect(children.length, equals(3)); + expect(children.length, equals(childrenFromOtherApi.length)); + + // Get the first child's first child's third child. + child = children[2]! as Map; + childFromOtherApi = childrenFromOtherApi[2]! as Map; + + // Verify the first child's first child's third child are the same. + expect(child['description'], startsWith('Text')); + expect(child['description'], childFromOtherApi['description']); + expect(child['valueId'], equals(childFromOtherApi['valueId'])); + + // If the tree was requested with previews, then check that the + // child has the `textPreview` key: + if (checkForPreviews) { + expect(hasTextPreview(child, preview: 'c'), isTrue); + } + + // Get the first child's first child's third child's children. + children = childrenFromJsonResponse(child); + childrenFromOtherApi = await childrenFromGetChildrenSummaryTree( + childFromOtherApi['valueId']! as String, + group, + ); + + // Verify first child's first child's third child's has no children. + expect(children.length, equals(0)); + expect(childrenFromOtherApi.length, equals(children.length)); + } + + bool allChildrenSatisfyCondition( + Map treeRoot, { + required bool Function(Map child) condition, + }) { + final List children = childrenFromJsonResponse(treeRoot); + for (int childIdx = 0; childIdx < children.length; childIdx++) { + final Map child = children[childIdx]! as Map; + if (!condition(child)) { + return false; + } + if (!allChildrenSatisfyCondition(child, condition: condition)) { + return false; + } + } + + return true; + } + + bool oneChildSatisfiesCondition( + Map treeRoot, { + required bool Function(Map child) condition, + }) { + final List children = childrenFromJsonResponse(treeRoot); + for (int childIdx = 0; childIdx < children.length; childIdx++) { + final Map child = children[childIdx]! as Map; + if (condition(child)) { + return true; + } + if (oneChildSatisfiesCondition(child, condition: condition)) { + return true; + } + } + + return false; + } + + /// Determines which API to call to get the summary tree. + String getExtensionApiToCall({ + required bool useGetRootWidgetTreeApi, + required bool withPreviews, + }) { + if (useGetRootWidgetTreeApi) { + return WidgetInspectorServiceExtensions.getRootWidgetTree.name; + } else if (withPreviews) { + return WidgetInspectorServiceExtensions.getRootWidgetSummaryTreeWithPreviews.name; + } else { + return WidgetInspectorServiceExtensions.getRootWidgetSummaryTree.name; + } + } + + /// Determines which parameters to use for the summary tree API call. + Map getExtensionApiParams({ + required bool useGetRootWidgetTreeApi, + required String groupName, + required bool withPreviews, + }) { + if (useGetRootWidgetTreeApi) { + return { + 'groupName': groupName, + 'isSummaryTree': 'true', + 'withPreviews': '$withPreviews', + }; + } else if (withPreviews) { + return {'groupName': groupName}; + } else { + return {'objectGroup': groupName}; + } + } + + for (final bool useGetRootWidgetTreeApi in [true, false]) { + final String extensionApiNoPreviews = getExtensionApiToCall( + useGetRootWidgetTreeApi: useGetRootWidgetTreeApi, + withPreviews: false, + ); + final String extensionApiWithPreviews = getExtensionApiToCall( + useGetRootWidgetTreeApi: useGetRootWidgetTreeApi, + withPreviews: true, + ); + + testWidgets('summary tree using ext.flutter.inspector.$extensionApiNoPreviews', ( + WidgetTester tester, + ) async { + const String group = 'test-group'; + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + final Map jsonA = await selectedWidgetResponseForElement(elementA); + + service.resetPubRootDirectories(); + + Map rootJson = + (await service.testExtension( + extensionApiNoPreviews, + getExtensionApiParams( + useGetRootWidgetTreeApi: useGetRootWidgetTreeApi, + groupName: group, + withPreviews: false, + ), + ))! + as Map; + + // We haven't yet properly specified which directories are summary tree + // directories so we get an empty tree other than the root that is always + // included. + final Object? rootWidget = service.toObject(rootJson['valueId']! as String); + expect(rootWidget, equals(WidgetsBinding.instance.rootElement)); + final List childrenJson = rootJson['children']! as List; + // There are no summary tree children. + expect(childrenJson.length, equals(0)); + + final Map creationLocation = verifyAndReturnCreationLocation( + jsonA, + ); + final String testFile = verifyAndReturnTestFile(creationLocation); + addPubRootDirectoryFor(testFile); + + rootJson = + (await service.testExtension( + extensionApiNoPreviews, + getExtensionApiParams( + useGetRootWidgetTreeApi: useGetRootWidgetTreeApi, + groupName: group, + withPreviews: false, + ), + ))! + as Map; + + expect( + allChildrenSatisfyCondition(rootJson, condition: wasCreatedByLocalProject), + isTrue, + ); + await verifyChildrenMatchOtherApi(rootJson, group: group); + }); + + testWidgets( 'summary tree with previews using ext.flutter.inspector.$extensionApiWithPreviews', - (WidgetTester tester) async { + (WidgetTester tester) async { + const String group = 'test-group'; + + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + final Map jsonA = await selectedWidgetResponseForElement( + elementA, + ); + + final Map creationLocation = verifyAndReturnCreationLocation( + jsonA, + ); + final String testFile = verifyAndReturnTestFile(creationLocation); + addPubRootDirectoryFor(testFile); + + final Map rootJson = + (await service.testExtension( + extensionApiWithPreviews, + getExtensionApiParams( + useGetRootWidgetTreeApi: useGetRootWidgetTreeApi, + groupName: group, + withPreviews: true, + ), + ))! + as Map; + + expect( + allChildrenSatisfyCondition(rootJson, condition: wasCreatedByLocalProject), + isTrue, + ); + await verifyChildrenMatchOtherApi(rootJson, group: group, checkForPreviews: true); + }, + ); + } + + testWidgets('full tree using ext.flutter.inspector.getRootWidgetTree', ( + WidgetTester tester, + ) async { const String group = 'test-group'; await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); - final Map jsonA = - await selectedWidgetResponseForElement(elementA); + final Map jsonA = await selectedWidgetResponseForElement(elementA); - final Map creationLocation = - verifyAndReturnCreationLocation(jsonA); + final Map creationLocation = verifyAndReturnCreationLocation(jsonA); final String testFile = verifyAndReturnTestFile(creationLocation); addPubRootDirectoryFor(testFile); final Map rootJson = (await service.testExtension( - extensionApiWithPreviews, - getExtensionApiParams( - useGetRootWidgetTreeApi: useGetRootWidgetTreeApi, - groupName: group, - withPreviews: true, - ), - ))! as Map; + WidgetInspectorServiceExtensions.getRootWidgetTree.name, + { + 'groupName': group, + 'isSummaryTree': 'false', + 'withPreviews': 'false', + }, + ))! + as Map; + expect(allChildrenSatisfyCondition(rootJson, condition: isMissingType), isFalse); expect( - allChildrenSatisfyCondition(rootJson, - condition: wasCreatedByLocalProject, + allChildrenSatisfyCondition(rootJson, condition: wasCreatedByLocalProject), + isFalse, + ); + expect( + oneChildSatisfiesCondition( + rootJson, + condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + !hasTextPreview(child, preview: 'a'); + }, ), isTrue, ); - await verifyChildrenMatchOtherApi( - rootJson, - group: group, - checkForPreviews: true, + expect( + oneChildSatisfiesCondition( + rootJson, + condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + !hasTextPreview(child, preview: 'b'); + }, + ), + isTrue, + ); + expect( + oneChildSatisfiesCondition( + rootJson, + condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + !hasTextPreview(child, preview: 'c'); + }, + ), + isTrue, ); }); - } - testWidgets( - 'full tree using ext.flutter.inspector.getRootWidgetTree', - (WidgetTester tester) async { - const String group = 'test-group'; + testWidgets('tree without full details using ext.flutter.inspector.getRootWidgetTree', ( + WidgetTester tester, + ) async { + const String group = 'test-group'; - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - final Map jsonA = - await selectedWidgetResponseForElement(elementA); + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + final Map jsonA = await selectedWidgetResponseForElement(elementA); - final Map creationLocation = - verifyAndReturnCreationLocation(jsonA); - final String testFile = verifyAndReturnTestFile(creationLocation); - addPubRootDirectoryFor(testFile); + final Map creationLocation = verifyAndReturnCreationLocation(jsonA); + final String testFile = verifyAndReturnTestFile(creationLocation); + addPubRootDirectoryFor(testFile); - final Map rootJson = (await service.testExtension( - WidgetInspectorServiceExtensions.getRootWidgetTree.name, - { - 'groupName': group, - 'isSummaryTree': 'false', - 'withPreviews': 'false', - }, - ))! as Map; + final Map rootJson = + (await service.testExtension( + WidgetInspectorServiceExtensions.getRootWidgetTree.name, + { + 'groupName': group, + 'isSummaryTree': 'false', + 'withPreviews': 'false', + 'fullDetails': 'false', + }, + ))! + as Map; - expect( - allChildrenSatisfyCondition(rootJson, - condition: isMissingType, - ), - isFalse, - ); - expect( - allChildrenSatisfyCondition(rootJson, - condition: wasCreatedByLocalProject, - ), - isFalse, - ); - expect( - oneChildSatisfiesCondition(rootJson, condition: (Map child) { - return hasDescription(child, description: 'Text') && - wasCreatedByLocalProject(child) && - !hasTextPreview(child, preview: 'a'); - }, - ), - isTrue, - ); - expect( - oneChildSatisfiesCondition(rootJson, condition: (Map child) { - return hasDescription(child, description: 'Text') && - wasCreatedByLocalProject(child) && - !hasTextPreview(child, preview: 'b'); - }, - ), - isTrue, - ); - expect( - oneChildSatisfiesCondition(rootJson, condition: (Map child) { - return hasDescription(child, description: 'Text') && - wasCreatedByLocalProject(child) && - !hasTextPreview(child, preview: 'c'); - }, - ), - isTrue, - ); - }); + expect(allChildrenSatisfyCondition(rootJson, condition: isMissingType), isTrue); + expect( + allChildrenSatisfyCondition(rootJson, condition: wasCreatedByLocalProject), + isFalse, + ); + expect( + oneChildSatisfiesCondition( + rootJson, + condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + !hasTextPreview(child, preview: 'a'); + }, + ), + isTrue, + ); + expect( + oneChildSatisfiesCondition( + rootJson, + condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + !hasTextPreview(child, preview: 'b'); + }, + ), + isTrue, + ); + expect( + oneChildSatisfiesCondition( + rootJson, + condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + !hasTextPreview(child, preview: 'c'); + }, + ), + isTrue, + ); + }); - testWidgets( - 'tree without full details using ext.flutter.inspector.getRootWidgetTree', - (WidgetTester tester) async { - const String group = 'test-group'; + testWidgets('full tree with previews using ext.flutter.inspector.getRootWidgetTree', ( + WidgetTester tester, + ) async { + const String group = 'test-group'; - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - final Map jsonA = - await selectedWidgetResponseForElement(elementA); + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + final Map jsonA = await selectedWidgetResponseForElement(elementA); - final Map creationLocation = - verifyAndReturnCreationLocation(jsonA); - final String testFile = verifyAndReturnTestFile(creationLocation); - addPubRootDirectoryFor(testFile); + final Map creationLocation = verifyAndReturnCreationLocation(jsonA); + final String testFile = verifyAndReturnTestFile(creationLocation); + addPubRootDirectoryFor(testFile); - final Map rootJson = (await service.testExtension( - WidgetInspectorServiceExtensions.getRootWidgetTree.name, - { - 'groupName': group, - 'isSummaryTree': 'false', - 'withPreviews': 'false', - 'fullDetails': 'false', - }, - ))! as Map; + final Map rootJson = + (await service.testExtension( + WidgetInspectorServiceExtensions.getRootWidgetTree.name, + { + 'groupName': group, + 'isSummaryTree': 'false', + 'withPreviews': 'true', + }, + ))! + as Map; - expect( - allChildrenSatisfyCondition(rootJson, - condition: isMissingType, - ), - isTrue, - ); - expect( - allChildrenSatisfyCondition(rootJson, - condition: wasCreatedByLocalProject, - ), - isFalse, - ); - expect( - oneChildSatisfiesCondition(rootJson, condition: (Map child) { - return hasDescription(child, description: 'Text') && - wasCreatedByLocalProject(child) && - !hasTextPreview(child, preview: 'a'); - }, - ), - isTrue, - ); - expect( - oneChildSatisfiesCondition(rootJson, condition: (Map child) { - return hasDescription(child, description: 'Text') && - wasCreatedByLocalProject(child) && - !hasTextPreview(child, preview: 'b'); - }, - ), - isTrue, - ); - expect( - oneChildSatisfiesCondition(rootJson, condition: (Map child) { - return hasDescription(child, description: 'Text') && - wasCreatedByLocalProject(child) && - !hasTextPreview(child, preview: 'c'); - }, - ), - isTrue, - ); - }); + expect(allChildrenSatisfyCondition(rootJson, condition: isMissingType), isFalse); + expect( + allChildrenSatisfyCondition(rootJson, condition: wasCreatedByLocalProject), + isFalse, + ); + expect( + oneChildSatisfiesCondition( + rootJson, + condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + hasTextPreview(child, preview: 'a'); + }, + ), + isTrue, + ); + expect( + oneChildSatisfiesCondition( + rootJson, + condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + hasTextPreview(child, preview: 'b'); + }, + ), + isTrue, + ); + expect( + oneChildSatisfiesCondition( + rootJson, + condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + hasTextPreview(child, preview: 'c'); + }, + ), + isTrue, + ); + }); - testWidgets( - 'full tree with previews using ext.flutter.inspector.getRootWidgetTree', - (WidgetTester tester) async { - const String group = 'test-group'; - - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - final Map jsonA = - await selectedWidgetResponseForElement(elementA); - - final Map creationLocation = - verifyAndReturnCreationLocation(jsonA); - final String testFile = verifyAndReturnTestFile(creationLocation); - addPubRootDirectoryFor(testFile); - - final Map rootJson = (await service.testExtension( - WidgetInspectorServiceExtensions.getRootWidgetTree.name, - { - 'groupName': group, - 'isSummaryTree': 'false', - 'withPreviews': 'true', - }, - ))! as Map; - - expect( - allChildrenSatisfyCondition(rootJson, - condition: isMissingType, - ), - isFalse, - ); - expect( - allChildrenSatisfyCondition(rootJson, - condition: wasCreatedByLocalProject, - ), - isFalse, - ); - expect( - oneChildSatisfiesCondition(rootJson, condition: (Map child) { - return hasDescription(child, description: 'Text') && - wasCreatedByLocalProject(child) && - hasTextPreview(child, preview: 'a'); - }, - ), - isTrue, - ); - expect( - oneChildSatisfiesCondition(rootJson, condition: (Map child) { - return hasDescription(child, description: 'Text') && - wasCreatedByLocalProject(child) && - hasTextPreview(child, preview: 'b'); - }, - ), - isTrue, - ); - expect( - oneChildSatisfiesCondition(rootJson, condition: (Map child) { - return hasDescription(child, description: 'Text') && - wasCreatedByLocalProject(child) && - hasTextPreview(child, preview: 'c'); - }, - ), - isTrue, - ); - }); - - testWidgets( + testWidgets( 'tree without full details and with previews using ext.flutter.inspector.getRootWidgetTree', (WidgetTester tester) async { + const String group = 'test-group'; + + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + final Map jsonA = await selectedWidgetResponseForElement(elementA); + + final Map creationLocation = verifyAndReturnCreationLocation( + jsonA, + ); + final String testFile = verifyAndReturnTestFile(creationLocation); + addPubRootDirectoryFor(testFile); + + final Map rootJson = + (await service.testExtension( + WidgetInspectorServiceExtensions.getRootWidgetTree.name, + { + 'groupName': group, + 'isSummaryTree': 'false', + 'withPreviews': 'true', + 'fullDetails': 'false', + }, + ))! + as Map; + + expect(allChildrenSatisfyCondition(rootJson, condition: isMissingType), isTrue); + expect( + allChildrenSatisfyCondition(rootJson, condition: wasCreatedByLocalProject), + isFalse, + ); + expect( + oneChildSatisfiesCondition( + rootJson, + condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + hasTextPreview(child, preview: 'a'); + }, + ), + isTrue, + ); + expect( + oneChildSatisfiesCondition( + rootJson, + condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + hasTextPreview(child, preview: 'b'); + }, + ), + isTrue, + ); + expect( + oneChildSatisfiesCondition( + rootJson, + condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + hasTextPreview(child, preview: 'c'); + }, + ), + isTrue, + ); + }, + ); + }); + + testWidgets('ext.flutter.inspector.getSelectedSummaryWidget', ( + WidgetTester tester, + ) async { const String group = 'test-group'; await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); - final Map jsonA = - await selectedWidgetResponseForElement(elementA); + + final List children = elementA.debugDescribeChildren(); + expect(children.length, equals(1)); + final DiagnosticsNode richTextDiagnostic = children.first; + + service.disposeAllGroups(); + service.resetPubRootDirectories(); + service.setSelection(elementA, 'my-group'); + final Map jsonA = + (await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ))! + as Map; + service.setSelection(richTextDiagnostic.value, 'my-group'); + + service.resetPubRootDirectories(); + Map? summarySelection = + await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedSummaryWidget.name, + {'objectGroup': group}, + ) + as Map?; + // No summary selection because we haven't set the pub root directories + // yet to indicate what directories are in the summary tree. + expect(summarySelection, isNull); final Map creationLocation = - verifyAndReturnCreationLocation(jsonA); - final String testFile = verifyAndReturnTestFile(creationLocation); - addPubRootDirectoryFor(testFile); - - final Map rootJson = (await service.testExtension( - WidgetInspectorServiceExtensions.getRootWidgetTree.name, - { - 'groupName': group, - 'isSummaryTree': 'false', - 'withPreviews': 'true', - 'fullDetails': 'false', - }, - ))! as Map; + jsonA['creationLocation']! as Map; + expect(creationLocation, isNotNull); + final String testFile = creationLocation['file']! as String; + expect(testFile, endsWith('widget_inspector_test.dart')); + final List segments = Uri.parse(testFile).pathSegments; + // Strip a couple subdirectories away to generate a plausible pub root + // directory. + final String pubRootTest = '/${segments.take(segments.length - 2).join('/')}'; + service.resetPubRootDirectories(); + await service.testExtension( + WidgetInspectorServiceExtensions.addPubRootDirectories.name, + {'arg0': pubRootTest}, + ); + summarySelection = + (await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedSummaryWidget.name, + {'objectGroup': group}, + ))! + as Map; + expect(summarySelection['valueId'], isNotNull); + // We got the Text element instead of the selected RichText element + // because only the RichText element is part of the summary tree. + expect(service.toObject(summarySelection['valueId']! as String), elementA); + // Verify tha the regular getSelectedWidget method still returns + // the RichText object not the Text element. + final Map regularSelection = + (await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ))! + as Map; expect( - allChildrenSatisfyCondition(rootJson, - condition: isMissingType, - ), - isTrue, - ); - expect( - allChildrenSatisfyCondition(rootJson, - condition: wasCreatedByLocalProject, - ), - isFalse, - ); - expect( - oneChildSatisfiesCondition(rootJson, condition: (Map child) { - return hasDescription(child, description: 'Text') && - wasCreatedByLocalProject(child) && - hasTextPreview(child, preview: 'a'); - }, - ), - isTrue, - ); - expect( - oneChildSatisfiesCondition(rootJson, condition: (Map child) { - return hasDescription(child, description: 'Text') && - wasCreatedByLocalProject(child) && - hasTextPreview(child, preview: 'b'); - }, - ), - isTrue, - ); - expect( - oneChildSatisfiesCondition(rootJson, condition: (Map child) { - return hasDescription(child, description: 'Text') && - wasCreatedByLocalProject(child) && - hasTextPreview(child, preview: 'c'); - }, - ), - isTrue, + service.toObject(regularSelection['valueId']! as String), + richTextDiagnostic.value, ); }); - }); - testWidgets('ext.flutter.inspector.getSelectedSummaryWidget', - (WidgetTester tester) async { - const String group = 'test-group'; + testWidgets('ext.flutter.inspector creationLocation', (WidgetTester tester) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + final Element elementB = findElementABC('b'); - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); + service.disposeAllGroups(); + service.resetPubRootDirectories(); + service.setSelection(elementA, 'my-group'); + final Map jsonA = + (await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ))! + as Map; + final Map creationLocationA = + jsonA['creationLocation']! as Map; + expect(creationLocationA, isNotNull); + final String fileA = creationLocationA['file']! as String; + final int lineA = creationLocationA['line']! as int; + final int columnA = creationLocationA['column']! as int; - final List children = - elementA.debugDescribeChildren(); - expect(children.length, equals(1)); - final DiagnosticsNode richTextDiagnostic = children.first; - - service.disposeAllGroups(); - service.resetPubRootDirectories(); - service.setSelection(elementA, 'my-group'); - final Map jsonA = (await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ))! as Map; - service.setSelection(richTextDiagnostic.value, 'my-group'); - - service.resetPubRootDirectories(); - Map? summarySelection = await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedSummaryWidget.name, - {'objectGroup': group}, - ) as Map?; - // No summary selection because we haven't set the pub root directories - // yet to indicate what directories are in the summary tree. - expect(summarySelection, isNull); - - final Map creationLocation = - jsonA['creationLocation']! as Map; - expect(creationLocation, isNotNull); - final String testFile = creationLocation['file']! as String; - expect(testFile, endsWith('widget_inspector_test.dart')); - final List segments = Uri.parse(testFile).pathSegments; - // Strip a couple subdirectories away to generate a plausible pub root - // directory. - final String pubRootTest = - '/${segments.take(segments.length - 2).join('/')}'; - service.resetPubRootDirectories(); - await service.testExtension( - WidgetInspectorServiceExtensions.addPubRootDirectories.name, - {'arg0': pubRootTest}, - ); - - summarySelection = (await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedSummaryWidget.name, - {'objectGroup': group}, - ))! as Map; - expect(summarySelection['valueId'], isNotNull); - // We got the Text element instead of the selected RichText element - // because only the RichText element is part of the summary tree. - expect(service.toObject(summarySelection['valueId']! as String), - elementA); - - // Verify tha the regular getSelectedWidget method still returns - // the RichText object not the Text element. - final Map regularSelection = - (await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ))! as Map; - expect(service.toObject(regularSelection['valueId']! as String), - richTextDiagnostic.value); - }); - - testWidgets('ext.flutter.inspector creationLocation', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - final Element elementB = findElementABC('b'); - - service.disposeAllGroups(); - service.resetPubRootDirectories(); - service.setSelection(elementA, 'my-group'); - final Map jsonA = (await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ))! as Map; - final Map creationLocationA = - jsonA['creationLocation']! as Map; - expect(creationLocationA, isNotNull); - final String fileA = creationLocationA['file']! as String; - final int lineA = creationLocationA['line']! as int; - final int columnA = creationLocationA['column']! as int; - - service.setSelection(elementB, 'my-group'); - final Map jsonB = (await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ))! as Map; - final Map creationLocationB = - jsonB['creationLocation']! as Map; - expect(creationLocationB, isNotNull); - final String fileB = creationLocationB['file']! as String; - final int lineB = creationLocationB['line']! as int; - final int columnB = creationLocationB['column']! as int; - expect(fileA, endsWith('widget_inspector_test.dart')); - expect(fileA, equals(fileB)); - // We don't hardcode the actual lines the widgets are created on as that - // would make this test fragile. - expect(lineA + 1, equals(lineB)); - // Column numbers are more stable than line numbers. - expect(columnA, equals(15)); - expect(columnA, equals(columnB)); - }); - }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // [intended] Test requires --track-widget-creation flag. + service.setSelection(elementB, 'my-group'); + final Map jsonB = + (await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ))! + as Map; + final Map creationLocationB = + jsonB['creationLocation']! as Map; + expect(creationLocationB, isNotNull); + final String fileB = creationLocationB['file']! as String; + final int lineB = creationLocationB['line']! as int; + final int columnB = creationLocationB['column']! as int; + expect(fileA, endsWith('widget_inspector_test.dart')); + expect(fileA, equals(fileB)); + // We don't hardcode the actual lines the widgets are created on as that + // would make this test fragile. + expect(lineA + 1, equals(lineB)); + // Column numbers are more stable than line numbers. + expect(columnA, equals(15)); + expect(columnA, equals(columnB)); + }); + }, + skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. + ); }); group('InspectorSelection', () { - testWidgets('receives notifications when selection changes', - (WidgetTester tester) async { + testWidgets('receives notifications when selection changes', (WidgetTester tester) async { await pumpWidgetTreeWithABC(tester); final InspectorSelection selection = InspectorSelection(); addTearDown(selection.dispose); @@ -2859,10 +2802,8 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { selection.addListener(() { count++; }); - final RenderParagraph renderObjectA = - tester.renderObject(find.text('a')); - final RenderParagraph renderObjectB = - tester.renderObject(find.text('b')); + final RenderParagraph renderObjectA = tester.renderObject(find.text('a')); + final RenderParagraph renderObjectB = tester.renderObject(find.text('b')); final Element elementA = find.text('a').evaluate().first; selection.candidates = [renderObjectA, renderObjectB]; @@ -2918,20 +2859,20 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { final String aId = service.toId(a, group1)!; final String bId = service.toId(b, group1)!; expect(service.toId(a, group2), equals(aId)); - await service.testExtension( - WidgetInspectorServiceExtensions.disposeId.name, - {'arg': bId, 'objectGroup': group1}, - ); + await service.testExtension(WidgetInspectorServiceExtensions.disposeId.name, { + 'arg': bId, + 'objectGroup': group1, + }); expect(() => service.toObject(bId), throwsFlutterError); - await service.testExtension( - WidgetInspectorServiceExtensions.disposeId.name, - {'arg': aId, 'objectGroup': group1}, - ); + await service.testExtension(WidgetInspectorServiceExtensions.disposeId.name, { + 'arg': aId, + 'objectGroup': group1, + }); expect(service.toObject(aId), equals(a)); - await service.testExtension( - WidgetInspectorServiceExtensions.disposeId.name, - {'arg': aId, 'objectGroup': group2}, - ); + await service.testExtension(WidgetInspectorServiceExtensions.disposeId.name, { + 'arg': aId, + 'objectGroup': group2, + }); expect(() => service.toObject(aId), throwsFlutterError); }); @@ -2955,7 +2896,10 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { service.setSelection(elementB.renderObject); expect(selectionChangedCount, equals(2)); expect(service.selection.current, equals(elementB.renderObject)); - expect(service.selection.currentElement, equals((elementB.renderObject!.debugCreator! as DebugCreator).element)); + expect( + service.selection.currentElement, + equals((elementB.renderObject!.debugCreator! as DebugCreator).element), + ); service.setSelection('invalid selection'); expect(selectionChangedCount, equals(2)); @@ -3008,7 +2952,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { if (i + 1 == expectedChain.length) { expect(chainNode['childIndex'], isNull); } else { - expect(chainNode['childIndex'], equals(childrenElements.indexOf(expectedChain[i+1]))); + expect(chainNode['childIndex'], equals(childrenElements.indexOf(expectedChain[i + 1]))); } for (int j = 0; j < childrenElements.length; j += 1) { expect(jsonChildren[j], isMap); @@ -3022,10 +2966,12 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { const Diagnosticable diagnosticable = Text('a', textDirection: TextDirection.ltr); const String group = 'group'; final String id = service.toId(diagnosticable, group)!; - final List propertiesJson = (await service.testExtension( - WidgetInspectorServiceExtensions.getProperties.name, - {'arg': id, 'objectGroup': group}, - ))! as List; + final List propertiesJson = + (await service.testExtension( + WidgetInspectorServiceExtensions.getProperties.name, + {'arg': id, 'objectGroup': group}, + ))! + as List; final List properties = diagnosticable.toDiagnosticsNode().getProperties(); expect(properties, isNotEmpty); expect(propertiesJson.length, equals(properties.length)); @@ -3040,10 +2986,12 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { await pumpWidgetTreeWithABC(tester); final DiagnosticsNode diagnostic = find.byType(Stack).evaluate().first.toDiagnosticsNode(); final String id = service.toId(diagnostic, group)!; - final List propertiesJson = (await service.testExtension( - WidgetInspectorServiceExtensions.getChildren.name, - {'arg': id, 'objectGroup': group}, - ))! as List; + final List propertiesJson = + (await service.testExtension( + WidgetInspectorServiceExtensions.getChildren.name, + {'arg': id, 'objectGroup': group}, + ))! + as List; final List children = diagnostic.getChildren(); expect(children.length, equals(3)); expect(propertiesJson.length, equals(children.length)); @@ -3058,10 +3006,12 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { await pumpWidgetTreeWithABC(tester); final Diagnosticable diagnosticable = find.byType(Stack).evaluate().first; final String id = service.toId(diagnosticable, group)!; - final List childrenJson = (await service.testExtension( - WidgetInspectorServiceExtensions.getChildrenDetailsSubtree.name, - {'arg': id, 'objectGroup': group}, - ))! as List; + final List childrenJson = + (await service.testExtension( + WidgetInspectorServiceExtensions.getChildrenDetailsSubtree.name, + {'arg': id, 'objectGroup': group}, + ))! + as List; final List children = diagnosticable.toDiagnosticsNode().getChildren(); expect(children.length, equals(3)); expect(childrenJson.length, equals(children.length)); @@ -3070,11 +3020,15 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(service.toObject(childJson['valueId']! as String), equals(children[i].value)); final List propertiesJson = childJson['properties']! as List; final Element element = service.toObject(childJson['valueId']! as String)! as Element; - final List expectedProperties = element.toDiagnosticsNode().getProperties(); - final Iterable propertyValues = expectedProperties.map((DiagnosticsNode e) => e.value.toString()); - for (final Map propertyJson in propertiesJson.cast>()) { + final List expectedProperties = + element.toDiagnosticsNode().getProperties(); + final Iterable propertyValues = expectedProperties.map( + (DiagnosticsNode e) => e.value.toString(), + ); + for (final Map propertyJson + in propertiesJson.cast>()) { final String id = propertyJson['valueId']! as String; - final String property = service.toObject(id)!.toString(); + final String property = service.toObject(id)!.toString(); expect(propertyValues, contains(property)); } } @@ -3086,10 +3040,12 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { await pumpWidgetTreeWithABC(tester); final Diagnosticable diagnosticable = find.byType(Stack).evaluate().first; final String id = service.toId(diagnosticable, group)!; - final Map subtreeJson = (await service.testExtension( - WidgetInspectorServiceExtensions.getDetailsSubtree.name, - {'arg': id, 'objectGroup': group}, - ))! as Map; + final Map subtreeJson = + (await service.testExtension( + WidgetInspectorServiceExtensions.getDetailsSubtree.name, + {'arg': id, 'objectGroup': group}, + ))! + as Map; expect(subtreeJson['valueId'], equals(id)); final List childrenJson = subtreeJson['children']! as List; final List children = diagnosticable.toDiagnosticsNode().getChildren(); @@ -3099,27 +3055,35 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { final Map childJson = childrenJson[i]! as Map; expect(service.toObject(childJson['valueId']! as String), equals(children[i].value)); final List propertiesJson = childJson['properties']! as List; - for (final Map propertyJson in propertiesJson.cast>()) { + for (final Map propertyJson + in propertiesJson.cast>()) { expect(propertyJson, isNot(contains('children'))); } final Element element = service.toObject(childJson['valueId']! as String)! as Element; - final List expectedProperties = element.toDiagnosticsNode().getProperties(); - final Iterable propertyValues = expectedProperties.map((DiagnosticsNode e) => e.value.toString()); - for (final Map propertyJson in propertiesJson.cast>()) { + final List expectedProperties = + element.toDiagnosticsNode().getProperties(); + final Iterable propertyValues = expectedProperties.map( + (DiagnosticsNode e) => e.value.toString(), + ); + for (final Map propertyJson + in propertiesJson.cast>()) { final String id = propertyJson['valueId']! as String; final String property = service.toObject(id)!.toString(); expect(propertyValues, contains(property)); } } - final Map deepSubtreeJson = (await service.testExtension( - WidgetInspectorServiceExtensions.getDetailsSubtree.name, - {'arg': id, 'objectGroup': group, 'subtreeDepth': '3'}, - ))! as Map; + final Map deepSubtreeJson = + (await service.testExtension( + WidgetInspectorServiceExtensions.getDetailsSubtree.name, + {'arg': id, 'objectGroup': group, 'subtreeDepth': '3'}, + ))! + as Map; final List deepChildrenJson = deepSubtreeJson['children']! as List; for (final Map childJson in deepChildrenJson.cast>()) { final List propertiesJson = childJson['properties']! as List; - for (final Map propertyJson in propertiesJson.cast>()) { + for (final Map propertyJson + in propertiesJson.cast>()) { expect(propertyJson, contains('children')); } } @@ -3134,10 +3098,12 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { b.related = a; final String id = service.toId(a, group)!; - final Map subtreeJson = (await service.testExtension( - WidgetInspectorServiceExtensions.getDetailsSubtree.name, - {'arg': id, 'objectGroup': group}, - ))! as Map; + final Map subtreeJson = + (await service.testExtension( + WidgetInspectorServiceExtensions.getDetailsSubtree.name, + {'arg': id, 'objectGroup': group}, + ))! + as Map; expect(subtreeJson['valueId'], equals(id)); expect(subtreeJson, contains('children')); final List propertiesJson = subtreeJson['properties']! as List; @@ -3150,7 +3116,8 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(relatedProperty, contains('properties')); final List relatedWidgetProperties = relatedProperty['properties']! as List; expect(relatedWidgetProperties.length, equals(1)); - final Map nestedRelatedProperty = relatedWidgetProperties.first! as Map; + final Map nestedRelatedProperty = + relatedWidgetProperties.first! as Map; expect(nestedRelatedProperty['name'], equals('related')); // Make sure we do not include properties or children for diagnostic a // which we already included as the root node as that would indicate a @@ -3174,26 +3141,25 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { service.resetPubRootDirectories(); }); - testWidgets( - 'has createdByLocalProject when the widget is in the pubRootDirectory', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); + testWidgets('has createdByLocalProject when the widget is in the pubRootDirectory', ( + WidgetTester tester, + ) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + await service.testExtension( + WidgetInspectorServiceExtensions.addPubRootDirectories.name, + {'arg0': pubRootTest}, + ); + expect( await service.testExtension( - WidgetInspectorServiceExtensions.addPubRootDirectories.name, - {'arg0': pubRootTest}, - ); - expect( - await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ), - contains('createdByLocalProject'), - ); - }, - ); + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ), + contains('createdByLocalProject'), + ); + }); testWidgets( 'does not have createdByLocalProject if the prefix of the pubRootDirectory is different', @@ -3216,26 +3182,25 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { }, ); - testWidgets( - 'has createdByLocalProject if the pubRootDirectory is prefixed with file://', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); + testWidgets('has createdByLocalProject if the pubRootDirectory is prefixed with file://', ( + WidgetTester tester, + ) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + await service.testExtension( + WidgetInspectorServiceExtensions.addPubRootDirectories.name, + {'arg0': 'file://$pubRootTest'}, + ); + expect( await service.testExtension( - WidgetInspectorServiceExtensions.addPubRootDirectories.name, - {'arg0': 'file://$pubRootTest'}, - ); - expect( - await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ), - contains('createdByLocalProject'), - ); - }, - ); + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ), + contains('createdByLocalProject'), + ); + }); testWidgets( 'does not have createdByLocalProject if the pubRootDirectory has a different suffix', @@ -3258,30 +3223,26 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { }, ); - testWidgets( - 'has createdByLocalProject if at least one of the pubRootDirectories matches', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); + testWidgets('has createdByLocalProject if at least one of the pubRootDirectories matches', ( + WidgetTester tester, + ) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + await service.testExtension( + WidgetInspectorServiceExtensions.addPubRootDirectories.name, + {'arg0': '/unrelated/$pubRootTest', 'arg1': 'file://$pubRootTest'}, + ); + + expect( await service.testExtension( - WidgetInspectorServiceExtensions.addPubRootDirectories.name, - { - 'arg0': '/unrelated/$pubRootTest', - 'arg1': 'file://$pubRootTest', - }, - ); - - expect( - await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ), - contains('createdByLocalProject'), - ); - }, - ); + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ), + contains('createdByLocalProject'), + ); + }); testWidgets( 'widget is part of core framework and is the child of a widget in the package pubRootDirectories', @@ -3291,18 +3252,15 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { // The RichText child of the Text widget is created by the core framework // not the current package. - final Element richText = find - .descendant( - of: find.text('a'), - matching: find.byType(RichText), - ) - .evaluate() - .first; + final Element richText = + find + .descendant(of: find.text('a'), matching: find.byType(RichText)) + .evaluate() + .first; service.setSelection(richText, 'my-group'); service.setPubRootDirectories([pubRootTest]); final Map jsonObject = - json.decode(service.getSelectedWidget(null, 'my-group')) - as Map; + json.decode(service.getSelectedWidget(null, 'my-group')) as Map; expect(jsonObject, isNot(contains('createdByLocalProject'))); final Map creationLocation = jsonObject['creationLocation']! as Map; @@ -3311,10 +3269,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { // thus the creation location is in text.dart not basic.dart final List pathSegmentsFramework = Uri.parse(creationLocation['file']! as String).pathSegments; - expect( - pathSegmentsFramework.join('/'), - endsWith('/flutter/lib/src/widgets/text.dart'), - ); + expect(pathSegmentsFramework.join('/'), endsWith('/flutter/lib/src/widgets/text.dart')); // Strip off /src/widgets/text.dart. final String pubRootFramework = @@ -3383,26 +3338,25 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { service.resetPubRootDirectories(); }); - testWidgets( - 'has createdByLocalProject when the widget is in the pubRootDirectory', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); + testWidgets('has createdByLocalProject when the widget is in the pubRootDirectory', ( + WidgetTester tester, + ) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + await service.testExtension( + WidgetInspectorServiceExtensions.addPubRootDirectories.name, + {'arg0': pubRootTest, 'isolateId': '34'}, + ); + expect( await service.testExtension( - WidgetInspectorServiceExtensions.addPubRootDirectories.name, - {'arg0': pubRootTest, 'isolateId': '34'}, - ); - expect( - await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ), - contains('createdByLocalProject'), - ); - }, - ); + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ), + contains('createdByLocalProject'), + ); + }); testWidgets( 'does not have createdByLocalProject if the prefix of the pubRootDirectory is different', @@ -3413,10 +3367,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { await service.testExtension( WidgetInspectorServiceExtensions.addPubRootDirectories.name, - { - 'arg0': '/invalid/$pubRootTest', - 'isolateId': '34' - }, + {'arg0': '/invalid/$pubRootTest', 'isolateId': '34'}, ); expect( await service.testExtension( @@ -3428,26 +3379,25 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { }, ); - testWidgets( - 'has createdByLocalProject if the pubRootDirectory is prefixed with file://', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); + testWidgets('has createdByLocalProject if the pubRootDirectory is prefixed with file://', ( + WidgetTester tester, + ) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + await service.testExtension( + WidgetInspectorServiceExtensions.addPubRootDirectories.name, + {'arg0': 'file://$pubRootTest', 'isolateId': '34'}, + ); + expect( await service.testExtension( - WidgetInspectorServiceExtensions.addPubRootDirectories.name, - {'arg0': 'file://$pubRootTest', 'isolateId': '34'}, - ); - expect( - await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ), - contains('createdByLocalProject'), - ); - }, - ); + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ), + contains('createdByLocalProject'), + ); + }); testWidgets( 'does not have createdByLocalProject if the pubRootDirectory has a different suffix', @@ -3458,10 +3408,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { await service.testExtension( WidgetInspectorServiceExtensions.addPubRootDirectories.name, - { - 'arg0': '$pubRootTest/different', - 'isolateId': '34' - }, + {'arg0': '$pubRootTest/different', 'isolateId': '34'}, ); expect( await service.testExtension( @@ -3473,31 +3420,30 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { }, ); - testWidgets( - 'has createdByLocalProject if at least one of the pubRootDirectories matches', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); + testWidgets('has createdByLocalProject if at least one of the pubRootDirectories matches', ( + WidgetTester tester, + ) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + await service.testExtension( + WidgetInspectorServiceExtensions.addPubRootDirectories.name, + { + 'arg0': '/unrelated/$pubRootTest', + 'isolateId': '34', + 'arg1': 'file://$pubRootTest', + }, + ); + + expect( await service.testExtension( - WidgetInspectorServiceExtensions.addPubRootDirectories.name, - { - 'arg0': '/unrelated/$pubRootTest', - 'isolateId': '34', - 'arg1': 'file://$pubRootTest', - }, - ); - - expect( - await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ), - contains('createdByLocalProject'), - ); - }, - ); + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ), + contains('createdByLocalProject'), + ); + }); }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. ); @@ -3509,20 +3455,20 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { return event; } - group('ext.flutter.inspector createdByLocalProject', () { - late final String pubRootTest; + group( + 'ext.flutter.inspector createdByLocalProject', + () { + late final String pubRootTest; - setUpAll(() { - pubRootTest = generateTestPubRootDirectory(service); - }); + setUpAll(() { + pubRootTest = generateTestPubRootDirectory(service); + }); - setUp(() { - service.resetPubRootDirectories(); - }); + setUp(() { + service.resetPubRootDirectories(); + }); - testWidgets( - 'reacts to add and removing pubRootDirectories', - (WidgetTester tester) async { + testWidgets('reacts to add and removing pubRootDirectories', (WidgetTester tester) async { await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); @@ -3545,9 +3491,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { await service.testExtension( WidgetInspectorServiceExtensions.removePubRootDirectories.name, - { - 'arg0': pubRootTest, - }, + {'arg0': pubRootTest}, ); service.setSelection(elementA, 'my-group'); expect( @@ -3557,22 +3501,18 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { ), isNot(contains('createdByLocalProject')), ); - }, - ); + }); - testWidgets( - 'does not match when the package directory does not match', - (WidgetTester tester) async { + testWidgets('does not match when the package directory does not match', ( + WidgetTester tester, + ) async { await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); service.setSelection(elementA, 'my-group'); service.testExtension( WidgetInspectorServiceExtensions.addPubRootDirectories.name, - { - 'arg0': '$pubRootTest/different', - 'arg1': '/unrelated/$pubRootTest', - }, + {'arg0': '$pubRootTest/different', 'arg1': '/unrelated/$pubRootTest'}, ); expect( await service.testExtension( @@ -3581,33 +3521,30 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { ), isNot(contains('createdByLocalProject')), ); - }, - ); + }); - testWidgets( - 'has createdByLocalProject when the pubRootDirectory is prefixed with file://', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); + testWidgets( + 'has createdByLocalProject when the pubRootDirectory is prefixed with file://', + (WidgetTester tester) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); - service.testExtension( - WidgetInspectorServiceExtensions.addPubRootDirectories.name, - {'arg0':'file://$pubRootTest'}, - ); - expect( - await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ), - contains('createdByLocalProject'), - ); - }, - ); + service.testExtension( + WidgetInspectorServiceExtensions.addPubRootDirectories.name, + {'arg0': 'file://$pubRootTest'}, + ); + expect( + await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ), + contains('createdByLocalProject'), + ); + }, + ); - testWidgets( - 'can handle consecutive calls to add', - (WidgetTester tester) async { + testWidgets('can handle consecutive calls to add', (WidgetTester tester) async { await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); service.setSelection(elementA, 'my-group'); @@ -3627,21 +3564,17 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { ), contains('createdByLocalProject'), ); - }, - ); - testWidgets( - 'can handle removing an unrelated pubRootDirectory', - (WidgetTester tester) async { + }); + testWidgets('can handle removing an unrelated pubRootDirectory', ( + WidgetTester tester, + ) async { await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); service.setSelection(elementA, 'my-group'); service.testExtension( WidgetInspectorServiceExtensions.addPubRootDirectories.name, - { - 'arg0': pubRootTest, - 'arg1': '/invalid/$pubRootTest', - }, + {'arg0': pubRootTest, 'arg1': '/invalid/$pubRootTest'}, ); expect( await service.testExtension( @@ -3662,32 +3595,27 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { ), contains('createdByLocalProject'), ); - }, - ); + }); - testWidgets( - 'can handle parent widget being part of a separate package', - (WidgetTester tester) async { + testWidgets('can handle parent widget being part of a separate package', ( + WidgetTester tester, + ) async { await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); - final Element richText = find - .descendant( - of: find.text('a'), - matching: find.byType(RichText), - ) - .evaluate() - .first; + final Element richText = + find.descendant(of: find.text('a'), matching: find.byType(RichText)).evaluate().first; service.setSelection(richText, 'my-group'); service.testExtension( WidgetInspectorServiceExtensions.addPubRootDirectories.name, - { 'arg0': pubRootTest }, + {'arg0': pubRootTest}, ); final Map jsonObject = (await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ))! as Map; + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ))! + as Map; expect(jsonObject, isNot(contains('createdByLocalProject'))); final Map creationLocation = jsonObject['creationLocation']! as Map; @@ -3696,10 +3624,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { // thus the creation location is in text.dart not basic.dart final List pathSegmentsFramework = Uri.parse(creationLocation['file']! as String).pathSegments; - expect( - pathSegmentsFramework.join('/'), - endsWith('/flutter/lib/src/widgets/text.dart'), - ); + expect(pathSegmentsFramework.join('/'), endsWith('/flutter/lib/src/widgets/text.dart')); // Strip off /src/widgets/text.dart. final String pubRootFramework = @@ -3728,10 +3653,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { service.resetPubRootDirectories(); service.testExtension( WidgetInspectorServiceExtensions.addPubRootDirectories.name, - { - 'arg0': pubRootFramework, - 'arg1': pubRootTest, - }, + {'arg0': pubRootFramework, 'arg1': pubRootTest}, ); service.setSelection(elementA, 'my-group'); expect( @@ -3749,26 +3671,25 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { ), contains('createdByLocalProject'), ); - }, - ); - }, + }); + }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. ); - group('ext.flutter.inspector createdByLocalProject extra args regression test', () { - late final String pubRootTest; + group( + 'ext.flutter.inspector createdByLocalProject extra args regression test', + () { + late final String pubRootTest; - setUpAll(() { - pubRootTest = generateTestPubRootDirectory(service); - }); + setUpAll(() { + pubRootTest = generateTestPubRootDirectory(service); + }); - setUp(() { - service.resetPubRootDirectories(); - }); + setUp(() { + service.resetPubRootDirectories(); + }); - testWidgets( - 'reacts to add and removing pubRootDirectories', - (WidgetTester tester) async { + testWidgets('reacts to add and removing pubRootDirectories', (WidgetTester tester) async { await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); @@ -3785,117 +3706,97 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect( await service.testExtension( WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group', 'isolateId': '34',}, + {'objectGroup': 'my-group', 'isolateId': '34'}, ), contains('createdByLocalProject'), ); await service.testExtension( WidgetInspectorServiceExtensions.removePubRootDirectories.name, - { - 'arg0': pubRootTest, - 'isolateId': '34', - }, + {'arg0': pubRootTest, 'isolateId': '34'}, ); service.setSelection(elementA, 'my-group'); expect( await service.testExtension( WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group', 'isolateId': '34',}, + {'objectGroup': 'my-group', 'isolateId': '34'}, ), isNot(contains('createdByLocalProject')), ); - }, - ); + }); - testWidgets( - 'does not match when the package directory does not match', - (WidgetTester tester) async { + testWidgets('does not match when the package directory does not match', ( + WidgetTester tester, + ) async { await pumpWidgetTreeWithABC(tester); final Element elementA = findElementABC('a'); service.setSelection(elementA, 'my-group'); service.testExtension( WidgetInspectorServiceExtensions.addPubRootDirectories.name, - { - 'arg0': '$pubRootTest/different', - 'arg1': '/unrelated/$pubRootTest', - }, + {'arg0': '$pubRootTest/different', 'arg1': '/unrelated/$pubRootTest'}, ); expect( await service.testExtension( WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group', 'isolateId': '34',}, + {'objectGroup': 'my-group', 'isolateId': '34'}, ), isNot(contains('createdByLocalProject')), ); - }, - ); + }); - testWidgets( - 'has createdByLocalProject when the pubRootDirectory is prefixed with file://', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); + testWidgets( + 'has createdByLocalProject when the pubRootDirectory is prefixed with file://', + (WidgetTester tester) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); - service.testExtension( - WidgetInspectorServiceExtensions.addPubRootDirectories.name, - { - 'arg0':'file://$pubRootTest', - 'isolateId': '34', - }, - ); - expect( - await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group', 'isolateId': '34',}, - ), - contains('createdByLocalProject'), - ); - }, - ); - - testWidgets( - 'can handle consecutive calls to add', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); - - service.testExtension( - WidgetInspectorServiceExtensions.addPubRootDirectories.name, - { - 'arg0': pubRootTest, - 'isolateId': '34', - }, - ); - service.testExtension( - WidgetInspectorServiceExtensions.addPubRootDirectories.name, - { - 'arg0': '/invalid/$pubRootTest', - 'isolateId': '34', - }, - ); - expect( - await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group', 'isolateId': '34',}, - ), - contains('createdByLocalProject'), - ); - }, - ); - testWidgets( - 'can handle removing an unrelated pubRootDirectory', - (WidgetTester tester) async { - await pumpWidgetTreeWithABC(tester); - final Element elementA = findElementABC('a'); - service.setSelection(elementA, 'my-group'); - - service.testExtension( + service.testExtension( WidgetInspectorServiceExtensions.addPubRootDirectories.name, - { + {'arg0': 'file://$pubRootTest', 'isolateId': '34'}, + ); + expect( + await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group', 'isolateId': '34'}, + ), + contains('createdByLocalProject'), + ); + }, + ); + + testWidgets('can handle consecutive calls to add', (WidgetTester tester) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + + service.testExtension( + WidgetInspectorServiceExtensions.addPubRootDirectories.name, + {'arg0': pubRootTest, 'isolateId': '34'}, + ); + service.testExtension( + WidgetInspectorServiceExtensions.addPubRootDirectories.name, + {'arg0': '/invalid/$pubRootTest', 'isolateId': '34'}, + ); + expect( + await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group', 'isolateId': '34'}, + ), + contains('createdByLocalProject'), + ); + }); + testWidgets('can handle removing an unrelated pubRootDirectory', ( + WidgetTester tester, + ) async { + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + service.setSelection(elementA, 'my-group'); + + service.testExtension( + WidgetInspectorServiceExtensions.addPubRootDirectories.name, + { 'arg0': pubRootTest, 'arg1': '/invalid/$pubRootTest', 'isolateId': '34', @@ -3904,449 +3805,455 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect( await service.testExtension( WidgetInspectorServiceExtensions.getSelectedWidget.name, - { - 'objectGroup': 'my-group', - 'isolateId': '34', - }, + {'objectGroup': 'my-group', 'isolateId': '34'}, ), contains('createdByLocalProject'), ); service.testExtension( WidgetInspectorServiceExtensions.removePubRootDirectories.name, - { - 'arg0': '/invalid/$pubRootTest', - 'isolateId': '34', - }, + {'arg0': '/invalid/$pubRootTest', 'isolateId': '34'}, ); expect( await service.testExtension( WidgetInspectorServiceExtensions.getSelectedWidget.name, - { - 'objectGroup': 'my-group', - 'isolateId': '34', - }, + {'objectGroup': 'my-group', 'isolateId': '34'}, ), contains('createdByLocalProject'), ); - }, - ); - }, + }); + }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. ); - testWidgets('ext.flutter.inspector.trackRebuildDirtyWidgets with tear-offs', (WidgetTester tester) async { - final Widget widget = Directionality( - textDirection: TextDirection.ltr, - child: WidgetInspector( - exitWidgetSelectionButtonBuilder: null, - moveExitWidgetSelectionButtonBuilder: null, - child: _applyConstructor(_TrivialWidget.new), - ), - ); + testWidgets( + 'ext.flutter.inspector.trackRebuildDirtyWidgets with tear-offs', + (WidgetTester tester) async { + final Widget widget = Directionality( + textDirection: TextDirection.ltr, + child: WidgetInspector( + exitWidgetSelectionButtonBuilder: null, + moveExitWidgetSelectionButtonBuilder: null, + child: _applyConstructor(_TrivialWidget.new), + ), + ); - expect( - await service.testBoolExtension( - WidgetInspectorServiceExtensions.trackRebuildDirtyWidgets.name, - {'enabled': 'true'}, - ), - equals('true'), - ); + expect( + await service.testBoolExtension( + WidgetInspectorServiceExtensions.trackRebuildDirtyWidgets.name, + {'enabled': 'true'}, + ), + equals('true'), + ); - await tester.pumpWidget(widget); - }, + await tester.pumpWidget(widget); + }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. ); - testWidgets('ext.flutter.inspector.widgetLocationIdMap', - (WidgetTester tester) async { - service.rebuildCount = 0; + testWidgets( + 'ext.flutter.inspector.widgetLocationIdMap', + (WidgetTester tester) async { + service.rebuildCount = 0; - await tester.pumpWidget(const ClockDemo()); + await tester.pumpWidget(const ClockDemo()); - final Element clockDemoElement = find.byType(ClockDemo).evaluate().first; + final Element clockDemoElement = find.byType(ClockDemo).evaluate().first; - service.setSelection(clockDemoElement, 'my-group'); - final Map jsonObject = (await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ))! as Map; - final Map creationLocation = - jsonObject['creationLocation']! as Map; - final String file = creationLocation['file']! as String; - expect(file, endsWith('widget_inspector_test.dart')); + service.setSelection(clockDemoElement, 'my-group'); + final Map jsonObject = + (await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ))! + as Map; + final Map creationLocation = + jsonObject['creationLocation']! as Map; + final String file = creationLocation['file']! as String; + expect(file, endsWith('widget_inspector_test.dart')); - final Map locationMapJson = (await service.testExtension( - WidgetInspectorServiceExtensions.widgetLocationIdMap.name, - {}, - ))! as Map; + final Map locationMapJson = + (await service.testExtension( + WidgetInspectorServiceExtensions.widgetLocationIdMap.name, + {}, + ))! + as Map; - final Map widgetTestLocations = - locationMapJson[file]! as Map; - expect(widgetTestLocations, isNotNull); + final Map widgetTestLocations = + locationMapJson[file]! as Map; + expect(widgetTestLocations, isNotNull); - final List ids = widgetTestLocations['ids']! as List; - expect(ids.length, greaterThan(0)); - final List lines = - widgetTestLocations['lines']! as List; - expect(lines.length, equals(ids.length)); - final List columns = - widgetTestLocations['columns']! as List; - expect(columns.length, equals(ids.length)); - final List names = - widgetTestLocations['names']! as List; - expect(names.length, equals(ids.length)); - expect(names, contains('ClockDemo')); - expect(names, contains('Directionality')); - expect(names, contains('ClockText')); - }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // [intended] Test requires --track-widget-creation flag. + final List ids = widgetTestLocations['ids']! as List; + expect(ids.length, greaterThan(0)); + final List lines = widgetTestLocations['lines']! as List; + expect(lines.length, equals(ids.length)); + final List columns = widgetTestLocations['columns']! as List; + expect(columns.length, equals(ids.length)); + final List names = widgetTestLocations['names']! as List; + expect(names.length, equals(ids.length)); + expect(names, contains('ClockDemo')); + expect(names, contains('Directionality')); + expect(names, contains('ClockText')); + }, + skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. + ); - testWidgets('ext.flutter.inspector.trackRebuildDirtyWidgets', (WidgetTester tester) async { - service.rebuildCount = 0; + testWidgets( + 'ext.flutter.inspector.trackRebuildDirtyWidgets', + (WidgetTester tester) async { + service.rebuildCount = 0; - await tester.pumpWidget(const ClockDemo()); + await tester.pumpWidget(const ClockDemo()); - final Element clockDemoElement = find.byType(ClockDemo).evaluate().first; + final Element clockDemoElement = find.byType(ClockDemo).evaluate().first; - service.setSelection(clockDemoElement, 'my-group'); - final Map jsonObject = (await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ))! as Map; - final Map creationLocation = jsonObject['creationLocation']! as Map; - expect(creationLocation, isNotNull); - final String file = creationLocation['file']! as String; - expect(file, endsWith('widget_inspector_test.dart')); - final List segments = Uri.parse(file).pathSegments; - // Strip a couple subdirectories away to generate a plausible pub root - // directory. - final String pubRootTest = '/${segments.take(segments.length - 2).join('/')}'; - service.resetPubRootDirectories(); - await service.testExtension( - WidgetInspectorServiceExtensions.addPubRootDirectories.name, - {'arg0': pubRootTest}, - ); + service.setSelection(clockDemoElement, 'my-group'); + final Map jsonObject = + (await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ))! + as Map; + final Map creationLocation = + jsonObject['creationLocation']! as Map; + expect(creationLocation, isNotNull); + final String file = creationLocation['file']! as String; + expect(file, endsWith('widget_inspector_test.dart')); + final List segments = Uri.parse(file).pathSegments; + // Strip a couple subdirectories away to generate a plausible pub root + // directory. + final String pubRootTest = '/${segments.take(segments.length - 2).join('/')}'; + service.resetPubRootDirectories(); + await service.testExtension( + WidgetInspectorServiceExtensions.addPubRootDirectories.name, + {'arg0': pubRootTest}, + ); - final List> rebuildEvents = - service.dispatchedEvents('Flutter.RebuiltWidgets'); - expect(rebuildEvents, isEmpty); + final List> rebuildEvents = service.dispatchedEvents( + 'Flutter.RebuiltWidgets', + ); + expect(rebuildEvents, isEmpty); - expect(service.rebuildCount, equals(0)); - expect( - await service.testBoolExtension( - WidgetInspectorServiceExtensions.trackRebuildDirtyWidgets.name, - {'enabled': 'true'}, - ), - equals('true'), - ); - expect(service.rebuildCount, equals(1)); - await tester.pump(); + expect(service.rebuildCount, equals(0)); + expect( + await service.testBoolExtension( + WidgetInspectorServiceExtensions.trackRebuildDirtyWidgets.name, + {'enabled': 'true'}, + ), + equals('true'), + ); + expect(service.rebuildCount, equals(1)); + await tester.pump(); - expect(rebuildEvents.length, equals(1)); - Map event = removeLastEvent(rebuildEvents); - expect(event['startTime'], isA()); - List data = event['events']! as List; - expect(data.length, equals(14)); - final int numDataEntries = data.length ~/ 2; - Map> newLocations = event['newLocations']! as Map>; - expect(newLocations, isNotNull); - expect(newLocations.length, equals(1)); - expect(newLocations.keys.first, equals(file)); - Map>> fileLocationsMap = event['locations']! as Map>>; - expect(fileLocationsMap, isNotNull); - expect(fileLocationsMap.length, equals(1)); - expect(fileLocationsMap.keys.first, equals(file)); - final List locationsForFile = newLocations[file]!; - expect(locationsForFile.length, equals(21)); - final int numLocationEntries = locationsForFile.length ~/ 3; - expect(numLocationEntries, equals(numDataEntries)); - final Map> locations = fileLocationsMap[file]!; - expect(locations.length, equals(4)); - expect(locations['ids']!.length, equals(7)); + expect(rebuildEvents.length, equals(1)); + Map event = removeLastEvent(rebuildEvents); + expect(event['startTime'], isA()); + List data = event['events']! as List; + expect(data.length, equals(14)); + final int numDataEntries = data.length ~/ 2; + Map> newLocations = event['newLocations']! as Map>; + expect(newLocations, isNotNull); + expect(newLocations.length, equals(1)); + expect(newLocations.keys.first, equals(file)); + Map>> fileLocationsMap = + event['locations']! as Map>>; + expect(fileLocationsMap, isNotNull); + expect(fileLocationsMap.length, equals(1)); + expect(fileLocationsMap.keys.first, equals(file)); + final List locationsForFile = newLocations[file]!; + expect(locationsForFile.length, equals(21)); + final int numLocationEntries = locationsForFile.length ~/ 3; + expect(numLocationEntries, equals(numDataEntries)); + final Map> locations = fileLocationsMap[file]!; + expect(locations.length, equals(4)); + expect(locations['ids']!.length, equals(7)); - final Map knownLocations = {}; - _addToKnownLocationsMap( - knownLocations: knownLocations, - newLocations: fileLocationsMap, - ); - int totalCount = 0; - int maxCount = 0; - for (int i = 0; i < data.length; i += 2) { - final int id = data[i]; - final int count = data[i + 1]; - totalCount += count; - maxCount = max(maxCount, count); - expect(knownLocations, contains(id)); - } - expect(totalCount, equals(27)); - // The creation locations that were rebuilt the most were rebuilt 6 times - // as there are 6 instances of the ClockText widget. - expect(maxCount, equals(6)); + final Map knownLocations = {}; + _addToKnownLocationsMap(knownLocations: knownLocations, newLocations: fileLocationsMap); + int totalCount = 0; + int maxCount = 0; + for (int i = 0; i < data.length; i += 2) { + final int id = data[i]; + final int count = data[i + 1]; + totalCount += count; + maxCount = max(maxCount, count); + expect(knownLocations, contains(id)); + } + expect(totalCount, equals(27)); + // The creation locations that were rebuilt the most were rebuilt 6 times + // as there are 6 instances of the ClockText widget. + expect(maxCount, equals(6)); - final List clocks = find.byType(ClockText).evaluate().toList(); - expect(clocks.length, equals(6)); - // Update a single clock. - StatefulElement clockElement = clocks.first as StatefulElement; - _ClockTextState state = clockElement.state as _ClockTextState; - state.updateTime(); // Triggers a rebuild. - await tester.pump(); - expect(rebuildEvents.length, equals(1)); - event = removeLastEvent(rebuildEvents); - expect(event['startTime'], isA()); - data = event['events']! as List; - // No new locations were rebuilt. - expect(event, isNot(contains('newLocations'))); - expect(event, isNot(contains('locations'))); + final List clocks = find.byType(ClockText).evaluate().toList(); + expect(clocks.length, equals(6)); + // Update a single clock. + StatefulElement clockElement = clocks.first as StatefulElement; + _ClockTextState state = clockElement.state as _ClockTextState; + state.updateTime(); // Triggers a rebuild. + await tester.pump(); + expect(rebuildEvents.length, equals(1)); + event = removeLastEvent(rebuildEvents); + expect(event['startTime'], isA()); + data = event['events']! as List; + // No new locations were rebuilt. + expect(event, isNot(contains('newLocations'))); + expect(event, isNot(contains('locations'))); - // There were two rebuilds: one for the ClockText element itself and one - // for its child. - expect(data.length, equals(4)); - int id = data[0]; - int count = data[1]; - _CreationLocation location = knownLocations[id]!; - expect(location.file, equals(file)); - // ClockText widget. - expect(location.line, equals(57)); - expect(location.column, equals(9)); - expect(location.name, equals('ClockText')); - expect(count, equals(1)); + // There were two rebuilds: one for the ClockText element itself and one + // for its child. + expect(data.length, equals(4)); + int id = data[0]; + int count = data[1]; + _CreationLocation location = knownLocations[id]!; + expect(location.file, equals(file)); + // ClockText widget. + expect(location.line, equals(54)); + expect(location.column, equals(64)); + expect(location.name, equals('ClockText')); + expect(count, equals(1)); - id = data[2]; - count = data[3]; - location = knownLocations[id]!; - expect(location.file, equals(file)); - // Text widget in _ClockTextState build method. - expect(location.line, equals(95)); - expect(location.column, equals(12)); - expect(location.name, equals('Text')); - expect(count, equals(1)); + id = data[2]; + count = data[3]; + location = knownLocations[id]!; + expect(location.file, equals(file)); + // Text widget in _ClockTextState build method. + expect(location.line, equals(88)); + expect(location.column, equals(12)); + expect(location.name, equals('Text')); + expect(count, equals(1)); - // Update 3 of the clocks; - for (int i = 0; i < 3; i++) { - clockElement = clocks[i] as StatefulElement; + // Update 3 of the clocks; + for (int i = 0; i < 3; i++) { + clockElement = clocks[i] as StatefulElement; + state = clockElement.state as _ClockTextState; + state.updateTime(); // Triggers a rebuild. + } + + await tester.pump(); + expect(rebuildEvents.length, equals(1)); + event = removeLastEvent(rebuildEvents); + expect(event['startTime'], isA()); + data = event['events']! as List; + // No new locations were rebuilt. + expect(event, isNot(contains('newLocations'))); + expect(event, isNot(contains('locations'))); + + expect(data.length, equals(4)); + id = data[0]; + count = data[1]; + location = knownLocations[id]!; + expect(location.file, equals(file)); + // ClockText widget. + expect(location.line, equals(54)); + expect(location.column, equals(64)); + expect(location.name, equals('ClockText')); + expect(count, equals(3)); // 3 clock widget instances rebuilt. + + id = data[2]; + count = data[3]; + location = knownLocations[id]!; + expect(location.file, equals(file)); + // Text widget in _ClockTextState build method. + expect(location.line, equals(88)); + expect(location.column, equals(12)); + expect(location.name, equals('Text')); + expect(count, equals(3)); // 3 clock widget instances rebuilt. + + // Update one clock 3 times. + clockElement = clocks.first as StatefulElement; state = clockElement.state as _ClockTextState; state.updateTime(); // Triggers a rebuild. - } + state.updateTime(); // Triggers a rebuild. + state.updateTime(); // Triggers a rebuild. - await tester.pump(); - expect(rebuildEvents.length, equals(1)); - event = removeLastEvent(rebuildEvents); - expect(event['startTime'], isA()); - data = event['events']! as List; - // No new locations were rebuilt. - expect(event, isNot(contains('newLocations'))); - expect(event, isNot(contains('locations'))); + await tester.pump(); + expect(rebuildEvents.length, equals(1)); + event = removeLastEvent(rebuildEvents); + expect(event['startTime'], isA()); + data = event['events']! as List; + // No new locations were rebuilt. + expect(event, isNot(contains('newLocations'))); + expect(event, isNot(contains('locations'))); - expect(data.length, equals(4)); - id = data[0]; - count = data[1]; - location = knownLocations[id]!; - expect(location.file, equals(file)); - // ClockText widget. - expect(location.line, equals(57)); - expect(location.column, equals(9)); - expect(location.name, equals('ClockText')); - expect(count, equals(3)); // 3 clock widget instances rebuilt. + expect(data.length, equals(4)); + id = data[0]; + count = data[1]; + // Even though a rebuild was triggered 3 times, only one rebuild actually + // occurred. + expect(count, equals(1)); - id = data[2]; - count = data[3]; - location = knownLocations[id]!; - expect(location.file, equals(file)); - // Text widget in _ClockTextState build method. - expect(location.line, equals(95)); - expect(location.column, equals(12)); - expect(location.name, equals('Text')); - expect(count, equals(3)); // 3 clock widget instances rebuilt. + // Trigger a widget creation location that wasn't previously triggered. + state.stopClock(); + await tester.pump(); + expect(rebuildEvents.length, equals(1)); + event = removeLastEvent(rebuildEvents); + expect(event['startTime'], isA()); + expect(event['frameNumber'], isA()); + data = event['events']! as List; + newLocations = event['newLocations']! as Map>; + fileLocationsMap = event['locations']! as Map>>; - // Update one clock 3 times. - clockElement = clocks.first as StatefulElement; - state = clockElement.state as _ClockTextState; - state.updateTime(); // Triggers a rebuild. - state.updateTime(); // Triggers a rebuild. - state.updateTime(); // Triggers a rebuild. - - await tester.pump(); - expect(rebuildEvents.length, equals(1)); - event = removeLastEvent(rebuildEvents); - expect(event['startTime'], isA()); - data = event['events']! as List; - // No new locations were rebuilt. - expect(event, isNot(contains('newLocations'))); - expect(event, isNot(contains('locations'))); - - expect(data.length, equals(4)); - id = data[0]; - count = data[1]; - // Even though a rebuild was triggered 3 times, only one rebuild actually - // occurred. - expect(count, equals(1)); - - // Trigger a widget creation location that wasn't previously triggered. - state.stopClock(); - await tester.pump(); - expect(rebuildEvents.length, equals(1)); - event = removeLastEvent(rebuildEvents); - expect(event['startTime'], isA()); - expect(event['frameNumber'], isA()); - data = event['events']! as List; - newLocations = event['newLocations']! as Map>; - fileLocationsMap = event['locations']! as Map>>; - - expect(data.length, equals(4)); - // The second pair in data is the previously unseen rebuild location. - id = data[2]; - count = data[3]; - expect(count, equals(1)); - // Verify the rebuild location is new. - expect(knownLocations, isNot(contains(id))); - _addToKnownLocationsMap( - knownLocations: knownLocations, - newLocations: fileLocationsMap, - ); - // Verify the rebuild location was included in the newLocations data. - expect(knownLocations, contains(id)); - - // Turn off rebuild counts. - expect( - await service.testBoolExtension( - WidgetInspectorServiceExtensions.trackRebuildDirtyWidgets.name, - {'enabled': 'false'}, - ), - equals('false'), - ); - - state.updateTime(); // Triggers a rebuild. - await tester.pump(); - // Verify that rebuild events are not fired once the extension is disabled. - expect(rebuildEvents, isEmpty); - }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // [intended] Test requires --track-widget-creation flag. - - testWidgets('ext.flutter.inspector.trackRepaintWidgets', (WidgetTester tester) async { - service.rebuildCount = 0; - - await tester.pumpWidget(const ClockDemo()); - - final Element clockDemoElement = find.byType(ClockDemo).evaluate().first; - - service.setSelection(clockDemoElement, 'my-group'); - final Map jsonObject = (await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ))! as Map; - final Map creationLocation = - jsonObject['creationLocation']! as Map; - expect(creationLocation, isNotNull); - final String file = creationLocation['file']! as String; - expect(file, endsWith('widget_inspector_test.dart')); - final List segments = Uri.parse(file).pathSegments; - // Strip a couple subdirectories away to generate a plausible pub root - // directory. - final String pubRootTest = '/${segments.take(segments.length - 2).join('/')}'; - service.resetPubRootDirectories(); - await service.testExtension( - WidgetInspectorServiceExtensions.addPubRootDirectories.name, - {'arg0': pubRootTest}, - ); - - final List> repaintEvents = - service.dispatchedEvents('Flutter.RepaintWidgets'); - expect(repaintEvents, isEmpty); - - expect(service.rebuildCount, equals(0)); - expect( - await service.testBoolExtension( - WidgetInspectorServiceExtensions.trackRepaintWidgets.name, - {'enabled': 'true'}, - ), - equals('true'), - ); - // Unlike trackRebuildDirtyWidgets, trackRepaintWidgets doesn't force a full - // rebuild. - expect(service.rebuildCount, equals(0)); - - await tester.pump(); - - expect(repaintEvents.length, equals(1)); - Map event = removeLastEvent(repaintEvents); - expect(event['startTime'], isA()); - List data = event['events']! as List; - expect(data.length, equals(18)); - final int numDataEntries = data.length ~/ 2; - final Map> newLocations = event['newLocations']! as Map>; - expect(newLocations, isNotNull); - expect(newLocations.length, equals(1)); - expect(newLocations.keys.first, equals(file)); - final Map>> fileLocationsMap = event['locations']! as Map>>; - expect(fileLocationsMap, isNotNull); - expect(fileLocationsMap.length, equals(1)); - expect(fileLocationsMap.keys.first, equals(file)); - final List locationsForFile = newLocations[file]!; - expect(locationsForFile.length, equals(27)); - final int numLocationEntries = locationsForFile.length ~/ 3; - expect(numLocationEntries, equals(numDataEntries)); - final Map> locations = fileLocationsMap[file]!; - expect(locations.length, equals(4)); - expect(locations['ids']!.length, equals(9)); - - final Map knownLocations = {}; - _addToKnownLocationsMap( - knownLocations: knownLocations, - newLocations: fileLocationsMap, - ); - int totalCount = 0; - int maxCount = 0; - for (int i = 0; i < data.length; i += 2) { - final int id = data[i]; - final int count = data[i + 1]; - totalCount += count; - maxCount = max(maxCount, count); + expect(data.length, equals(4)); + // The second pair in data is the previously unseen rebuild location. + id = data[2]; + count = data[3]; + expect(count, equals(1)); + // Verify the rebuild location is new. + expect(knownLocations, isNot(contains(id))); + _addToKnownLocationsMap(knownLocations: knownLocations, newLocations: fileLocationsMap); + // Verify the rebuild location was included in the newLocations data. expect(knownLocations, contains(id)); - } - expect(totalCount, equals(34)); - // The creation locations that were rebuilt the most were rebuilt 6 times - // as there are 6 instances of the ClockText widget. - expect(maxCount, equals(6)); - final List clocks = find.byType(ClockText).evaluate().toList(); - expect(clocks.length, equals(6)); - // Update a single clock. - final StatefulElement clockElement = clocks.first as StatefulElement; - final _ClockTextState state = clockElement.state as _ClockTextState; - state.updateTime(); // Triggers a rebuild. - await tester.pump(); - expect(repaintEvents.length, equals(1)); - event = removeLastEvent(repaintEvents); - expect(event['startTime'], isA()); - expect(event['frameNumber'], isA()); - data = event['events']! as List; - // No new locations were rebuilt. - expect(event, isNot(contains('newLocations'))); - expect(event, isNot(contains('locations'))); + // Turn off rebuild counts. + expect( + await service.testBoolExtension( + WidgetInspectorServiceExtensions.trackRebuildDirtyWidgets.name, + {'enabled': 'false'}, + ), + equals('false'), + ); - // Triggering a rebuild of one widget in this app causes the whole app - // to repaint. - expect(data.length, equals(18)); + state.updateTime(); // Triggers a rebuild. + await tester.pump(); + // Verify that rebuild events are not fired once the extension is disabled. + expect(rebuildEvents, isEmpty); + }, + skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. + ); - // TODO(jacobr): add an additional repaint test that uses multiple repaint - // boundaries to test more complex repaint conditions. + testWidgets( + 'ext.flutter.inspector.trackRepaintWidgets', + (WidgetTester tester) async { + service.rebuildCount = 0; - // Turn off rebuild counts. - expect( - await service.testBoolExtension( - WidgetInspectorServiceExtensions.trackRepaintWidgets.name, - {'enabled': 'false'}, - ), - equals('false'), - ); + await tester.pumpWidget(const ClockDemo()); - state.updateTime(); // Triggers a rebuild. - await tester.pump(); - // Verify that repaint events are not fired once the extension is disabled. - expect(repaintEvents, isEmpty); - }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // [intended] Test requires --track-widget-creation flag. + final Element clockDemoElement = find.byType(ClockDemo).evaluate().first; + + service.setSelection(clockDemoElement, 'my-group'); + final Map jsonObject = + (await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ))! + as Map; + final Map creationLocation = + jsonObject['creationLocation']! as Map; + expect(creationLocation, isNotNull); + final String file = creationLocation['file']! as String; + expect(file, endsWith('widget_inspector_test.dart')); + final List segments = Uri.parse(file).pathSegments; + // Strip a couple subdirectories away to generate a plausible pub root + // directory. + final String pubRootTest = '/${segments.take(segments.length - 2).join('/')}'; + service.resetPubRootDirectories(); + await service.testExtension( + WidgetInspectorServiceExtensions.addPubRootDirectories.name, + {'arg0': pubRootTest}, + ); + + final List> repaintEvents = service.dispatchedEvents( + 'Flutter.RepaintWidgets', + ); + expect(repaintEvents, isEmpty); + + expect(service.rebuildCount, equals(0)); + expect( + await service.testBoolExtension( + WidgetInspectorServiceExtensions.trackRepaintWidgets.name, + {'enabled': 'true'}, + ), + equals('true'), + ); + // Unlike trackRebuildDirtyWidgets, trackRepaintWidgets doesn't force a full + // rebuild. + expect(service.rebuildCount, equals(0)); + + await tester.pump(); + + expect(repaintEvents.length, equals(1)); + Map event = removeLastEvent(repaintEvents); + expect(event['startTime'], isA()); + List data = event['events']! as List; + expect(data.length, equals(18)); + final int numDataEntries = data.length ~/ 2; + final Map> newLocations = + event['newLocations']! as Map>; + expect(newLocations, isNotNull); + expect(newLocations.length, equals(1)); + expect(newLocations.keys.first, equals(file)); + final Map>> fileLocationsMap = + event['locations']! as Map>>; + expect(fileLocationsMap, isNotNull); + expect(fileLocationsMap.length, equals(1)); + expect(fileLocationsMap.keys.first, equals(file)); + final List locationsForFile = newLocations[file]!; + expect(locationsForFile.length, equals(27)); + final int numLocationEntries = locationsForFile.length ~/ 3; + expect(numLocationEntries, equals(numDataEntries)); + final Map> locations = fileLocationsMap[file]!; + expect(locations.length, equals(4)); + expect(locations['ids']!.length, equals(9)); + + final Map knownLocations = {}; + _addToKnownLocationsMap(knownLocations: knownLocations, newLocations: fileLocationsMap); + int totalCount = 0; + int maxCount = 0; + for (int i = 0; i < data.length; i += 2) { + final int id = data[i]; + final int count = data[i + 1]; + totalCount += count; + maxCount = max(maxCount, count); + expect(knownLocations, contains(id)); + } + expect(totalCount, equals(34)); + // The creation locations that were rebuilt the most were rebuilt 6 times + // as there are 6 instances of the ClockText widget. + expect(maxCount, equals(6)); + + final List clocks = find.byType(ClockText).evaluate().toList(); + expect(clocks.length, equals(6)); + // Update a single clock. + final StatefulElement clockElement = clocks.first as StatefulElement; + final _ClockTextState state = clockElement.state as _ClockTextState; + state.updateTime(); // Triggers a rebuild. + await tester.pump(); + expect(repaintEvents.length, equals(1)); + event = removeLastEvent(repaintEvents); + expect(event['startTime'], isA()); + expect(event['frameNumber'], isA()); + data = event['events']! as List; + // No new locations were rebuilt. + expect(event, isNot(contains('newLocations'))); + expect(event, isNot(contains('locations'))); + + // Triggering a rebuild of one widget in this app causes the whole app + // to repaint. + expect(data.length, equals(18)); + + // TODO(jacobr): add an additional repaint test that uses multiple repaint + // boundaries to test more complex repaint conditions. + + // Turn off rebuild counts. + expect( + await service.testBoolExtension( + WidgetInspectorServiceExtensions.trackRepaintWidgets.name, + {'enabled': 'false'}, + ), + equals('false'), + ); + + state.updateTime(); // Triggers a rebuild. + await tester.pump(); + // Verify that repaint events are not fired once the extension is disabled. + expect(repaintEvents, isEmpty); + }, + skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. + ); testWidgets('ext.flutter.inspector.show', (WidgetTester tester) async { - final Iterable> extensionChangedEvents = service.getServiceExtensionStateChangedEvents('ext.flutter.inspector.show'); + final Iterable> extensionChangedEvents = service + .getServiceExtensionStateChangedEvents('ext.flutter.inspector.show'); Map extensionChangedEvent; int debugShowChangeCounter = 0; @@ -4369,7 +4276,9 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { } WidgetsBinding.instance.debugShowWidgetInspectorOverride = false; - valueListenableBuilderWidget.valueListenable.addListener(debugShowWidgetInspectorOverrideCallback); + valueListenableBuilderWidget.valueListenable.addListener( + debugShowWidgetInspectorOverrideCallback, + ); service.rebuildCount = 0; expect(extensionChangedEvents, isEmpty); @@ -4436,8 +4345,11 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(WidgetsBinding.instance.debugShowWidgetInspectorOverride, isFalse); }); - testWidgets('ext.flutter.inspector.show via WidgetsApp.debugShowWidgetInspectorOverride', (WidgetTester tester) async { - final Iterable> extensionChangedEvents = service.getServiceExtensionStateChangedEvents('ext.flutter.inspector.show'); + testWidgets('ext.flutter.inspector.show via WidgetsApp.debugShowWidgetInspectorOverride', ( + WidgetTester tester, + ) async { + final Iterable> extensionChangedEvents = service + .getServiceExtensionStateChangedEvents('ext.flutter.inspector.show'); Map extensionChangedEvent; int debugShowChangeCounter = 0; @@ -4460,7 +4372,9 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { } WidgetsApp.debugShowWidgetInspectorOverride = false; - valueListenableBuilderWidget.valueListenable.addListener(debugShowWidgetInspectorOverrideCallback); + valueListenableBuilderWidget.valueListenable.addListener( + debugShowWidgetInspectorOverrideCallback, + ); service.rebuildCount = 0; expect(extensionChangedEvents, isEmpty); @@ -4527,50 +4441,49 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(WidgetsApp.debugShowWidgetInspectorOverride, isFalse); }); - testWidgets('ext.flutter.inspector.screenshot', (WidgetTester tester) async { - final GlobalKey outerContainerKey = GlobalKey(); - final GlobalKey paddingKey = GlobalKey(); - final GlobalKey redContainerKey = GlobalKey(); - final GlobalKey whiteContainerKey = GlobalKey(); - final GlobalKey sizedBoxKey = GlobalKey(); + testWidgets( + 'ext.flutter.inspector.screenshot', + (WidgetTester tester) async { + final GlobalKey outerContainerKey = GlobalKey(); + final GlobalKey paddingKey = GlobalKey(); + final GlobalKey redContainerKey = GlobalKey(); + final GlobalKey whiteContainerKey = GlobalKey(); + final GlobalKey sizedBoxKey = GlobalKey(); - // Complex widget tree intended to exercise features such as children - // with rotational transforms and clipping without introducing platform - // specific behavior as text rendering would. - await tester.pumpWidget( - Center( - child: RepaintBoundaryWithDebugPaint( - child: ColoredBox( - key: outerContainerKey, - color: Colors.white, - child: Padding( - key: paddingKey, - padding: const EdgeInsets.all(100.0), - child: SizedBox( - key: sizedBoxKey, - height: 100.0, - width: 100.0, - child: Transform.rotate( - angle: 1.0, // radians - child: ClipRRect( - borderRadius: const BorderRadius.only( - topLeft: Radius.elliptical(10.0, 20.0), - topRight: Radius.elliptical(5.0, 30.0), - bottomLeft: Radius.elliptical(2.5, 12.0), - bottomRight: Radius.elliptical(15.0, 6.0), - ), - child: ColoredBox( - key: redContainerKey, - color: Colors.red, + // Complex widget tree intended to exercise features such as children + // with rotational transforms and clipping without introducing platform + // specific behavior as text rendering would. + await tester.pumpWidget( + Center( + child: RepaintBoundaryWithDebugPaint( + child: ColoredBox( + key: outerContainerKey, + color: Colors.white, + child: Padding( + key: paddingKey, + padding: const EdgeInsets.all(100.0), + child: SizedBox( + key: sizedBoxKey, + height: 100.0, + width: 100.0, + child: Transform.rotate( + angle: 1.0, // radians + child: ClipRRect( + borderRadius: const BorderRadius.only( + topLeft: Radius.elliptical(10.0, 20.0), + topRight: Radius.elliptical(5.0, 30.0), + bottomLeft: Radius.elliptical(2.5, 12.0), + bottomRight: Radius.elliptical(15.0, 6.0), + ), child: ColoredBox( - key: whiteContainerKey, - color: Colors.white, - child: RepaintBoundary( - child: Center( - child: Container( - color: Colors.black, - height: 10.0, - width: 10.0, + key: redContainerKey, + color: Colors.red, + child: ColoredBox( + key: whiteContainerKey, + color: Colors.white, + child: RepaintBoundary( + child: Center( + child: Container(color: Colors.black, height: 10.0, width: 10.0), ), ), ), @@ -4582,306 +4495,265 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { ), ), ), - ), - ); + ); - final Element repaintBoundary = - find.byType(RepaintBoundaryWithDebugPaint).evaluate().single; + final Element repaintBoundary = + find.byType(RepaintBoundaryWithDebugPaint).evaluate().single; - final RenderRepaintBoundary renderObject = repaintBoundary.renderObject! as RenderRepaintBoundary; + final RenderRepaintBoundary renderObject = + repaintBoundary.renderObject! as RenderRepaintBoundary; - final OffsetLayer layer = renderObject.debugLayer! as OffsetLayer; - final int expectedChildLayerCount = getChildLayerCount(layer); - expect(expectedChildLayerCount, equals(2)); + final OffsetLayer layer = renderObject.debugLayer! as OffsetLayer; + final int expectedChildLayerCount = getChildLayerCount(layer); + expect(expectedChildLayerCount, equals(2)); - final ui.Image image1 = await layer.toImage( - renderObject.semanticBounds.inflate(50.0), - ); - addTearDown(image1.dispose); + final ui.Image image1 = await layer.toImage(renderObject.semanticBounds.inflate(50.0)); + addTearDown(image1.dispose); - await expectLater( - image1, - matchesGoldenFile('inspector.repaint_boundary_margin.png'), - ); + await expectLater(image1, matchesGoldenFile('inspector.repaint_boundary_margin.png')); - // Regression test for how rendering with a pixel scale other than 1.0 - // was handled. - final ui.Image image2 = await layer.toImage( - renderObject.semanticBounds.inflate(50.0), - pixelRatio: 0.5, - ); - addTearDown(image2.dispose); + // Regression test for how rendering with a pixel scale other than 1.0 + // was handled. + final ui.Image image2 = await layer.toImage( + renderObject.semanticBounds.inflate(50.0), + pixelRatio: 0.5, + ); + addTearDown(image2.dispose); - await expectLater( - image2, - matchesGoldenFile('inspector.repaint_boundary_margin_small.png'), - ); + await expectLater(image2, matchesGoldenFile('inspector.repaint_boundary_margin_small.png')); - final ui.Image image3 = await layer.toImage( - renderObject.semanticBounds.inflate(50.0), - pixelRatio: 2.0, - ); - addTearDown(image3.dispose); + final ui.Image image3 = await layer.toImage( + renderObject.semanticBounds.inflate(50.0), + pixelRatio: 2.0, + ); + addTearDown(image3.dispose); - await expectLater( - image3, - matchesGoldenFile('inspector.repaint_boundary_margin_large.png'), - ); + await expectLater(image3, matchesGoldenFile('inspector.repaint_boundary_margin_large.png')); - final Layer? layerParent = layer.parent; - final Layer? firstChild = layer.firstChild; + final Layer? layerParent = layer.parent; + final Layer? firstChild = layer.firstChild; - expect(layerParent, isNotNull); - expect(firstChild, isNotNull); + expect(layerParent, isNotNull); + expect(firstChild, isNotNull); - final ui.Image? screenshot1 = await service.screenshot( - repaintBoundary, - width: 300.0, - height: 300.0, - ); - addTearDown(() => screenshot1?.dispose()); + final ui.Image? screenshot1 = await service.screenshot( + repaintBoundary, + width: 300.0, + height: 300.0, + ); + addTearDown(() => screenshot1?.dispose()); - await expectLater( - screenshot1, - matchesGoldenFile('inspector.repaint_boundary.png'), - ); + await expectLater(screenshot1, matchesGoldenFile('inspector.repaint_boundary.png')); - // Verify that taking a screenshot didn't change the layers associated with - // the renderObject. - expect(renderObject.debugLayer, equals(layer)); - // Verify that taking a screenshot did not change the number of children - // of the layer. - expect(getChildLayerCount(layer), equals(expectedChildLayerCount)); + // Verify that taking a screenshot didn't change the layers associated with + // the renderObject. + expect(renderObject.debugLayer, equals(layer)); + // Verify that taking a screenshot did not change the number of children + // of the layer. + expect(getChildLayerCount(layer), equals(expectedChildLayerCount)); - final ui.Image? screenshot2 = await service.screenshot( - repaintBoundary, - width: 500.0, - height: 500.0, - margin: 50.0, - ); - addTearDown(() => screenshot2?.dispose()); + final ui.Image? screenshot2 = await service.screenshot( + repaintBoundary, + width: 500.0, + height: 500.0, + margin: 50.0, + ); + addTearDown(() => screenshot2?.dispose()); - await expectLater( - screenshot2, - matchesGoldenFile('inspector.repaint_boundary_margin.png'), - ); + await expectLater(screenshot2, matchesGoldenFile('inspector.repaint_boundary_margin.png')); - // Verify that taking a screenshot didn't change the layers associated with - // the renderObject. - expect(renderObject.debugLayer, equals(layer)); - // Verify that taking a screenshot did not change the number of children - // of the layer. - expect(getChildLayerCount(layer), equals(expectedChildLayerCount)); + // Verify that taking a screenshot didn't change the layers associated with + // the renderObject. + expect(renderObject.debugLayer, equals(layer)); + // Verify that taking a screenshot did not change the number of children + // of the layer. + expect(getChildLayerCount(layer), equals(expectedChildLayerCount)); - // Make sure taking a screenshot didn't change the parent of the layer. - expect(layer.parent, equals(layerParent)); + // Make sure taking a screenshot didn't change the parent of the layer. + expect(layer.parent, equals(layerParent)); - final ui.Image? screenshot3 = await service.screenshot( - repaintBoundary, - width: 300.0, - height: 300.0, - debugPaint: true, - ); - addTearDown(() => screenshot3?.dispose()); + final ui.Image? screenshot3 = await service.screenshot( + repaintBoundary, + width: 300.0, + height: 300.0, + debugPaint: true, + ); + addTearDown(() => screenshot3?.dispose()); - await expectLater( - screenshot3, - matchesGoldenFile('inspector.repaint_boundary_debugPaint.png'), - ); - // Verify that taking a screenshot with debug paint on did not change - // the number of children the layer has. - expect(getChildLayerCount(layer), equals(expectedChildLayerCount)); + await expectLater( + screenshot3, + matchesGoldenFile('inspector.repaint_boundary_debugPaint.png'), + ); + // Verify that taking a screenshot with debug paint on did not change + // the number of children the layer has. + expect(getChildLayerCount(layer), equals(expectedChildLayerCount)); - // Ensure that creating screenshots including ones with debug paint - // hasn't changed the regular render of the widget. - await expectLater( - find.byType(RepaintBoundaryWithDebugPaint), - matchesGoldenFile('inspector.repaint_boundary.png'), - ); + // Ensure that creating screenshots including ones with debug paint + // hasn't changed the regular render of the widget. + await expectLater( + find.byType(RepaintBoundaryWithDebugPaint), + matchesGoldenFile('inspector.repaint_boundary.png'), + ); - expect(renderObject.debugLayer, equals(layer)); - expect(layer.attached, isTrue); + expect(renderObject.debugLayer, equals(layer)); + expect(layer.attached, isTrue); - // Full size image - final ui.Image? screenshot4 = await service.screenshot( - find.byKey(outerContainerKey).evaluate().single, - width: 100.0, - height: 100.0, - ); - addTearDown(() => screenshot4?.dispose()); + // Full size image + final ui.Image? screenshot4 = await service.screenshot( + find.byKey(outerContainerKey).evaluate().single, + width: 100.0, + height: 100.0, + ); + addTearDown(() => screenshot4?.dispose()); - await expectLater( - screenshot4, - matchesGoldenFile('inspector.container.png'), - ); + await expectLater(screenshot4, matchesGoldenFile('inspector.container.png')); - final ui.Image? screenshot5 = await service.screenshot( - find.byKey(outerContainerKey).evaluate().single, - width: 100.0, - height: 100.0, - debugPaint: true, - ); - addTearDown(() => screenshot5?.dispose()); - - await expectLater( - screenshot5, - matchesGoldenFile('inspector.container_debugPaint.png'), - ); - - { - // Verify calling the screenshot method still works if the RenderObject - // needs to be laid out again. - final RenderObject container = - find.byKey(outerContainerKey).evaluate().single.renderObject!; - container - ..markNeedsLayout() - ..markNeedsPaint(); - expect(container.debugNeedsLayout, isTrue); - - final ui.Image? screenshot6 = await service.screenshot( + final ui.Image? screenshot5 = await service.screenshot( find.byKey(outerContainerKey).evaluate().single, width: 100.0, height: 100.0, debugPaint: true, ); - addTearDown(() => screenshot6?.dispose()); + addTearDown(() => screenshot5?.dispose()); + + await expectLater(screenshot5, matchesGoldenFile('inspector.container_debugPaint.png')); + + { + // Verify calling the screenshot method still works if the RenderObject + // needs to be laid out again. + final RenderObject container = + find.byKey(outerContainerKey).evaluate().single.renderObject!; + container + ..markNeedsLayout() + ..markNeedsPaint(); + expect(container.debugNeedsLayout, isTrue); + + final ui.Image? screenshot6 = await service.screenshot( + find.byKey(outerContainerKey).evaluate().single, + width: 100.0, + height: 100.0, + debugPaint: true, + ); + addTearDown(() => screenshot6?.dispose()); + + await expectLater(screenshot6, matchesGoldenFile('inspector.container_debugPaint.png')); + expect(container.debugNeedsLayout, isFalse); + } + + // Small image + final ui.Image? screenshot7 = await service.screenshot( + find.byKey(outerContainerKey).evaluate().single, + width: 50.0, + height: 100.0, + ); + addTearDown(() => screenshot7?.dispose()); + + await expectLater(screenshot7, matchesGoldenFile('inspector.container_small.png')); + + final ui.Image? screenshot8 = await service.screenshot( + find.byKey(outerContainerKey).evaluate().single, + width: 400.0, + height: 400.0, + maxPixelRatio: 3.0, + ); + addTearDown(() => screenshot8?.dispose()); + + await expectLater(screenshot8, matchesGoldenFile('inspector.container_large.png')); + + // This screenshot will show the clip rect debug paint but no other + // debug paint. + final ui.Image? screenshot9 = await service.screenshot( + find.byType(ClipRRect).evaluate().single, + width: 100.0, + height: 100.0, + debugPaint: true, + ); + addTearDown(() => screenshot9?.dispose()); + + await expectLater(screenshot9, matchesGoldenFile('inspector.clipRect_debugPaint.png')); + + final Element clipRect = find.byType(ClipRRect).evaluate().single; + + final ui.Image? clipRectScreenshot = await service.screenshot( + clipRect, + width: 100.0, + height: 100.0, + margin: 20.0, + debugPaint: true, + ); + addTearDown(() => clipRectScreenshot?.dispose()); + + // Add a margin so that the clip icon shows up in the screenshot. + // This golden image is platform dependent due to the clip icon. + await expectLater( + clipRectScreenshot, + matchesGoldenFile('inspector.clipRect_debugPaint_margin.png'), + ); + + // Verify we get the same image if we go through the service extension + // instead of invoking the screenshot method directly. + final Future base64ScreenshotFuture = service + .testExtension(WidgetInspectorServiceExtensions.screenshot.name, { + 'id': service.toId(clipRect, 'group')!, + 'width': '100.0', + 'height': '100.0', + 'margin': '20.0', + 'debugPaint': 'true', + }); + + final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); + final ui.Image screenshotImage = + (await binding.runAsync(() async { + final String base64Screenshot = (await base64ScreenshotFuture)! as String; + final ui.Codec codec = await ui.instantiateImageCodec( + base64.decode(base64Screenshot), + ); + final ui.FrameInfo frame = await codec.getNextFrame(); + return frame.image; + }))!; + addTearDown(screenshotImage.dispose); + + await expectLater(screenshotImage, matchesReferenceImage(clipRectScreenshot!)); + + // Test with a very visible debug paint + final ui.Image? screenshot10 = await service.screenshot( + find.byKey(paddingKey).evaluate().single, + width: 300.0, + height: 300.0, + debugPaint: true, + ); + addTearDown(() => screenshot10?.dispose()); + + await expectLater(screenshot10, matchesGoldenFile('inspector.padding_debugPaint.png')); + + // The bounds for this box crop its rendered content. + final ui.Image? screenshot11 = await service.screenshot( + find.byKey(sizedBoxKey).evaluate().single, + width: 300.0, + height: 300.0, + debugPaint: true, + ); + addTearDown(() => screenshot11?.dispose()); + + await expectLater(screenshot11, matchesGoldenFile('inspector.sizedBox_debugPaint.png')); + + // Verify that setting a margin includes the previously cropped content. + final ui.Image? screenshot12 = await service.screenshot( + find.byKey(sizedBoxKey).evaluate().single, + width: 300.0, + height: 300.0, + margin: 50.0, + debugPaint: true, + ); + addTearDown(() => screenshot12?.dispose()); await expectLater( - screenshot6, - matchesGoldenFile('inspector.container_debugPaint.png'), + screenshot12, + matchesGoldenFile('inspector.sizedBox_debugPaint_margin.png'), ); - expect(container.debugNeedsLayout, isFalse); - } - - // Small image - final ui.Image? screenshot7 = await service.screenshot( - find.byKey(outerContainerKey).evaluate().single, - width: 50.0, - height: 100.0, - ); - addTearDown(() => screenshot7?.dispose()); - - await expectLater( - screenshot7, - matchesGoldenFile('inspector.container_small.png'), - ); - - final ui.Image? screenshot8 = await service.screenshot( - find.byKey(outerContainerKey).evaluate().single, - width: 400.0, - height: 400.0, - maxPixelRatio: 3.0, - ); - addTearDown(() => screenshot8?.dispose()); - - await expectLater( - screenshot8, - matchesGoldenFile('inspector.container_large.png'), - ); - - // This screenshot will show the clip rect debug paint but no other - // debug paint. - final ui.Image? screenshot9 = await service.screenshot( - find.byType(ClipRRect).evaluate().single, - width: 100.0, - height: 100.0, - debugPaint: true, - ); - addTearDown(() => screenshot9?.dispose()); - - await expectLater( - screenshot9, - matchesGoldenFile('inspector.clipRect_debugPaint.png'), - ); - - final Element clipRect = find.byType(ClipRRect).evaluate().single; - - final ui.Image? clipRectScreenshot = await service.screenshot( - clipRect, - width: 100.0, - height: 100.0, - margin: 20.0, - debugPaint: true, - ); - addTearDown(() => clipRectScreenshot?.dispose()); - - // Add a margin so that the clip icon shows up in the screenshot. - // This golden image is platform dependent due to the clip icon. - await expectLater( - clipRectScreenshot, - matchesGoldenFile('inspector.clipRect_debugPaint_margin.png'), - ); - - // Verify we get the same image if we go through the service extension - // instead of invoking the screenshot method directly. - final Future base64ScreenshotFuture = service.testExtension( - WidgetInspectorServiceExtensions.screenshot.name, - { - 'id': service.toId(clipRect, 'group')!, - 'width': '100.0', - 'height': '100.0', - 'margin': '20.0', - 'debugPaint': 'true', - }, - ); - - final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); - final ui.Image screenshotImage = (await binding.runAsync(() async { - final String base64Screenshot = (await base64ScreenshotFuture)! as String; - final ui.Codec codec = await ui.instantiateImageCodec(base64.decode(base64Screenshot)); - final ui.FrameInfo frame = await codec.getNextFrame(); - return frame.image; - }))!; - addTearDown(screenshotImage.dispose); - - await expectLater( - screenshotImage, - matchesReferenceImage(clipRectScreenshot!), - ); - - // Test with a very visible debug paint - final ui.Image? screenshot10 = await service.screenshot( - find.byKey(paddingKey).evaluate().single, - width: 300.0, - height: 300.0, - debugPaint: true, - ); - addTearDown(() => screenshot10?.dispose()); - - await expectLater( - screenshot10, - matchesGoldenFile('inspector.padding_debugPaint.png'), - ); - - // The bounds for this box crop its rendered content. - final ui.Image? screenshot11 = await service.screenshot( - find.byKey(sizedBoxKey).evaluate().single, - width: 300.0, - height: 300.0, - debugPaint: true, - ); - addTearDown(() => screenshot11?.dispose()); - - await expectLater( - screenshot11, - matchesGoldenFile('inspector.sizedBox_debugPaint.png'), - ); - - // Verify that setting a margin includes the previously cropped content. - final ui.Image? screenshot12 = await service.screenshot( - find.byKey(sizedBoxKey).evaluate().single, - width: 300.0, - height: 300.0, - margin: 50.0, - debugPaint: true, - ); - addTearDown(() => screenshot12?.dispose()); - - await expectLater( - screenshot12, - matchesGoldenFile('inspector.sizedBox_debugPaint_margin.png'), - ); - }, skip: impellerEnabled); // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/143616 + }, + skip: impellerEnabled, // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/143616 + ); group('layout explorer', () { const String group = 'test-group'; @@ -4896,12 +4768,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { child: Center( child: Row( children: [ - Flexible( - child: ColoredBox( - color: Colors.green, - child: Text('a'), - ), - ), + Flexible(child: ColoredBox(color: Colors.green, child: Text('a'))), Text('b'), ], ), @@ -4910,24 +4777,29 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { await tester.pumpWidget(widget); } - testWidgets('ext.flutter.inspector.getLayoutExplorerNode for RenderBox with BoxParentData',(WidgetTester tester) async { + testWidgets('ext.flutter.inspector.getLayoutExplorerNode for RenderBox with BoxParentData', ( + WidgetTester tester, + ) async { await pumpWidgetForLayoutExplorer(tester); final Element rowElement = tester.element(find.byType(Row)); service.setSelection(rowElement, group); final String id = service.toId(rowElement, group)!; - final Map result = (await service.testExtension( - WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, - {'id': id, 'groupName': group, 'subtreeDepth': '1'}, - ))! as Map; + final Map result = + (await service.testExtension( + WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, + {'id': id, 'groupName': group, 'subtreeDepth': '1'}, + ))! + as Map; expect(result['description'], equals('Row')); final Map? renderObject = result['renderObject'] as Map?; expect(renderObject, isNotNull); expect(renderObject!['description'], startsWith('RenderFlex')); - final Map? parentRenderElement = result['parentRenderElement'] as Map?; + final Map? parentRenderElement = + result['parentRenderElement'] as Map?; expect(parentRenderElement, isNotNull); expect(parentRenderElement!['description'], equals('Center')); @@ -4955,24 +4827,29 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(parentData['offsetY'], equals('293.0')); }); - testWidgets('ext.flutter.inspector.getLayoutExplorerNode for RenderBox with FlexParentData',(WidgetTester tester) async { + testWidgets('ext.flutter.inspector.getLayoutExplorerNode for RenderBox with FlexParentData', ( + WidgetTester tester, + ) async { await pumpWidgetForLayoutExplorer(tester); final Element flexibleElement = tester.element(find.byType(Flexible).first); service.setSelection(flexibleElement, group); final String id = service.toId(flexibleElement, group)!; - final Map result = (await service.testExtension( - WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, - {'id': id, 'groupName': group, 'subtreeDepth': '1'}, - ))! as Map; + final Map result = + (await service.testExtension( + WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, + {'id': id, 'groupName': group, 'subtreeDepth': '1'}, + ))! + as Map; expect(result['description'], equals('Flexible')); final Map? renderObject = result['renderObject'] as Map?; expect(renderObject, isNotNull); expect(renderObject!['description'], startsWith('_RenderColoredBox')); - final Map? parentRenderElement = result['parentRenderElement'] as Map?; + final Map? parentRenderElement = + result['parentRenderElement'] as Map?; expect(parentRenderElement, isNotNull); expect(parentRenderElement!['description'], equals('Row')); @@ -4997,7 +4874,9 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(result['parentData'], isNull); }); - testWidgets('ext.flutter.inspector.getLayoutExplorerNode for RenderView',(WidgetTester tester) async { + testWidgets('ext.flutter.inspector.getLayoutExplorerNode for RenderView', ( + WidgetTester tester, + ) async { await pumpWidgetForLayoutExplorer(tester); final Element element = tester.element(find.byType(Directionality).first); @@ -5009,10 +4888,12 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { service.setSelection(root, group); final String id = service.toId(root, group)!; - final Map result = (await service.testExtension( - WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, - {'id': id, 'groupName': group, 'subtreeDepth': '1'}, - ))! as Map; + final Map result = + (await service.testExtension( + WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, + {'id': id, 'groupName': group, 'subtreeDepth': '1'}, + ))! + as Map; expect(result['description'], equals('[root]')); final Map? renderObject = result['renderObject'] as Map?; @@ -5047,25 +4928,31 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { service.setSelection(childElement, group); final String id = service.toId(childElement, group)!; - Map result = (await service.testExtension( - WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, - {'id': id, 'groupName': group, 'subtreeDepth': '1'}, - ))! as Map; + Map result = + (await service.testExtension( + WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, + {'id': id, 'groupName': group, 'subtreeDepth': '1'}, + ))! + as Map; expect(result['description'], equals('Flexible')); expect(result['flexFit'], equals('loose')); final String valueId = result['valueId']! as String; - final bool flexFitSuccess = (await service.testExtension( - WidgetInspectorServiceExtensions.setFlexFit.name, - {'id': valueId, 'flexFit': 'FlexFit.tight'}, - ))! as bool; + final bool flexFitSuccess = + (await service.testExtension( + WidgetInspectorServiceExtensions.setFlexFit.name, + {'id': valueId, 'flexFit': 'FlexFit.tight'}, + ))! + as bool; expect(flexFitSuccess, isTrue); - result = (await service.testExtension( - WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, - {'id': id, 'groupName': group, 'subtreeDepth': '1'}, - ))! as Map; + result = + (await service.testExtension( + WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, + {'id': id, 'groupName': group, 'subtreeDepth': '1'}, + ))! + as Map; expect(result['description'], equals('Flexible')); expect(result['flexFit'], equals('tight')); }); @@ -5077,25 +4964,31 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { service.setSelection(childElement, group); final String id = service.toId(childElement, group)!; - Map result = (await service.testExtension( - WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, - {'id': id, 'groupName': group, 'subtreeDepth': '1'}, - ))! as Map; + Map result = + (await service.testExtension( + WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, + {'id': id, 'groupName': group, 'subtreeDepth': '1'}, + ))! + as Map; expect(result['description'], equals('Flexible')); expect(result['flexFactor'], equals(1)); final String valueId = result['valueId']! as String; - final bool flexFactorSuccess = (await service.testExtension( - WidgetInspectorServiceExtensions.setFlexFactor.name, - {'id': valueId, 'flexFactor': '3'}, - ))! as bool; + final bool flexFactorSuccess = + (await service.testExtension( + WidgetInspectorServiceExtensions.setFlexFactor.name, + {'id': valueId, 'flexFactor': '3'}, + ))! + as bool; expect(flexFactorSuccess, isTrue); - result = (await service.testExtension( - WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, - {'id': id, 'groupName': group, 'subtreeDepth': '1'}, - ))! as Map; + result = + (await service.testExtension( + WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, + {'id': id, 'groupName': group, 'subtreeDepth': '1'}, + ))! + as Map; expect(result['description'], equals('Flexible')); expect(result['flexFactor'], equals(3)); }); @@ -5107,21 +5000,21 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { service.setSelection(rowElement, group); final String id = service.toId(rowElement, group)!; - Map result = (await service.testExtension( - WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, - {'id': id, 'groupName': group, 'subtreeDepth': '1'}, - ))! as Map; + Map result = + (await service.testExtension( + WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, + {'id': id, 'groupName': group, 'subtreeDepth': '1'}, + ))! + as Map; expect(result['description'], equals('Row')); Map renderObject = result['renderObject']! as Map; List> properties = (renderObject['properties']! as List).cast>(); - Map mainAxisAlignmentProperties = - properties.firstWhere( + Map mainAxisAlignmentProperties = properties.firstWhere( (Map p) => p['type'] == 'EnumProperty', ); - Map crossAxisAlignmentProperties = - properties.firstWhere( + Map crossAxisAlignmentProperties = properties.firstWhere( (Map p) => p['type'] == 'EnumProperty', ); String mainAxisAlignment = mainAxisAlignmentProperties['description']! as String; @@ -5130,31 +5023,32 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(crossAxisAlignment, equals('center')); final String valueId = result['valueId']! as String; - final bool flexFactorSuccess = (await service.testExtension( - WidgetInspectorServiceExtensions.setFlexProperties.name, - { - 'id': valueId, - 'mainAxisAlignment': 'MainAxisAlignment.center', - 'crossAxisAlignment': 'CrossAxisAlignment.start', - }, - ))! as bool; + final bool flexFactorSuccess = + (await service.testExtension( + WidgetInspectorServiceExtensions.setFlexProperties.name, + { + 'id': valueId, + 'mainAxisAlignment': 'MainAxisAlignment.center', + 'crossAxisAlignment': 'CrossAxisAlignment.start', + }, + ))! + as bool; expect(flexFactorSuccess, isTrue); - result = (await service.testExtension( - WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, - {'id': id, 'groupName': group, 'subtreeDepth': '1'}, - ))! as Map; + result = + (await service.testExtension( + WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, + {'id': id, 'groupName': group, 'subtreeDepth': '1'}, + ))! + as Map; expect(result['description'], equals('Row')); renderObject = result['renderObject']! as Map; - properties = - (renderObject['properties']! as List).cast>(); - mainAxisAlignmentProperties = - properties.firstWhere( + properties = (renderObject['properties']! as List).cast>(); + mainAxisAlignmentProperties = properties.firstWhere( (Map p) => p['type'] == 'EnumProperty', ); - crossAxisAlignmentProperties = - properties.firstWhere( + crossAxisAlignmentProperties = properties.firstWhere( (Map p) => p['type'] == 'EnumProperty', ); mainAxisAlignment = mainAxisAlignmentProperties['description']! as String; @@ -5163,14 +5057,18 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(crossAxisAlignment, equals('start')); }); - testWidgets('ext.flutter.inspector.getLayoutExplorerNode does not throw StackOverflowError',(WidgetTester tester) async { + testWidgets('ext.flutter.inspector.getLayoutExplorerNode does not throw StackOverflowError', ( + WidgetTester tester, + ) async { // Regression test for https://github.com/flutter/flutter/issues/115228 const Key leafKey = ValueKey('ColoredBox'); await tester.pumpWidget( CupertinoApp( home: CupertinoPageScaffold( child: Builder( - builder: (BuildContext context) => ColoredBox(key: leafKey, color: CupertinoTheme.of(context).primaryColor), + builder: + (BuildContext context) => + ColoredBox(key: leafKey, color: CupertinoTheme.of(context).primaryColor), ), ), ), @@ -5194,51 +5092,48 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { }); testWidgets( - 'ext.flutter.inspector.getLayoutExplorerNode, on a ToolTip, does not throw StackOverflowError', - (WidgetTester tester) async { - // Regression test for https://github.com/flutter/devtools/issues/5946 - const Widget widget = MaterialApp( - home: Directionality( - textDirection: TextDirection.ltr, - child: Center( - child: Row( - children: [ - Flexible( - child: ColoredBox( - color: Colors.green, - child: Tooltip( - message: 'a', - child: ElevatedButton( - onPressed: null, - child: Text('a'), + 'ext.flutter.inspector.getLayoutExplorerNode, on a ToolTip, does not throw StackOverflowError', + (WidgetTester tester) async { + // Regression test for https://github.com/flutter/devtools/issues/5946 + const Widget widget = MaterialApp( + home: Directionality( + textDirection: TextDirection.ltr, + child: Center( + child: Row( + children: [ + Flexible( + child: ColoredBox( + color: Colors.green, + child: Tooltip( + message: 'a', + child: ElevatedButton(onPressed: null, child: Text('a')), ), ), ), - ), - ], + ], + ), ), ), - ), - ); - await tester.pumpWidget(widget); - - final Element elevatedButton = - tester.element(find.byType(ElevatedButton).first); - service.setSelection(elevatedButton, group); - - final String id = service.toId(elevatedButton, group)!; - - Object? error; - try { - await service.testExtension( - WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, - {'id': id, 'groupName': group, 'subtreeDepth': '1'}, ); - } catch (e) { - error = e; - } - expect(error, isNull); - }); + await tester.pumpWidget(widget); + + final Element elevatedButton = tester.element(find.byType(ElevatedButton).first); + service.setSelection(elevatedButton, group); + + final String id = service.toId(elevatedButton, group)!; + + Object? error; + try { + await service.testExtension( + WidgetInspectorServiceExtensions.getLayoutExplorerNode.name, + {'id': id, 'groupName': group, 'subtreeDepth': '1'}, + ); + } catch (e) { + error = e; + } + expect(error, isNull); + }, + ); }); test('ext.flutter.inspector.structuredErrors', () async { @@ -5258,11 +5153,13 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { ); // Create an error. - FlutterError.reportError(FlutterErrorDetails( - library: 'rendering library', - context: ErrorDescription('during layout'), - exception: StackTrace.current, - )); + FlutterError.reportError( + FlutterErrorDetails( + library: 'rendering library', + context: ErrorDescription('during layout'), + exception: StackTrace.current, + ), + ); // Validate that we received an error. flutterErrorEvents = service.dispatchedEvents('Flutter.Error'); @@ -5281,11 +5178,13 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { ); // Send a second error. - FlutterError.reportError(FlutterErrorDetails( - library: 'rendering library', - context: ErrorDescription('also during layout'), - exception: StackTrace.current, - )); + FlutterError.reportError( + FlutterErrorDetails( + library: 'rendering library', + context: ErrorDescription('also during layout'), + exception: StackTrace.current, + ), + ); // Validate that the error count increased. flutterErrorEvents = service.dispatchedEvents('Flutter.Error'); @@ -5301,7 +5200,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { await binding.runTest(() async { binding.reassembleApplication(); await binding.pump(); - }, () { }); + }, () {}); // The run test overrides the flutter error handler, so we should // restore it back for the structure error to continue working. FlutterError.onError = oldHandler; @@ -5309,11 +5208,13 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { binding.postTest(); // Send another error. - FlutterError.reportError(FlutterErrorDetails( - library: 'rendering library', - context: ErrorDescription('during layout'), - exception: StackTrace.current, - )); + FlutterError.reportError( + FlutterErrorDetails( + library: 'rendering library', + context: ErrorDescription('during layout'), + exception: StackTrace.current, + ), + ); // And, validate that the error count has been reset. flutterErrorEvents = service.dispatchedEvents('Flutter.Error'); @@ -5361,7 +5262,11 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { top: 456.0, child: CompositedTransformTarget( link: link, - child: Container(height: 20.0, width: 20.0, color: const Color.fromARGB(128, 255, 0, 0)), + child: Container( + height: 20.0, + width: 20.0, + color: const Color.fromARGB(128, 255, 0, 0), + ), ), ), ], @@ -5374,10 +5279,19 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { children: [ // Container so we can see how the follower layer was // transformed relative to its initial location. - Container(height: 15.0, width: 15.0, color: const Color.fromARGB(128, 0, 0, 255)), + Container( + height: 15.0, + width: 15.0, + color: const Color.fromARGB(128, 0, 0, 255), + ), CompositedTransformFollower( link: link, - child: Container(key: key, height: 10.0, width: 10.0, color: const Color.fromARGB(128, 0, 255, 0)), + child: Container( + key: key, + height: 10.0, + width: 10.0, + color: const Color.fromARGB(128, 0, 255, 0), + ), ), ], ), @@ -5459,7 +5373,12 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { angle: 1.0, // radians child: CompositedTransformTarget( link: link, - child: Container(key: key1, height: 20.0, width: 20.0, color: const Color.fromARGB(128, 255, 0, 0)), + child: Container( + key: key1, + height: 20.0, + width: 20.0, + color: const Color.fromARGB(128, 255, 0, 0), + ), ), ), ), @@ -5471,13 +5390,22 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { child: Stack( key: stackWithTransformFollower, children: [ - Container(height: 15.0, width: 15.0, color: const Color.fromARGB(128, 0, 0, 255)), + Container( + height: 15.0, + width: 15.0, + color: const Color.fromARGB(128, 0, 0, 255), + ), Transform.rotate( key: rotate2, angle: -0.3, // radians child: CompositedTransformFollower( link: link, - child: Container(key: key2, height: 10.0, width: 10.0, color: const Color.fromARGB(128, 0, 255, 0)), + child: Container( + key: key2, + height: 10.0, + width: 10.0, + color: const Color.fromARGB(128, 0, 255, 0), + ), ), ), ], @@ -5547,81 +5475,78 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { expect(box2.localToGlobal(Offset.zero), equals(position2)); }); - testWidgets('getChildrenDetailsSubtree', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - title: 'Hello, World', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - home: Scaffold( - appBar: AppBar( - title: const Text('Hello, World'), - ), - body: const Center( - child: Text('Hello, World!'), + testWidgets( + 'getChildrenDetailsSubtree', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + title: 'Hello, World', + theme: ThemeData(primarySwatch: Colors.blue), + home: Scaffold( + appBar: AppBar(title: const Text('Hello, World')), + body: const Center(child: Text('Hello, World!')), ), ), - ), - ); - service.setSelection(find.text('Hello, World!').evaluate().first, 'my-group'); + ); + service.setSelection(find.text('Hello, World!').evaluate().first, 'my-group'); - // Figure out the pubRootDirectory - final Map jsonObject = (await service.testExtension( - WidgetInspectorServiceExtensions.getSelectedWidget.name, - {'objectGroup': 'my-group'}, - ))! as Map; - final Map creationLocation = jsonObject['creationLocation']! as Map; - expect(creationLocation, isNotNull); - final String file = creationLocation['file']! as String; - expect(file, endsWith('widget_inspector_test.dart')); - final List segments = Uri.parse(file).pathSegments; - // Strip a couple subdirectories away to generate a plausible pub rootdirectory. - final String pubRootTest = '/${segments.take(segments.length - 2).join('/')}'; - service.resetPubRootDirectories(); - service.addPubRootDirectories([pubRootTest]); + // Figure out the pubRootDirectory + final Map jsonObject = + (await service.testExtension( + WidgetInspectorServiceExtensions.getSelectedWidget.name, + {'objectGroup': 'my-group'}, + ))! + as Map; + final Map creationLocation = + jsonObject['creationLocation']! as Map; + expect(creationLocation, isNotNull); + final String file = creationLocation['file']! as String; + expect(file, endsWith('widget_inspector_test.dart')); + final List segments = Uri.parse(file).pathSegments; + // Strip a couple subdirectories away to generate a plausible pub rootdirectory. + final String pubRootTest = '/${segments.take(segments.length - 2).join('/')}'; + service.resetPubRootDirectories(); + service.addPubRootDirectories([pubRootTest]); - final String summary = service.getRootWidgetSummaryTree('foo1'); - // ignore: avoid_dynamic_calls - final List childrenOfRoot = json.decode(summary)['children'] as List; - final List childrenOfMaterialApp = (childrenOfRoot.first! as Map)['children']! as List; - final Map scaffold = childrenOfMaterialApp.first! as Map; - expect(scaffold['description'], 'Scaffold'); - final String objectId = scaffold['valueId']! as String; - final String details = service.getDetailsSubtree(objectId, 'foo2'); - // ignore: avoid_dynamic_calls - final List detailedChildren = json.decode(details)['children'] as List; + final String summary = service.getRootWidgetSummaryTree('foo1'); + // ignore: avoid_dynamic_calls + final List childrenOfRoot = json.decode(summary)['children'] as List; + final List childrenOfMaterialApp = + (childrenOfRoot.first! as Map)['children']! as List; + final Map scaffold = childrenOfMaterialApp.first! as Map; + expect(scaffold['description'], 'Scaffold'); + final String objectId = scaffold['valueId']! as String; + final String details = service.getDetailsSubtree(objectId, 'foo2'); + // ignore: avoid_dynamic_calls + final List detailedChildren = json.decode(details)['children'] as List; - final List> appBars = >[]; - void visitChildren(List children) { - for (final Map child in children.cast>()) { - if (child['description'] == 'AppBar') { - appBars.add(child); - } - if (child.containsKey('children')) { - visitChildren(child['children']! as List); + final List> appBars = >[]; + void visitChildren(List children) { + for (final Map child in children.cast>()) { + if (child['description'] == 'AppBar') { + appBars.add(child); + } + if (child.containsKey('children')) { + visitChildren(child['children']! as List); + } } } - } - visitChildren(detailedChildren); - expect(appBars.single, isNot(contains('children'))); - }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // [intended] Test requires --track-widget-creation flag. - testWidgets('InspectorSerializationDelegate addAdditionalPropertiesCallback', (WidgetTester tester) async { + visitChildren(detailedChildren); + expect(appBars.single, isNot(contains('children'))); + }, + skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. + ); + + testWidgets('InspectorSerializationDelegate addAdditionalPropertiesCallback', ( + WidgetTester tester, + ) async { await tester.pumpWidget( MaterialApp( title: 'Hello World!', home: Scaffold( - appBar: AppBar( - title: const Text('Hello World!'), - ), - body: const Center( - child: Column( - children: [ - Text('Hello World!'), - ], - ), - ), + appBar: AppBar(title: const Text('Hello World!')), + body: const Center(child: Column(children: [Text('Hello World!')])), ), ), ); @@ -5634,13 +5559,13 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { service: service, includeProperties: true, addAdditionalPropertiesCallback: - (DiagnosticsNode node, InspectorSerializationDelegate delegate) => { - if (node.value case Element(:final RenderObject renderObject)) - 'renderObject': renderObject.toDiagnosticsNode().toJsonMap( + (DiagnosticsNode node, InspectorSerializationDelegate delegate) => { + if (node.value case Element(:final RenderObject renderObject)) + 'renderObject': renderObject.toDiagnosticsNode().toJsonMap( delegate.copyWith(subtreeDepth: 0), ), - 'callbackExecuted': true, - }, + 'callbackExecuted': true, + }, ); final Map json = node.toJsonMap(delegate); expect(json['callbackExecuted'], true); @@ -5649,104 +5574,117 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { final Map renderObjectJson = json['renderObject']! as Map; expect(renderObjectJson['description'], startsWith('RenderFlex')); - final InspectorSerializationDelegate emptyDelegate = - InspectorSerializationDelegate( - service: service, - includeProperties: true, - addAdditionalPropertiesCallback: - (DiagnosticsNode node, InspectorSerializationDelegate delegate) { - return null; - }, - ); - final InspectorSerializationDelegate defaultDelegate = - InspectorSerializationDelegate( - service: service, - includeProperties: true, - ); + final InspectorSerializationDelegate emptyDelegate = InspectorSerializationDelegate( + service: service, + includeProperties: true, + addAdditionalPropertiesCallback: ( + DiagnosticsNode node, + InspectorSerializationDelegate delegate, + ) { + return null; + }, + ); + final InspectorSerializationDelegate defaultDelegate = InspectorSerializationDelegate( + service: service, + includeProperties: true, + ); expect(node.toJsonMap(emptyDelegate), node.toJsonMap(defaultDelegate)); }); - testWidgets('debugIsLocalCreationLocation test', (WidgetTester tester) async { - setupDefaultPubRootDirectory(service); + testWidgets( + 'debugIsLocalCreationLocation test', + (WidgetTester tester) async { + setupDefaultPubRootDirectory(service); - final GlobalKey key = GlobalKey(); + final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Container( - padding: const EdgeInsets.all(8), - child: Text('target', key: key, textDirection: TextDirection.ltr), + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: Container( + padding: const EdgeInsets.all(8), + child: Text('target', key: key, textDirection: TextDirection.ltr), + ), ), - ), - ); + ); - final Element element = key.currentContext! as Element; + final Element element = key.currentContext! as Element; - expect(debugIsLocalCreationLocation(element), isTrue); - expect(debugIsLocalCreationLocation(element.widget), isTrue); + expect(debugIsLocalCreationLocation(element), isTrue); + expect(debugIsLocalCreationLocation(element.widget), isTrue); - // Padding is inside container - final Finder paddingFinder = find.byType(Padding); + // Padding is inside container + final Finder paddingFinder = find.byType(Padding); - final Element paddingElement = paddingFinder.evaluate().first; + final Element paddingElement = paddingFinder.evaluate().first; - expect(debugIsLocalCreationLocation(paddingElement), isFalse); - expect(debugIsLocalCreationLocation(paddingElement.widget), isFalse); - }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // [intended] Test requires --track-widget-creation flag. + expect(debugIsLocalCreationLocation(paddingElement), isFalse); + expect(debugIsLocalCreationLocation(paddingElement.widget), isFalse); + }, + skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. + ); - testWidgets('debugIsWidgetLocalCreation test', (WidgetTester tester) async { - setupDefaultPubRootDirectory(service); + testWidgets( + 'debugIsWidgetLocalCreation test', + (WidgetTester tester) async { + setupDefaultPubRootDirectory(service); - final GlobalKey key = GlobalKey(); + final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Container( - padding: const EdgeInsets.all(8), - child: Text('target', key: key, textDirection: TextDirection.ltr), + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: Container( + padding: const EdgeInsets.all(8), + child: Text('target', key: key, textDirection: TextDirection.ltr), + ), ), - ), - ); + ); - final Element element = key.currentContext! as Element; - expect(debugIsWidgetLocalCreation(element.widget), isTrue); + final Element element = key.currentContext! as Element; + expect(debugIsWidgetLocalCreation(element.widget), isTrue); - final Finder paddingFinder = find.byType(Padding); - final Element paddingElement = paddingFinder.evaluate().first; - expect(debugIsWidgetLocalCreation(paddingElement.widget), isFalse); - }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // [intended] Test requires --track-widget-creation flag. + final Finder paddingFinder = find.byType(Padding); + final Element paddingElement = paddingFinder.evaluate().first; + expect(debugIsWidgetLocalCreation(paddingElement.widget), isFalse); + }, + skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --track-widget-creation flag. + ); - testWidgets('debugIsWidgetLocalCreation false test', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); + testWidgets( + 'debugIsWidgetLocalCreation false test', + (WidgetTester tester) async { + final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Container( - padding: const EdgeInsets.all(8), - child: Text('target', key: key, textDirection: TextDirection.ltr), + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: Container( + padding: const EdgeInsets.all(8), + child: Text('target', key: key, textDirection: TextDirection.ltr), + ), ), - ), - ); + ); - final Element element = key.currentContext! as Element; - expect(debugIsWidgetLocalCreation(element.widget), isFalse); - }, skip: WidgetInspectorService.instance.isWidgetCreationTracked()); // [intended] Test requires --no-track-widget-creation flag. + final Element element = key.currentContext! as Element; + expect(debugIsWidgetLocalCreation(element.widget), isFalse); + }, + skip: WidgetInspectorService.instance.isWidgetCreationTracked(), // [intended] Test requires --no-track-widget-creation flag. + ); test('devToolsInspectorUri test', () { activeDevToolsServerAddress = 'http://127.0.0.1:9100'; connectedVmServiceUri = 'http://127.0.0.1:55269/798ay5al_FM=/'; expect( WidgetInspectorService.instance.devToolsInspectorUri('inspector-0'), - equals('http://127.0.0.1:9100/#/inspector?uri=http%3A%2F%2F127.0.0.1%3A55269%2F798ay5al_FM%3D%2F&inspectorRef=inspector-0'), + equals( + 'http://127.0.0.1:9100/#/inspector?uri=http%3A%2F%2F127.0.0.1%3A55269%2F798ay5al_FM%3D%2F&inspectorRef=inspector-0', + ), ); }); test('DevToolsDeepLinkProperty test', () { - final DevToolsDeepLinkProperty node = - DevToolsDeepLinkProperty( + final DevToolsDeepLinkProperty node = DevToolsDeepLinkProperty( 'description of the deep link', 'http://the-deeplink/', ); @@ -5771,14 +5709,15 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { } static String generateTestPubRootDirectory(TestWidgetInspectorService service) { - final Map jsonObject = const SizedBox().toDiagnosticsNode().toJsonMap(InspectorSerializationDelegate(service: service)); - final Map creationLocation = jsonObject['creationLocation']! as Map; + final Map jsonObject = const SizedBox().toDiagnosticsNode().toJsonMap( + InspectorSerializationDelegate(service: service), + ); + final Map creationLocation = + jsonObject['creationLocation']! as Map; expect(creationLocation, isNotNull); final String file = creationLocation['file']! as String; expect(file, endsWith('widget_inspector_test.dart')); - final List segments = Uri - .parse(file) - .pathSegments; + final List segments = Uri.parse(file).pathSegments; // Strip a couple subdirectories away to generate a plausible pub root // directory. @@ -5789,8 +5728,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { static void setupDefaultPubRootDirectory(TestWidgetInspectorService service) { service.resetPubRootDirectories(); - service - .addPubRootDirectories([generateTestPubRootDirectory(service)]); + service.addPubRootDirectories([generateTestPubRootDirectory(service)]); } } @@ -5819,8 +5757,7 @@ void _addToKnownLocationsMap({ extension WidgetInspectorServiceExtension on WidgetInspectorService { Future> get currentPubRootDirectories async { - return ((await pubRootDirectories( - {}, - ))['result'] as List).cast(); + return ((await pubRootDirectories({}))['result'] as List) + .cast(); } }