Monday, September 29, 2014

Testing Angular and Polymer.dart


Regardless of the solution that I use to double bind variables between Angular.dart and Polymer.dart, I need to test it. Without tests, I will have no warning when new versions of the libraries break the last working version.

Before getting to the actual test, I need to make a change to the pubspec.yaml. Oddly, the latest versions of Angular.dart and Polymer.dart are incompatible—they have conflicting dependencies. Luckily, Dart Pub comes with a built-in solution for this, dependency_overrides. This setting lets application developers say, “I love you package X and I know you think that can only support version 0.10 of this other library, but let's give version 0.11 a try for Chris, OK?”

To get Angular.dart and Polymer.dart working, I had to convince Angular.dart that it might run with the following dependency_overrides:
name: angular_example
dependencies:
  angular: any
  polymer: any
  angular_node_bind: any
dependency_overrides:
  args: '>=0.10.0 <0.12.0'
  code_transformers: '>=0.1.4+2 <0.3.0'
  html5lib: '>=0.10.0 <0.13.0'
dev_dependencies:
  unittest: any
transformers:
- angular:
    html_files:
      - web/partials/custom.html
- polymer:
    entry_points:
    - web/index.html
    - test/index.html
The problem is that the next version of Polymer.dart might depend on args version 0.12, which is incompatible with Angular.dart and my overrides. So, for book testing purposes, I need to loosen those overrides even further:
name: angular_example
dependencies:
  angular: any
  polymer: any
  angular_node_bind: any
dependency_overrides:
  args: any
  code_transformers: any
  html5lib: any
# ...
A quick pub upgrade and... I am still using the same versions of these overridden dependencies:
Warning: You are using these overridden dependencies:
! args 0.12.0+2
! code_transformers 0.2.3+1
! html5lib 0.12.0
Nothing has changed, but I am ready for any future changes to Angular.dart or Polymer.dart that have proven troublesome in the recent pass.

With that, I am ready to write a double binding Angular.dart and Polymer.dart test. I already have testing page objects, so this is mostly just re-organizing to pull in the Angular code. The test becomes:
    test('angular sees the updated pizza state', (){
      schedule(()=> xPizza.addWholeTopping('green peppers'));
      schedule((){
        var angular_el = document.query('pre');
        expect(
          angular_el.text,
          contains('Whole: [green peppers]')
        );
      });
    });
I am using angular_node_bind to bind the current state of the pizza to an Angular scope variable inside a <pre> element. If I tell the page object to add green peppers to the whole pizza, the Polymer element should update itself with new values, which should trigger the Angular element to trigger itself to update.

There may be a simpler way to accomplish this, but the dumbest way to add an Angular application in test mode is to add it right to the test HTML:
<!DOCTYPE html>
<html ng-app lang="en">
<head>
  <!-- Load platforms polyfills -->
  <script src="packages/web_components/platform.js"></script>

  <!-- Load component(s) -->
  <link rel="import" href="packages/angular_example/elements/x-pizza.html">

  <!-- The actual tests -->
  <script type="application/dart" src="test.dart"></script>
  <script src="packages/unittest/test_controller.js"></script>
</head>
<body>
  <ng-view></ng-view>
</body>
</html>
Unfortunately, this means that I cannot pull the main.dart code that I have been using directly into my tests—both invoke initPolymer(). For now, I add a stripped-down version of the Angular app directly to my test's setup:
    setUp((){
      schedule(()=> Polymer.onReady);
      schedule((){
        var app = new PizzaStoreApp()
          ..bind(NodeBindDirective)
          ..bind(RouteInitializerFn, toValue: storeRouteInitializer);

        return applicationFactory()
          .addModule(app)
          .run();
      });
      schedule((){
        var _completer = new Completer();
        new Timer(new Duration(milliseconds: 50), ()=> _completer.complete());
        return _completer.future;
      });
      schedule((){
        _el = document.query('x-pizza') as PolymerElement;
        xPizza = new XPizzaComponent(_el);
        return xPizza.flush();
      });

      currentSchedule.onComplete.schedule(() => _el.remove());
    });
That is not too horrible, though it might be nice to move the Angular-specific code elsewhere—especially if it can be shared between the web application and test. The important line in there is the inclusion of NodeBindDirective (from angular_node_bind), which allows Angular to see updates to Polymer attributes. And indeed, if I remove that line, the test fails, but if I restore it, I find:
PASS: Angular sees the updated pizza state from the Polymer
I should note that I also need to include a 50 millisecond delay before my test will pass. The Angular application needs this time to load its custom partials and spin up. It would be good if I could eliminate that seemingly arbitrary delay.

But, for now, I am happy to have a useful test for Angular and Polymer playing nicely. This is a good stopping point.


Day #198

No comments:

Post a Comment