It took me a while, but I think that I have finally realized that the latest
KeyEvent
changes in Dart are for unit tests, not acceptance tests. I like unit tests for driving new functionality, but rather prefer having acceptance tests around as being better at catching bugs—or at least giving me the confidence that swaths of the application are behaving more or less as designed. Still, unit tests are pretty nice and I hope to put them to use tonight.Toward that end, I give up (at least temporarily) the hope to get the acceptance tests in the ICE Code Editor working. Instead, I am going to see if I can get the ctrl_alt_foo shortcut library working again.
Actually, working again is not quite right. It works now and the build has never once failed. But it works through an absolutely insane hack involving dynamically created JavaScript code. For the record, I opted for that hack not because Dart lacks keyboard handling, but because it lacked the ability to test it well.
With the JavaScript hack and various Dart hacks that preceded it, I tried my best to stay close to existing Dart APIs. Where Dart has
KeyEvent
, ctrl_alt_foo has KeyEventX
. Where Dart has KeyboardEventStream
, ctrl_alt_foo has KeyboardEventStreamX
. My sincere hope is that, even though acceptance tests may be out of the question, the recent KeyEvent
patched to Dart's bleeding edge are sufficient to remove my “X” versions of Dart core classes.From what I learned yesterday, I don't think that removing these is an option just yet. For one thing, I need to keep some of the decoration that I add to
KeyEvent
(e.g. isCtrl, isChar, etc). More pertinent to testing, I need to keep KeyboardEventStreamX
because I need it to return the same stream every time.As I found yesterday, adding custom keyboard events to a stream for testing only works if I am adding to the same stream on which the code is listening. What this means in practice is that it is not (currently) possible to dispatch events to elements in Dart as is possible in JavaScript. Dart limits developers to adding events to streams. If I create multiple streams for an element, then add an event to the last stream, only the last stream will see the event—not all of the streams listening to the same element.
In other words, my existing approach to
KeyboardEventStreamX
is not going to work because it returns a new stream every time its “on” class methods are invoked:class KeyboardEventStreamX extends KeyboardEventStream { static StreamInstead, I switch toonKeyDown(EventTarget target) { return Element. keyDownEvent. forTarget(target). map((e)=> new KeyEventX(e)); } }
ShortCut
, which needs a way to always use the same stream, but also to return a stream that wraps normal KeyEvent
instances in KeyEventX
. I settle on returning a static stream controller stream, which will wrap KeyEventX
instances and also exposing a static dispatchEvent()
method for placing events directly on the single instance of the document.body stream:class ShortCut { // ... static var _stream = KeyboardEventStream.onKeyDown(document.body); static var _streamController = new StreamController.broadcast(); static get stream { _stream.listen((e) {_streamController.add(new KeyEventX(e));}); return _streamController.stream; } static void dispatchEvent(KeyEvent e)=> _stream.add(e); // ... }I then use this to dispatch events as:
type(String key) { ShortCut.dispatchEvent( new KeyEvent('keydown', keyCode: keyCodeFor(key)) );And can test that the events are working with:
test("can listen for key events", (){
_s = ShortCut.stream.listen(expectAsync1((e) {
expect(e.isKey('A'), true);
}));
type('A');
});
That works:PASS: can listen for key eventsBut I don't know if that makes my life much easier. I am guaranteed one stream for listening to events. The
_stream
static variable is assigned when ShortCut
is loaded. If I limit my testing to dispatching keyboard events through ShortCut.dispatchEvent()
that same stream will always be used. So I have side-stepped the multi-stream problem. But the ShortCut.stream
value is just a regular Stream
, not a CustomStream
that supports add()
. Forcing events through dispatchEvent()
seems likely to cause confusion in the future.I will call it a night here and think on this approach.
Day #902
No comments:
Post a Comment