Now that I have my one-class-per-dialog branch merged into master, I am ready to continue “regular” development on the Dart version of the ICE Code Editor. Hopefully I can make good use of the newly separate code files.
I start with the hit-enter-to-save feature. More specifically, I start with a test. And since I have a
new_project_dialog_test.dart file, it is easy to figure out where to put that test. At the bottom of the file, I add: test("hitting the enter key saves", (){
helpers.click('button', text: '☰');
helpers.click('li', text: 'New');
query('input').value = 'My New Project';
document.activeElement.dispatchEvent(
new KeyboardEvent(
'keyup',
keyIdentifier: new String.fromCharCode(27)
)
);
helpers.click('button', text: '☰');
helpers.click('li', text: 'Open');
expect(
queryAll('div'),
helpers.elementsContain('My New Project')
);
});This is a test of the functional variety. It clicks the menu button, selects the “New” menu item and types in “My New Project” for the project name. Then, I fake a key event (because Dart cannot do the real thing) for Enter. Finally, I test that a project was created by inspecting the “Open” menu.In addition to being something of hack, that keyboard event sticks out among my otherwise pristine DOM test helpers. I move it and a couple of thoughtful shortcuts into my
helers.dart:hitEnter()=> type(13);
hitEscape()=> type(27);
type(int charCode) {
document.activeElement.dispatchEvent(
new KeyboardEvent(
'keyup',
keyIdentifier: new String.fromCharCode(charCode)
)
);
}This means that my test can now be written as: test("hitting the enter key saves", (){
helpers.click('button', text: '☰');
helpers.click('li', text: 'New');
query('input').value = 'My New Project';
helpers.hitEnter();
helpers.click('button', text: '☰');
helpers.click('li', text: 'Open');
expect(
queryAll('div'),
helpers.elementsContain('My New Project')
);
});That's much nicer!Of course, my test fails, but it is easy enough to get passing by adding some code. Thanks to my one-class-per-dialog refactoring, finding the class is much easier now. In
new_project_dialog.dart, I listen to the onKeyUp event stream:part of ice;
class NewProjectDialog {
// ...
open() {
// ...
dialog.query('button')
..onClick.listen((e)=> _create());
dialog.query('input')
..onKeyUp.listen((e){if (_isEnterKey(e)) _create();});
}
// ...
}Next, I need to make sure that creating a new project opens the project immediately. It is kind of an obvious feature, but one that I have yet to drive with tests. The test: test("creating a new project opens it immediately", (){
helpers.click('button', text: '☰');
helpers.click('li', text: 'New');
query('input').value = 'My Project';
helpers.click('button', text: 'Save');
editor.content = 'asdf';
helpers.click('button', text: '☰');
helpers.click('li', text: 'Save');
helpers.click('button', text: '☰');
helpers.click('li', text: 'New');
query('input').value = 'My New Project';
helpers.click('button', text: 'Save');
expect(
editor.content,
equals('')
);
});Exercising the menus in the full-screen editor, I create a project, edit the content, save it and then create a new project. At this point the content should be blank, but this test tells me that:FAIL: New Project Dialog creating a new project opens it immediately
Expected: ''
but: was 'asdf'.That is a pretty easy test to get passing—I only need set the content of the editor to the empty string immediately after creating a project:part of ice;
class NewProjectDialog {
// ...
_create() {
var title = query('.ice-dialog').query('input').value;
if(store.containsKey(title)) {
// prevent user from over-writing an existing project...
}
else {
store[title] = {};
ice.content = '';
_hideDialog();
}
}
}With that, the project is up to 59 passing tests.Before calling it a night, I take one more advantage of the newly separate classes and tests. I add some TODOs to the
new_project_dialog_test.dart test file:part of ice_test;
new_project_dialog_tests(){
group("New Project Dialog", (){
test("new project input field has focus", (){ /* ... */}
test("cannot have a duplicate name", () { /* ... */}
test("can be named", (){ /* ... */}
test("clicking the new menu item closes the main menu", (){ /* ... */}
test("the escape key closes the new project dialog", (){ /* ... */}
test("hitting the enter key saves", (){ /* ... */}
test("creating a new project opens it immediately", (){ /* ... */}
});
// TODO: templates
// TODO: blank name behavior
}That makes it easy to pick up with a #pairwithme partner or the next time I have to work on something. I am so glad that multiple code files are cheap in Dart. I am even more glad that I finally put that feature to good use in this project.Day #767
No comments:
Post a Comment