Monday, May 13, 2013

Getting Started with UI Workflow Testing in Dart

‹prev | My Chain | next›

I think it makes sense to work next on the full-screen version of the ICE Code Editor. Over the weekend I was able to get the persistent data store into decent shape. I have the feeling that there are still some gaps in what it needs to do, but the best way to expose those gaps is likely to work on a separate feature that relies on the Store class.

I am fairly eager to see how well the unit testing that is built into Dart does for testing UI workflow. I realize that unit testing and workflow testing are something of a contradiction in terms, but I think this might work.

The full-screen version of ICE is more of a mini-IDE in the browser. It incorporates the editor + preview layer that is already in ICE, but it also exposes various buttons and menus that support a light-weight, kid-friendly (for 3D Game Programming for Kids) development workflow. I ought to be able to create an instance of the ICE full-screen editor and click a button or two in a test setUp() block. From there, I should be able to test next steps. It will not be perfect, but anything is infinitely better than I could do with the JavaScript version of the code editor.

I start by defining a trace bullet test:
import 'package:unittest/unittest.dart';
import 'dart:html';
import 'package:ice_code_editor/ice.dart';

main() {
  group("initial UI", (){
    test("the editor is full-screen", (){
      var it = new Full();
      var el = document.query('#ice');
      expect(el, isNotNull);
    });
  });
}
This is a trace bullet test because the current expectation is not useful behavior to test in ICE.Full—ensuring that there is an ice DOM element is basic stuff. Once I have that working, I will change the test to verify that the width of the editor element contained within the ice DOM element is full screen.

First things, first: I have a failing test:
FAIL: initial UI the editor is full-screen undefined:1
  Caught 'file:///home/chris/repos/ice-code-editor/test/full_test.dart': Error: line 15 pos 20: type 'Full' is not loaded
        var it = new Full();
                     ^
  malformed type used. 
I follow the change-the-message or make-it-pass-cycle until I have it passing. I have to add a new part to my the ice.dart file in my Dart package:
library ice;
// import library and package dependencies here... 
part 'editor.dart';
part 'store.dart';
part 'gzip.dart';
part 'full.dart';
Then I define the Full screen class in full.dart as:
part of ice;

class Full {
  Element el;

  Full() {
    el = new Element.html('<div id=ice>');
    document.body.nodes.add(el);
  }
}
Now that I have hit the target with my tracer bullet, it is time to do it for real. Since I need the ACE code editor (the actual editor part of this) to load up, I make use of the Future in the Editor class. Once that Future is completed, I expect that my test can be run. Thankfully, async tests in Dart are pretty straight-forward:
test("the editor is full-screen", (){
      var it = new Full();

      _test(_) {
        var el = document.query('#ice');
        var editor_el = el.query('.ice-code-editor-editor');
        expect(editor_el.clientWidth, window.innerWidth);
      };
      it.editorReady.then(expectAsync1(_test));
    });
Once the editorReady Future has completed, then I run my async test, which I wrap in a private _test() function for clarity. The actual test that I hope to make pass says that my full-screen editor should have the same width as the browser window. Thanks to styles that I am already applying in the Editor class (ACE + a preview layer), I only need remove margins from the document's body:
part of ice;

class Full {
  Element el;
  Editor _ice;

  Full() {
    el = new Element.html('<div id=ice>');
    document.body.nodes.add(el);

    _fullScreenStyles();
    _ice = new Editor('#ice');
  }

  Future get editorReady => _ice.editorReady;

  _fullScreenStyles() {
    document.body.style
      ..margin = '0px'
      ..overflow = 'hidden';
  }
}
With that, I have my first useful UI test passing for the full-screen ICE Code editor:
PASS: initial UI the editor is full-screen undefined:1
 undefined:1
All 1 tests passed.
I am a little concerned with the need for the expectAsync in the test. To be sure, it is good that Dart supports it, but I can envision that getting in the way of future tests—especially if those workflow tests require additional async checks. Ah well, that is something that time will answer for me. For now, I am satisfied with a passing tests.

Update: my #pairwithme pair, Santiago Arias, found that I was not testing what I thought. Specifically, it was not sufficient to test just the width since a zero-height <div> would satisfy my test. In fact, I did have a zero-height <div> there. Happily, we were able to come up with a better solution.



Day #750

No comments:

Post a Comment