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