Friday, May 31, 2013

Are They Yaks or Are They Shiny? Ooh Shiny!

‹prev | My Chain | next›

The scariest remaining work in the Dart version of the ICE Code Editor probably resides in the button toolbar that will reside at the top of the screen. The “Hide Code” / “Show Code” button toggles are pretty easy, but the “Update” button could be tricky. Or it could be really, really easy. Either way, it scares me, which is a pretty good indication that I need to focus there tonight.

The “Update” button itself updates the preview layer to reflect any code changes:



Since that has to push the changes into an iframe, I am a little concerned that my test will get overly asynchronous. Dart does async tests pretty well, but I have been writing some wonderfully procedural functional tests of late. I am unsure how these will fit in.

The update button also includes a checkbox toggle to enable/disable auto-updates of the preview layer after the programmer has stopped typing. I am really unsure how that is going to work in my tests since it is impossible to create keyboard events in Dart and since the delay is 2 seconds — I begrudge tenths of seconds that get added to the tests.

Anyhow, I start by simply adding the buttons:
class Full {
  // ...
  _attachToolbar() {
    var toolbar = new Element.html('<div class=ice-toolbar>');

    toolbar.children
      ..add(_updateButton)
      ..add(_hideCodeButton)
      ..add(_mainMenuButton);

    el.children.add(toolbar);
  }

  get _updateButton {
    return new Element.html('''
        <button>
           <input type="checkbox"/> Update
         </button>'''
      );
  }

  get _hideCodeButton {
    return new Element.html('<button>Hide Code</button>');
  }
  // ...
}
Easy enough: I add two new buttons to the main toolbar, building both from HTML. I make use of Dart getters to cut down on parenthesitis. I also use multi-line strings when the update button threatens to get too wide. Ah, Dart, there are so many reasons why I love you. Except...

That simple change breaks 14 tests. What?

It turns out that many of my functional tests queried for the first <input> element in the document when trying to fill out dialog forms. Unfortunately, this change introduces another input—the checkbox <input> that will eventually toggle code auto updates.

The simple fix is easy enough. In each test, I query for the first text input field:
    test("can open the rename dialog", (){
      helpers.click('button', text: '☰');

      helpers.click('li', text: 'New');
      query('input[type=text]').value = 'My New Project';
      helpers.click('button', text: 'Save');
      // ...
    });
Ooh! I see an opportunity for another test helper here. I don't think I am going to get to the Update button tonight (unless in tonight's #pairwithme), but I think I can improve that helper to read:
    test("can open the rename dialog", (){
      helpers.click('button', text: '☰');

      helpers.click('li', text: 'New');
      helpers.typeIn('My New Project');
      helpers.click('button', text: 'Save');
      // ...
    });
If I can make it work, that would be very nice. The entire test setup could then be expressed in terms of “click on the button with this text” or “type in that value”. I think that worth the detour. Thanks to document.activeElement, which points to the currently active element in a web page (or document.body if nothing is active), I can write that helper as:
typeIn(String text)=>  document.activeElement.value = text;
In order for that to work, the various input fields in the UI have to be active, but we have been painstakingly doing just that over this past week (for a better UI experience).

In the end, I convert 14 instances of the query-element-set-value approach to the new helpers.typeIn() approach. The tests remain nice and snappy and are even more readable. That seems like a good win for the night—even if it was not the win I set out to get.


Day #768

No comments:

Post a Comment