Friday 26 April 2013

Test Driven UI Development with Node, Mocha and RequireJs (part 2)

Last time, we set out a framework for doing test driven development with our front end code.

Now we're going to build on that scaffolding to create a simple web app, adding modules as necessary as we go.

So we need a spec for our web app;  lets go with something simple from Codingdojo's Kata Catalogue.   The Roman Numeral converter is a nice simple one.

First we'll lay out a quick and dirty UI.

Next we will need some additional modules to select and manipulate DOM elements.  Instead of using the monolithic jquery, I'm going to use some simpler modules which should lead to easier to understand code.  In addition they should keep the network footprint of the app a little lower if we wanted to make it nicer for mobile browsers.

Let's start with qwery - a selector module - to get at our target elements.  Install it, and add to app.js

npm install qwery


The first problem is that under mocha, qwery will not even load without errors.  Obviously it will work in the browser, but we need to fake being a browser so that qwery will load.

ReferenceError: document is not defined

Fortunately there's another module that can help us out here - jsdom.  Install it and add it to the appTest.js to fake the global document object.

npm install jsdom

Now the tests run again, so we can start adding a real test.

Now that we actually want to start using qwery we find that it is not structured in such a way as to make it easy to break our dependency on it.  The same is true of many other browser modules.  So what do we do?   We store a local variable that references qwery and use this to break the dependency in our app code, using sinon to provide our stub interface.


So now we have selector code and a fake selector.  If we want to make it do something useful then we need a DOM manipulator.   Enter bonzo, a lightweight DOM manipulation utility.   Qwery and bonzo are both included as part of a set of utilities called ender.   The names of the modules mostly come from characters in Orson Scott Card's book "Ender's Game", which is quite a good read.

npm install bonzo

Bonzo needs a little bit more faking of browser environment, and similar dependency breaking code.


Now we need to be very careful about what our fake selector and DOM tool return, we need to make sure that it is correct.  So lets look at it in a browser - www.javascriptoo.com offers a useful service that you can use to check the API.   You can edit the html in the left hand window and the results will be shown on the right.

So our fake selector output is just going to be passed to the DOM tool, but the DOM tool's output needs to include stub functions that we will call.


Green test, yay!

But now we can't find qwery or bonzo to include it in the build process...

Tracing dependencies for: app
Error: ENOENT, no such file or directory '.../src/qwery.js'


Symbolic links to the minimized node module versions in src should do the trick.

cd src
ln -s ../node_modules/qwery/qwery.min.js qwery.js
ln -s ../node_modules/bonzo/bonzo.min.js bonzo.js
cd ..

Run the build process again, and now we have it in the browser - lets make a couple of tweaks to prove it works there.


So now that works - and you get a console message when you load the page in a browser.

Field contains 12

To finish off with the DOM manipulation we need to do two more things; set up a function that inserts the output into the DOM, and cause the init method to attach a click handler to the button that does the work, then we can crack on with the logic of the problem itself.

Writing the output is more of the same.


The click handling requires a new module, bean, which is just a simple event api.  Install it and create a symlink for the front end.

npm install bean
cd src
ln -s ../node_modules/bean/bean.min.js bean.js
cd ..

We can replace the skeleton test for init that we made last time now. Note especially here the use of the bind call to ensure that we don't end up with this referring to the DOM element. Also bean only operates on single elements, not on sets of them.

So all that remains is to write the logic of the Roman Numeral conversion, and link up to it in the action function.


Next time we'll run through the Roman Numeral code kata to finish off our little project, which will result in a working (if simple) web app.

First post in the series: here
Final post in the series: here

No comments:

Post a Comment