How to query the shadowDOM in an Angular component

2019-06-08 08:44发布

问题:

I have this unit-test setup and want to examine the shadowDOM of the <rating> element. But the shadowDOM has no childNodes.

In the code below I try to examine the elements shadowDOM in the getStars() method.

Is there a flaw in my approach? Should this work?

library bootstrap_angular.test.elements.rating;

import 'dart:html' as dom;
import 'dart:async';
import 'package:unittest/unittest.dart';
import 'package:unittest/html_enhanced_config.dart';
import 'package:angular/angular.dart' as ng;
import 'package:angular/mock/module.dart' as ngMock;

import 'package:bootstrap_angular/rating/rating.dart';

void main() {
  useHtmlEnhancedConfiguration();

  ng.Scope _rootScope;
  dom.Element _element;
  ngMock.TestBed _tb;
  ngMock.MockHttpBackend _backend;

  setUp(() {
    Future f;
    try {
    ngMock.setUpInjector();

      ngMock.module((ng.Module module) {
        module.install(new RatingModule());
      });

      ngMock.inject((ng.Scope scope, ngMock.TestBed testBed) {
        _rootScope = scope;
        _rootScope['rate'] = 3;
        _tb = testBed;

        f = dom.HttpRequest.getString('/bootstrap_angular/packages/bootstrap_angular/rating/rating.html')
        .then((ratingTemplate) {
          _backend = new ngMock.MockHttpBackend();

          assert(ratingTemplate.contains('<i '));
          _backend.expect('GET').respond(ratingTemplate);

          _element = _tb.compile('<rating value="rate"></rating>', scope: _rootScope);
          var element =_element.shadowRoot.querySelector('i');
          _rootScope.$digest();
        }).catchError((e) => print(e));
      });
    } catch(e) {
      print(e);
    }
    return f;
  });

  List<dom.Element> getStars() {
    print(_element.shadowRoot.querySelectorAll('i'));
    return _element.shadowRoot.querySelectorAll('i');
  }

  test("rating component", ( ) {
    expect(getStars().length, equals(3));
  });
}

The elements template HTML returned from the HTTP request

    <span ng-mouseleave="ctrl.reset()">
      <i ng-repeat="r in ctrl.range" ng-mouseenter="ctrl.enter($index + 1)" ng-click="ctrl.rate($index + 1)" class="glyphicon" ng-class="ctrl.stateClass($index, r)"></i>
    </span>

回答1:

Your basic approach should work.

For the AngularDart tests, we have a JQuery class in test/_specs.dart which handles peeking into the shadow DOM. For example see the JQuery.textWithShadow() method.

Looking at the code you posted, I suspect you have a race condition in that you aren't waiting for the compile to complete. Depending on the rating component, the compile may be asynchronous: possibly you need to flush the MockHttpBackend.

In the AngularDart tests, we deal with this using the async() concept, which will fail your test if you leave Futures hanging. See lib/mock/zone in the AngularDart Github repo.