Wednesday, September 24, 2014

Still, Still, Still Can't Test Keyboard Events in Dart (Still)


I have spent a lot of time trying to test keyboard events in Dart. A lot of time. And all with no success. Until last night.

As a side note, there is a school of thought that says that you cannot really know a language until you know its weaknesses. I generally have a tough time thinking of these on the spot so I keep a few in my proverbial back pocket. For the longest time, my greatest Dart annoyance has been the inability to create (and thus test) custom keyboard events. If this ever gets 100% fixed, I am going to have to think long and hard for other Dart foibles.

Assuming that my woes really are fixed.

Sadly, last night's solution is likely not an indicator that all is well. I had previously been unsuccessful in generating TextInput events in Polymer.dart elements, so when it worked last night, I though this astounding. It turns out that I have been testing with TextInput events for a while. A bunch of tests in the ICE Code Editor still rely on these to generate useful results.

But I might as well try the keyboard events again. It has been a while. In the ICE Code Editor, we have acceptance tests that read like:
      test("down arrow key moves forward in list", (){
        helpers.typeCtrl('O');
        helpers.typeIn('project 1');

        // project 11
        // project 10 *
        // project 1

        helpers.arrowDown(2);

        expect(
          document.activeElement.text,
          equals('Project 10')
        );
      });
If I hate Dart's keyboard event support, I love its tests. This test says that I:
  1. type Ctrl+O to open the Open Project dialog
  2. type in “project 1” to filter all projects that contain that substring (the three that we have are included in comments)
  3. Hit the down arrow key twice
After that, the expectation is that the active menu item will be “project 10.”

Ideally, a test like that would use keyboard events for just about all of that. Is the letter “o” pressed with the Control key modifier? On keyup in the project filter input, change the list of projects. On keyup when the key is an arrow key, move the active element up or down. But none of those tests actually use keyboard events. Some get away with TextInput events. Some, like the arrowDown() helper function, use absolutely awful hacks:
arrowDown([times=1]) {
  // var e = new KeyEvent('keydown', keyCode: KeyCode.DOWN).wrapped;
  var fake_button = document.query('#fake_down_key');
  if (fake_button == null) return;

  new Iterable.generate(times, (i) {
    // document.activeElement.dispatchEvent(e);
    fake_button.click();
  }).toList();
}
Instead of using keyboard events, I use a fake input field. Awful.

The reason the keyboard events do not work? Because Dart does not populate the keyCode property of the event being dispatched. This tends to make dispatching such events useless, hence the awful hack.

To see if thing have changed, I remove the hacks and reinstate the keyboard event code:
arrowDown([times=1]) {
  var e = new KeyEvent('keydown', keyCode: KeyCode.DOWN).wrapped;
  new Iterable.generate(times, (i) {
    document.activeElement.dispatchEvent(e);
  }).toList();
}
The KeyEvent class is the only way to add character data to keyboard events in Dart. Its wrapped property gives me the usual keyboard event for dispatch. The documentation for KeyEvent looks suspiciously unchanged and indeed, running this test still fails:
CONSOLE MESSAGE: FAIL: Keyboard Shortcuts Open Projects Dialog down arrow key moves forward in list
  Expected: 'Project 10'
    Actual: ''
     Which: is different. Both strings start the same, but the given value is missing the following trailing characters: Project 10 ...
Dang.

The suggested use of KeyEvents in the documentation is of no use to me in tests. I would have to create a Stream wrapper around this particular input field inside the application code, but somehow expose it outside the application so my test could add events to that stream. That might be acceptable if I had a single stream that I needed to expose for testing, but I have dozens.

So it seems the hack must remain. On the bright side, I still have a ready answer for something about Dart that I dislike. Actually, that is not much of a bright side.


Day #193

1 comment:

  1. Yeah it's annoying and what's worse is it's a chrome bug, and until it's fixed in Chrome, there's nothing the dart team can do to resolve it. I'm sure if the dart runtime was in Firefox this problem wouldn't exist.

    ReplyDelete