// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('WidgetInspector smoke test', (WidgetTester tester) async {
    // This is a smoke test to verify that adding the inspector doesn't crash.
    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new Stack(
          children: const <Widget>[
            const Text('a', textDirection: TextDirection.ltr),
            const Text('b', textDirection: TextDirection.ltr),
            const Text('c', textDirection: TextDirection.ltr),
          ],
        ),
      ),
    );

    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new WidgetInspector(
          selectButtonBuilder: null,
          child: new Stack(
            children: const <Widget>[
              const Text('a', textDirection: TextDirection.ltr),
              const Text('b', textDirection: TextDirection.ltr),
              const Text('c', textDirection: TextDirection.ltr),
            ],
          ),
        ),
      ),
    );

    expect(true, isTrue); // Expect that we reach here without crashing.
  });

  testWidgets('WidgetInspector interaction test', (WidgetTester tester) async {
    final List<String> log = <String>[];
    final GlobalKey selectButtonKey = new GlobalKey();
    final GlobalKey inspectorKey = new GlobalKey();
    final GlobalKey topButtonKey = new GlobalKey();

    Widget selectButtonBuilder(BuildContext context, VoidCallback onPressed) {
      return new Material(child: new RaisedButton(onPressed: onPressed, key: selectButtonKey));
    }
    // State type is private, hence using dynamic.
    dynamic getInspectorState() => inspectorKey.currentState;
    String paragraphText(RenderParagraph paragraph) => paragraph.text.text;

    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new WidgetInspector(
          key: inspectorKey,
          selectButtonBuilder: selectButtonBuilder,
          child: new Material(
            child: new ListView(
              children: <Widget>[
                new RaisedButton(
                  key: topButtonKey,
                  onPressed: () {
                    log.add('top');
                  },
                  child: const Text('TOP'),
                ),
                new RaisedButton(
                  onPressed: () {
                    log.add('bottom');
                  },
                  child: const Text('BOTTOM'),
                ),
              ],
            ),
          ),
        ),
      ),
    );

    expect(getInspectorState().selection.current, isNull);
    await tester.tap(find.text('TOP'));
    await tester.pump();
    // Tap intercepted by the inspector
    expect(log, equals(<String>[]));
    final InspectorSelection selection = getInspectorState().selection;
    expect(paragraphText(selection.current), equals('TOP'));
    final RenderObject topButton = find.byKey(topButtonKey).evaluate().first.renderObject;
    expect(selection.candidates.contains(topButton), isTrue);

    await tester.tap(find.text('TOP'));
    expect(log, equals(<String>['top']));
    log.clear();

    await tester.tap(find.text('BOTTOM'));
    expect(log, equals(<String>['bottom']));
    log.clear();
    // Ensure the inspector selection has not changed to bottom.
    expect(paragraphText(getInspectorState().selection.current), equals('TOP'));

    await tester.tap(find.byKey(selectButtonKey));
    await tester.pump();

    // We are now back in select mode so tapping the bottom button will have
    // not trigger a click but will cause it to be selected.
    await tester.tap(find.text('BOTTOM'));
    expect(log, equals(<String>[]));
    log.clear();
    expect(paragraphText(getInspectorState().selection.current), equals('BOTTOM'));
  });

  testWidgets('WidgetInspector non-invertible transform regression test', (WidgetTester tester) async {
    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new WidgetInspector(
          selectButtonBuilder: null,
          child: new Transform(
            transform: new Matrix4.identity()..scale(0.0),
            child: new Stack(
              children: const <Widget>[
                const Text('a', textDirection: TextDirection.ltr),
                const Text('b', textDirection: TextDirection.ltr),
                const Text('c', textDirection: TextDirection.ltr),
              ],
            ),
          ),
        ),
      ),
    );

    await tester.tap(find.byType(Transform));

    expect(true, isTrue); // Expect that we reach here without crashing.
  });

  testWidgets('WidgetInspector scroll test', (WidgetTester tester) async {
    final Key childKey = new UniqueKey();
    final GlobalKey selectButtonKey = new GlobalKey();
    final GlobalKey inspectorKey = new GlobalKey();

    Widget selectButtonBuilder(BuildContext context, VoidCallback onPressed) {
      return new Material(child: new RaisedButton(onPressed: onPressed, key: selectButtonKey));
    }
    // State type is private, hence using dynamic.
    dynamic getInspectorState() => inspectorKey.currentState;

    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new WidgetInspector(
          key: inspectorKey,
          selectButtonBuilder: selectButtonBuilder,
          child: new ListView(
            children: <Widget>[
              new 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);
    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);
    await tester.pump();

    // Fling still does nothing as are in inspect mode.
    expect(tester.getTopLeft(find.byKey(childKey)).dy, equals(0.0));

    await tester.tap(find.byType(ListView));
    await tester.pump();
    expect(getInspectorState().selection.current, isNotNull);

    // Now out of inspect mode due to the click.
    await tester.fling(find.byType(ListView), const Offset(0.0, -200.0), 200.0);
    await tester.pump();

    expect(tester.getTopLeft(find.byKey(childKey)).dy, equals(-200.0));

    await tester.fling(find.byType(ListView), const Offset(0.0, 200.0), 200.0);
    await tester.pump();

    expect(tester.getTopLeft(find.byKey(childKey)).dy, equals(0.0));
  });

  testWidgets('WidgetInspector long press', (WidgetTester tester) async {
    bool didLongPress = false;

    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new WidgetInspector(
          selectButtonBuilder: null,
          child: new GestureDetector(
            onLongPress: () {
              expect(didLongPress, isFalse);
              didLongPress = true;
            },
            child: const Text('target', textDirection: TextDirection.ltr),
          ),
        ),
      ),
    );

    await tester.longPress(find.text('target'));
    // The inspector will swallow the long press.
    expect(didLongPress, isFalse);
  });

  testWidgets('WidgetInspector offstage', (WidgetTester tester) async {
    final GlobalKey inspectorKey = new GlobalKey();
    final GlobalKey clickTarget = new GlobalKey();

    Widget createSubtree({ double width, Key key }) {
      return new Stack(
        children: <Widget>[
          new Positioned(
            key: key,
            left: 0.0,
            top: 0.0,
            width: width,
            height: 100.0,
            child: new Text(width.toString(), textDirection: TextDirection.ltr),
          ),
        ],
      );
    }
    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new WidgetInspector(
          key: inspectorKey,
          selectButtonBuilder: null,
          child: new Overlay(
            initialEntries: <OverlayEntry>[
              new OverlayEntry(
                opaque: false,
                maintainState: true,
                builder: (BuildContext _) => createSubtree(width: 94.0),
              ),
              new OverlayEntry(
                opaque: true,
                maintainState: true,
                builder: (BuildContext _) => createSubtree(width: 95.0),
              ),
              new OverlayEntry(
                opaque: false,
                maintainState: true,
                builder: (BuildContext _) => createSubtree(width: 96.0, key: clickTarget),
              ),
            ],
          ),
        ),
      ),
    );

    await tester.longPress(find.byKey(clickTarget));
    // State type is private, hence using dynamic.
    final dynamic inspectorState = inspectorKey.currentState;
    // The object with width 95.0 wins over the object with width 94.0 because
    // the subtree with width 94.0 is offstage.
    expect(inspectorState.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(inspectorState.selection.candidates.where((RenderObject object) => object is RenderParagraph).length, equals(2));
  });

  test('WidgetInspectorService null id', () {
    final WidgetInspectorService service = WidgetInspectorService.instance;
    service.disposeAllGroups();
    expect(service.toObject(null), isNull);
    expect(service.toId(null, 'test-group'), isNull);
  });

  test('WidgetInspectorService dispose group', () {
    final WidgetInspectorService service = WidgetInspectorService.instance;
    service.disposeAllGroups();
    final Object a = new Object();
    const String group1 = 'group-1';
    const String group2 = 'group-2';
    const String group3 = 'group-3';
    final String aId = service.toId(a, group1);
    expect(service.toId(a, group2), equals(aId));
    expect(service.toId(a, group3), equals(aId));
    service.disposeGroup(group1);
    service.disposeGroup(group2);
    expect(service.toObject(aId), equals(a));
    service.disposeGroup(group3);
    expect(() => service.toObject(aId), throwsFlutterError);
  });

  test('WidgetInspectorService dispose id', () {
    final WidgetInspectorService service = WidgetInspectorService.instance;
    service.disposeAllGroups();
    final Object a = new Object();
    final Object b = new Object();
    const String group1 = 'group-1';
    const String group2 = 'group-2';
    final String aId = service.toId(a, group1);
    final String bId = service.toId(b, group1);
    expect(service.toId(a, group2), equals(aId));
    service.disposeId(bId, group1);
    expect(() => service.toObject(bId), throwsFlutterError);
    service.disposeId(aId, group1);
    expect(service.toObject(aId), equals(a));
    service.disposeId(aId, group2);
    expect(() => service.toObject(aId), throwsFlutterError);
  });

  test('WidgetInspectorService toObjectForSourceLocation', () {
    const String group = 'test-group';
    const Text widget = const Text('a', textDirection: TextDirection.ltr);
    final WidgetInspectorService service = WidgetInspectorService.instance;
    service.disposeAllGroups();
    final String id = service.toId(widget, group);
    expect(service.toObjectForSourceLocation(id), equals(widget));
    final Element element = widget.createElement();
    final String elementId = service.toId(element, group);
    expect(service.toObjectForSourceLocation(elementId), equals(widget));
    expect(element, isNot(equals(widget)));
    service.disposeGroup(group);
    expect(() => service.toObjectForSourceLocation(elementId), throwsFlutterError);
  });

  test('WidgetInspectorService object id test', () {
    const Text a = const Text('a', textDirection: TextDirection.ltr);
    const Text b = const Text('b', textDirection: TextDirection.ltr);
    const Text c = const Text('c', textDirection: TextDirection.ltr);
    const Text d = const Text('d', textDirection: TextDirection.ltr);

    const String group1 = 'group-1';
    const String group2 = 'group-2';
    const String group3 = 'group-3';
    final WidgetInspectorService service = WidgetInspectorService.instance;
    service.disposeAllGroups();

    final String aId = service.toId(a, group1);
    final String bId = service.toId(b, group2);
    final String cId = service.toId(c, group3);
    final String dId = service.toId(d, group1);
    // Make sure we get a consistent id if we add the object to a group multiple
    // times.
    expect(aId, equals(service.toId(a, group1)));
    expect(service.toObject(aId), equals(a));
    expect(service.toObject(aId), isNot(equals(b)));
    expect(service.toObject(bId), equals(b));
    expect(service.toObject(cId), equals(c));
    expect(service.toObject(dId), equals(d));
    // Make sure we get a consistent id even if we add the object to a different
    // group.
    expect(aId, equals(service.toId(a, group3)));
    expect(aId, isNot(equals(bId)));
    expect(aId, isNot(equals(cId)));

    service.disposeGroup(group3);
  });

  testWidgets('WidgetInspectorService maybeSetSelection', (WidgetTester tester) async {
    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new Stack(
          children: const <Widget>[
            const Text('a', textDirection: TextDirection.ltr),
            const Text('b', textDirection: TextDirection.ltr),
            const Text('c', textDirection: TextDirection.ltr),
          ],
        ),
      ),
    );
    final Element elementA = find.text('a').evaluate().first;
    final Element elementB = find.text('b').evaluate().first;

    final WidgetInspectorService service = WidgetInspectorService.instance;
    service.disposeAllGroups();
    service.selection.clear();
    int selectionChangedCount = 0;
    service.selectionChangedCallback = () => selectionChangedCount++;
    service.setSelection('invalid selection');
    expect(selectionChangedCount, equals(0));
    expect(service.selection.currentElement, isNull);
    service.setSelection(elementA);
    expect(selectionChangedCount, equals(1));
    expect(service.selection.currentElement, equals(elementA));
    expect(service.selection.current, equals(elementA.renderObject));

    service.setSelection(elementB.renderObject);
    expect(selectionChangedCount, equals(2));
    expect(service.selection.current, equals(elementB.renderObject));
    expect(service.selection.currentElement, equals(elementB.renderObject.debugCreator.element));

    service.setSelection('invalid selection');
    expect(selectionChangedCount, equals(2));
    expect(service.selection.current, equals(elementB.renderObject));

    service.setSelectionById(service.toId(elementA, 'my-group'));
    expect(selectionChangedCount, equals(3));
    expect(service.selection.currentElement, equals(elementA));
    expect(service.selection.current, equals(elementA.renderObject));

    service.setSelectionById(service.toId(elementA, 'my-group'));
    expect(selectionChangedCount, equals(3));
    expect(service.selection.currentElement, equals(elementA));
  });

  testWidgets('WidgetInspectorService getParentChain', (WidgetTester tester) async {
    const String group = 'test-group';

    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new Stack(
          children: const <Widget>[
            const Text('a', textDirection: TextDirection.ltr),
            const Text('b', textDirection: TextDirection.ltr),
            const Text('c', textDirection: TextDirection.ltr),
          ],
        ),
      ),
    );

    final WidgetInspectorService service = WidgetInspectorService.instance;
    service.disposeAllGroups();
    final Element elementB = find.text('b').evaluate().first;
    final String bId = service.toId(elementB, group);
    final Object jsonList = json.decode(service.getParentChain(bId, group));
    expect(jsonList, isList);
    final List<Object> chainElements = jsonList;
    final List<Element> expectedChain = elementB.debugGetDiagnosticChain()?.reversed?.toList();
    // Sanity check that the chain goes back to the root.
    expect(expectedChain.first, tester.binding.renderViewElement);

    expect(chainElements.length, equals(expectedChain.length));
    for (int i = 0; i < expectedChain.length; i += 1) {
      expect(chainElements[i], isMap);
      final Map<String, Object> chainNode = chainElements[i];
      final Element element = expectedChain[i];
      expect(chainNode['node'], isMap);
      final Map<String, Object> jsonNode = chainNode['node'];
      expect(service.toObject(jsonNode['valueId']), equals(element));
      expect(service.toObject(jsonNode['objectId']), const isInstanceOf<DiagnosticsNode>());

      expect(chainNode['children'], isList);
      final List<Object> jsonChildren = chainNode['children'];
      final List<Element> childrenElements = <Element>[];
      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])));
      }
      for (int j = 0; j < childrenElements.length; j += 1) {
        expect(jsonChildren[j], isMap);
        final Map<String, Object> childJson = jsonChildren[j];
        expect(service.toObject(childJson['valueId']), equals(childrenElements[j]));
        expect(service.toObject(childJson['objectId']), const isInstanceOf<DiagnosticsNode>());
      }
    }
  });

  test('WidgetInspectorService getProperties', () {
    final DiagnosticsNode diagnostic = const Text('a', textDirection: TextDirection.ltr).toDiagnosticsNode();
    const String group = 'group';
    final WidgetInspectorService service = WidgetInspectorService.instance;
    service.disposeAllGroups();
    final String id = service.toId(diagnostic, group);
    final List<Object> propertiesJson = json.decode(service.getProperties(id, group));
    final List<DiagnosticsNode> properties = diagnostic.getProperties();
    expect(properties, isNotEmpty);
    expect(propertiesJson.length, equals(properties.length));
    for (int i = 0; i < propertiesJson.length; ++i) {
      final Map<String, Object> propertyJson = propertiesJson[i];
      expect(service.toObject(propertyJson['valueId']), equals(properties[i].value));
      expect(service.toObject(propertyJson['objectId']), const isInstanceOf<DiagnosticsNode>());
    }
  });

  testWidgets('WidgetInspectorService getChildren', (WidgetTester tester) async {
    const String group = 'test-group';

    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new Stack(
          children: const <Widget>[
            const Text('a', textDirection: TextDirection.ltr),
            const Text('b', textDirection: TextDirection.ltr),
            const Text('c', textDirection: TextDirection.ltr),
          ],
        ),
      ),
    );
    final DiagnosticsNode diagnostic = find.byType(Stack).evaluate().first.toDiagnosticsNode();
    final WidgetInspectorService service = WidgetInspectorService.instance;
    service.disposeAllGroups();
    final String id = service.toId(diagnostic, group);
    final List<Object> propertiesJson = json.decode(service.getChildren(id, group));
    final List<DiagnosticsNode> children = diagnostic.getChildren();
    expect(children.length, equals(3));
    expect(propertiesJson.length, equals(children.length));
    for (int i = 0; i < propertiesJson.length; ++i) {
      final Map<String, Object> propertyJson = propertiesJson[i];
      expect(service.toObject(propertyJson['valueId']), equals(children[i].value));
      expect(service.toObject(propertyJson['objectId']), const isInstanceOf<DiagnosticsNode>());
    }
  });

  testWidgets('WidgetInspectorService creationLocation', (WidgetTester tester) async {
    final WidgetInspectorService service = WidgetInspectorService.instance;

    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new Stack(
          children: const <Widget>[
            const Text('a'),
            const Text('b', textDirection: TextDirection.ltr),
            const Text('c', textDirection: TextDirection.ltr),
          ],
        ),
      ),
    );
    final Element elementA = find.text('a').evaluate().first;
    final Element elementB = find.text('b').evaluate().first;

    service.disposeAllGroups();
    service.setPubRootDirectories(<Object>[]);
    service.setSelection(elementA, 'my-group');
    final Map<String, Object> jsonA = json.decode(service.getSelectedWidget(null, 'my-group'));
    final Map<String, Object> creationLocationA = jsonA['creationLocation'];
    expect(creationLocationA, isNotNull);
    final String fileA = creationLocationA['file'];
    final int lineA = creationLocationA['line'];
    final int columnA = creationLocationA['column'];
    final List<Object> parameterLocationsA = creationLocationA['parameterLocations'];

    service.setSelection(elementB, 'my-group');
    final Map<String, Object> jsonB = json.decode(service.getSelectedWidget(null, 'my-group'));
    final Map<String, Object> creationLocationB = jsonB['creationLocation'];
    expect(creationLocationB, isNotNull);
    final String fileB = creationLocationB['file'];
    final int lineB = creationLocationB['line'];
    final int columnB = creationLocationB['column'];
    final List<Object> parameterLocationsB = creationLocationB['parameterLocations'];
    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(19));
    expect(columnA, equals(columnB));
    expect(parameterLocationsA.length, equals(1));
    final Map<String, Object> paramA = parameterLocationsA[0];
    expect(paramA['name'], equals('data'));
    expect(paramA['line'], equals(lineA));
    expect(paramA['column'], equals(24));

    expect(parameterLocationsB.length, equals(2));
    final Map<String, Object> paramB1 = parameterLocationsB[0];
    expect(paramB1['name'], equals('data'));
    expect(paramB1['line'], equals(lineB));
    expect(paramB1['column'], equals(24));
    final Map<String, Object> paramB2 = parameterLocationsB[1];
    expect(paramB2['name'], equals('textDirection'));
    expect(paramB2['line'], equals(lineB));
    expect(paramB2['column'], equals(29));
  }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // Test requires --track-widget-creation flag.

  testWidgets('WidgetInspectorService setPubRootDirectories', (WidgetTester tester) async {
    final WidgetInspectorService service = WidgetInspectorService.instance;

    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new Stack(
          children: const <Widget>[
            const Text('a'),
            const Text('b', textDirection: TextDirection.ltr),
            const Text('c', textDirection: TextDirection.ltr),
          ],
        ),
      ),
    );
    final Element elementA = find.text('a').evaluate().first;

    service.disposeAllGroups();
    service.setPubRootDirectories(<Object>[]);
    service.setSelection(elementA, 'my-group');
    Map<String, Object> jsonObject = json.decode(service.getSelectedWidget(null, 'my-group'));
    Map<String, Object> creationLocation = jsonObject['creationLocation'];
    expect(creationLocation, isNotNull);
    final String fileA = creationLocation['file'];
    expect(fileA, endsWith('widget_inspector_test.dart'));
    expect(jsonObject, isNot(contains('createdByLocalProject')));
    final List<String> segments = Uri.parse(fileA).pathSegments;
    // Strip a couple subdirectories away to generate a plausible pub root
    // directory.
    final String pubRootTest = '/' + segments.take(segments.length - 2).join('/');
    service.setPubRootDirectories(<Object>[pubRootTest]);

    service.setSelection(elementA, 'my-group');
    expect(json.decode(service.getSelectedWidget(null, 'my-group')), contains('createdByLocalProject'));

    service.setPubRootDirectories(<Object>['/invalid/$pubRootTest']);
    expect(json.decode(service.getSelectedWidget(null, 'my-group')), isNot(contains('createdByLocalProject')));

    service.setPubRootDirectories(<Object>['file://$pubRootTest']);
    expect(json.decode(service.getSelectedWidget(null, 'my-group')), contains('createdByLocalProject'));

    service.setPubRootDirectories(<Object>['$pubRootTest/different']);
    expect(json.decode(service.getSelectedWidget(null, 'my-group')), isNot(contains('createdByLocalProject')));

    service.setPubRootDirectories(<Object>[
      '/invalid/$pubRootTest',
      pubRootTest,
    ]);
    expect(json.decode(service.getSelectedWidget(null, 'my-group')), contains('createdByLocalProject'));

    // 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;
    service.setSelection(richText, 'my-group');
    service.setPubRootDirectories(<Object>[pubRootTest]);
    jsonObject = json.decode(service.getSelectedWidget(null, 'my-group'));
    expect(jsonObject, isNot(contains('createdByLocalProject')));
    creationLocation = jsonObject['creationLocation'];
    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<String> pathSegmentsFramework = Uri.parse(creationLocation['file']).pathSegments;
    expect(pathSegmentsFramework.join('/'), endsWith('/packages/flutter/lib/src/widgets/text.dart'));

    // Strip off /src/widgets/text.dart.
    final String pubRootFramework = '/' + pathSegmentsFramework.take(pathSegmentsFramework.length - 3).join('/');
    service.setPubRootDirectories(<Object>[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(<Object>[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'));
  }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // Test requires --track-widget-creation flag.
}