It’s high time I talked a little about the coding I do. Since my job transformed into a “User Interface Specialist” I’ve been back to coding — implementing the user interface for web applications. After years of being a backseat developer (i.e. doing QA for other developers) and choosing to try out the BA field instead, this change was thrust upon me. Go figure. Anyway, coding is a lot of fun. Although the pay isn’t quite as good. It is good honest productive work though. Gotta love that!
I contribute a lot of the code towards that in my own time. I rationalise giving up my time because the code has been made open source, a few of us have “joined forces” on a project named “use the forces”, to collaborate on code and products which we aren’t afforded time to collaborate on at work. We are compelled into meetings to discuss improving productivity, just not encouraged to work together productively. There is paperwork to write after all! Sorry if that sounds a little scathing: I’m a worker, not a talker (outside my blog!), and I have high expectations that leadership have yet to exceed… At any rate, I can make open source contributions as often as I wish, and I can enjoy go home on time day every day at work. It’s sweet and I Like It.
That was a long ramble. For people who wonder how I do what I do, why I do it, what it’s like, here is an insight into a Saturday afternoon coding session. I wrote these notes while coding, for this post. This is what coding is like for me. Distractions, warts and all.
do an update or checkout, make sure the source is the latest
fire up firefox with firebug (portable apps), it’s 3.5.2
good idea to test with multiple browsers, but one while developing. don’t have IE6.
talked myself into runnning IE8. Press F12 to get developer toolbar, toggle to IE7.
using e-texteditor (like textmate) on windows
run the tests. forces tests are in test/jquery-1.3.2.html (this is a wrapper to run all the tests)
find the file and right click and choose open with Firefox (I already had firefox running so it used the portableapps one and not the installed version — it doesn’t have the add-ons I need!)
may be a little slow downloading YUI to run tests (especially if downloading a game from steam in the background over 3G broadband)
1 test failing. find it, fix it, save it. run again.
can run in both Firefox and IE8 in parallel (even if encoding video in the background, if you are an avid multitasker, I recommend a multicore CPU — it’s essential for GTA IV anyway!)
tests passed, all good, don’t forget to check in (even if being distracted by Buffy – the first episode where Spike and Drusilla appear – and the kids playing trains)
now we’re ready to code something new.
(after we send the kids back to the Wii and outside for a splash in the wading pool)
there’s a bug discovered in the confirm validation whilst playing with the UI, which reminds me that there’s a demo form so you can interact with the UI whilst working. whilst coding to tests should be enough, I like the immediate feedback from trying it out (when coding UI), and there’s no real point running the test if I know it isn’t working anyway.
here’s the demo form. it has email and confirm email, and they are setup to work, like this.
type ‘firstname.lastname@example.org’ and ‘foo’ and tab away (validation runs onblur/onchange) the error appears.
correct the error, ‘foo’ becomes ‘email@example.com’ and the confirm validation is happy.
but what if it happened the other way? let’s reload the form.
firstname.lastname@example.org and email@example.com … confirm validation correctly highlights the problem.
fix the first field and tab on through … the validation hasn’t kicked in.
This is exploratory testing. It’s also a good old fashioned user-centred design technique “put yourself in their shoes”, thinking through how users will interact with the application. Don’t just think of one way they may use it, think of all the different ways they may use it. It’s not just random typing and clicking.
does Buffy really plan to use that machete on spike? oh. on the salad. ok.
look away from the tv.
Is this a bug?
You do have to decide if issues found are bugs, or possible features, or just interesting anomalies to note.
This is a bug, forces validation must be intuitive and easy to use and this clearly fails that. That’s part of the design goals.
now, at this point we could start solving the problem and I already have an idea in my head it’s related to the onblur/onchange trigger happening only on the con
firm field. I probably need to track the dependency between the two fields and run the validation when either changes.
This line of thought is very dangerous, because it overlooks a more important problem.
ALL THE TESTS PASSED.
if there is a bug, shouldn’t that show up? it’s passing all the tests, so there is a gap in the test coverage.
before we do anything else, let’s capture the problem as a test. our test should set the wrong value for email, the right value for confirm email, and verify the confirm validation message appears. it should then correct the email value, and verify that the error has disappeared. This test should fail, because we haven’t fixed the code yet. But, that’s important too, that’s how we verify the test is coded correctly.
ah Angel playing at Angelus. Cool, but not the same as when Angelus really takes over.
Let’s code …
coded a new test. it’s in unit/forms.ui.js …
must confess not having a great handle on unit vs integration vs acceptance tests. This feels like it might be more than a unit test, but the important thing for now is getting the tests in place. I am going to need to sort it out one day.
The new test fails, as expected.
Unfortunately it’s failing in the wrong place, the error message didn’t even appear. Debug the test. I insert “this.wait(null, 10000)” in to pause the test so I can see it.
aha, I forgot the setup step. I have to specify that the field needs confirm validation.
That’s done with this: $(‘#confirm-email’).forces_attr(‘type’, ‘confirm’);
I’m going to place that in the setUp method of the test case (see YUI3 for more) because it seems likely I will forget this again. I hope it won’t upset any other tests, but there’s an easy way to check this — run the tests again!
remembering to remove that 10 second wait will help. the test failed on it. which is useful when debugging. and extra useful in this instance for allowing me to grab a screenshot. and I saw the UI displaying the right thing so I have some confidence.
remove the pause, run the tests again.
everything passed. This is not good. Our test has not caught that bug. Let’s put the pause back in and find out why.
The UI is showing the right thing. The value is valid and confirmed, but the alert message is still being displayed. The test is supposed to check it doesn’t show that message. Something is wrong with the test.
ah, I was going to fix using a console log. But I’ve noticed, it is using $(‘#email-confirm’) instead of $(‘#confirm-email’). D’oh. Mistakes like this are the bane of any coder’s life. Especially when they don’t throw any syntax errors.
I can see this error in the previous test (that I copied from). A bit disturbing. Search and replace.
success! the test now fails at the expected point. we’ve now achieved the “Red” from red, green, refactor. We have a working test.
Don’t check in at this point. That would mean the next person who updates their copy from the project has this test failing and probably no idea why. They’ll try to fix it, we’ll be coding ourselves, it could lead to conflicts. It also erodes faith in the build if you update to the latest source and it’s broken. Like when we went to start and had to fix that other broken test. Not good.
So hold off checking in!
But it is a good time to take a quick break and help daughter play Purble place on Windows7. Now that she’s setup and happily occupied …
What comes after red? Green. We have to fix the code now so that it passes the test.
I’m going to change the way confim works, as I said before:
It currently uses this: $(‘#confirm-email’).forces_attr(‘type’, ‘confirm’)
I don’t really like this, the “forces_attr” function is a pseudo for attr() and it makes sense to use attr(‘type’, ’email’) as email is a valid type (HTML5).
There’s no such thing as the “confirm” type.
How about $(‘#confirm-email’).forces_isConfirmationFor(‘#email’) ? I like the explicit way this associates the two fields together. We’ll need to change the tests. It’s an easy change. As with any change, run those tests again.
Let’s solve that.
Coding time! Write a new stub function. It belongs in jquery.forces.forms.core.js
Run the tests.
This has broken the old confirmation validation as well. That’s ok. It needed refactoring, you can see it was a bit heavy on processing, running through all form fields to find the previous one. This direct association will be more efficient.
Although I don’t like the idea of the confirmation being of a field elsewhere in the form, but that may be personal bias. Hmm… I could make this method isConfirmationOfPreviousField() and hard code that. But maybe another designer will have a good reason for separating the fields. In fact, I read something recently about that. Let’s allow it.
Remove the old validation.
*sigh* youngest daughter tipped a whole bunch of paper on the floor. That’s a bit distracting. I better pick it up. ok, tidy again.
let’s recode the validation. We know the tests are ok, let’s just code until it works. I’m not describing my coding here today.
much excitement over the matchup game. two excited girls. “i won daddy!” that’s lovely girls.
I’m coding. Ok, now they’re making me laugh. ok, discipline. code.
Let’s flesh out this forces_isConfirmationFor method.
It occurs to me that now that I am tracking the associated field, I’d like that exposed. The UI code will need to know that, as it knows what label to display (e.g. doesn’t match “Email”)
let’s put a test in for that. That’s so simple it really will be a unit test. I’m putting it in unit/forms.js because it relates to core (not UI) functionality. new test case to find the confirmation field. note that it uses the same method, but passes no arguments. That seems to make sense to me.
Back from emergency track repairs to the new Thomas train set and with relatives arriving for a BBQ, may not accomplish much more. Oh, BBQ later, this is whipper snipping time. I have the best inlaws.
Let’s try to get that test to pass.
First attempt at code, run the tests. Fails, but we get an interesting message.
ok, fixed. I just had to remember we passed “#input1” to the function, not a jquery object, and the function had to turn it into a jquery object. I wonder if it works if we pass a jquery object? That deserves a new test.
It passes. Fantastic. That’s jquery working nicely for us.
Note the comma placement in adding that test. If an extra comma slips in, IE becomes very unhappy. It doesn’t like a comma after the last item in an object. It caught me out recently, so it is fresh in my mind. Let’s check the tests in IE8. It’s much slower. I am looking forward to the performance improvements IE9 promises.
Good. It returns the same results as FF. Let’s recap.
We can now specify a field is a confirmation for another field, and find out what that field is. Those tests
pass, we’re confident that functionality will continue to work.
Let’s work on addressing those other tests, and implement the actual validation. We’ll reproduce the functionality we had before. When the confirmation field is updated, it’s validated. (That won’t solve the original bug).
Doesn’t seem like we’ll need much code. And now I realise I don’t have a good test for this. The tests are based on checking the UI shows the correct value. I just want to test that the field state is correctly flagged as “invalid”. If the field is invalid, the UI should work anyway. But because I’m refactoring I need to work on the UI too. I want more test coverage so I can get confidence this bit is working before I code the UI. Really, I should have those tests already in place. Still learning the ropes and anticipating the right tests.
I will comment out the new code and write the tests (to get them “red”)
Refactored to combine the two previous tests, and added two new ones to check valid/invalid status.
And conveniently missed out a comma which caused a syntax error. Easily fixed.
Good thing about running these tests frequently is you find these things quickly. I probably should be pairing to avoid more of these mistakes in the first place. I’ve made so many mistakes so far, can you imagine if I hadn’t tested anything yet? Sorting through it all later would be a nightmare!!
Some more bugs in the test fixed, and we’re at the right state now.
4 tests are failing. That’s a lot, I think one at a time is ideal. I don’t think red, green refactor is about lots of failing tests! You want a much shorter cycle between red and green. Anyway, 4 tests failing. 2 are the pre-existing UI ones, 2 are to test how the confirmation validation is implemented (not how the UI looks).
Let’s see if uncommenting our code fixes that.
Nope, there was a bug. 11 errors showed up.
When that happens, you know something what you just changed has got a (usually simple) flaw. In this case, “isConfirmationFor()” vs “e.isConformationFor()”. Simple. But important! One can’t call methods without objects (well, you can, but that’s a ninja secret).
Fixing that and we return to 4 errors. That’s a bit better, but means the implementation did not pass the tests. Why not?
Let’s introduce some logging … hmm, it’s not logging anything for those two tests. Is it even validating anything? More logging. Nope, doesn’t seem to be. Aha, in that test case I will need to enable validation for the test form.
That was indeed the problem with the tests, they pass now.
So, we know that validation works for the confirm field (but not the source) field. It’s not working at the UI level though, let’s fix that up.
Wow Super Smash Bros Mega HEAVY brawl is a lot of fun. But it doesn’t get the coding done. That was very distracting Daniel. But great fun!
Quick run through and … we’ve restored things to where they were!
Now, I’m cooking. Not “cooking” as in “coding successfully”, but “cooking” as in “cooking at the BBQ”.
When I’m back, we can fix that bug so the confirmation validation occurs when either field is updated! Write some more unit tests first …!
* * *
Um, didn’t get to any more coding that Saturday. I ate tea, watched Robots, played L4D2 scavenge mode. It was awesome! More coding tomorrow. Maybe.
Ok, 5:51 pm the next day …
running the tests is even slower now YUI3 needs to download over shaped (64 kbps) broadband. If you can call it broadband.
1 test failed. It’s that bug, the confirm alert doesn’t go away when the original field is corrected. I’m gonna write another test for this, that’s not at the UI layer.
Test works. (It failed at the expected point.) Let’s code the validation (not the UI) so it works.
When the confirmation is setup, it now stores the “confirm” field as a “validation dependency” for the original field. When validating, it will check for dependencies and validate them. I’m aware that if this was called twice, things will go a little awry.
The second call will result in #email having a validation dependency for confirm2, that overwrites the one for confirm1. I don’t consider this a bug, because I don’t currently need to deal with more than one validation at a time. So I have not coded tests around it. Note that $(‘#confirm1,#confirm2’).forces_isConfirmationFor(‘#email’) should work anyway. But consider that an undocumented feature. It is certainly not a requirement so there are no tests guaranteeing it will work.
Tests run and there are lots more failures. Nothing new should be failing, there must be an error in the code changes I made.
This time, I’m using undo to return the code to the state it was. I don’t feel like debugging. Yes, that’s reset everything nicely. Sometimes this is the more efficient approach. You learn to judge when. Depends how much code you invested and how wrong things went!
Coding attempt #2.
OK, the validation dependency is now stored and there’s a test to verify that part works.
Now to invoke that dependency during the onchange validation. It’s not needed onsubmit, because ALL fields within the form are validated anyway.
Wow. Everything just passed with one line of code!
Because jquery runs on an array of objects, it was a snap to add the dependend fields in. Basically the change was control.forces_validate() to control.add(control.data(‘dependents’)).forces_validate().
I love jquery.
The UI has passed as well. I guess it was already setup to work when the valid/invalid events fire.
Still, let’s test the demo form and see it in action.
The demo form isn’t working. It’s still using forces_type(‘confirm’). Let’s update it to the refactored version.
Important to note that this new validation will not run against empty fields (this is by design). If you *require* confirmation, then you need to make the field required also. Otherwise, it implements optional confirmation, as in it may be omitted, but if present must be valid. I don’t have a use case for that, but it is the design pattern I follow for all validation.
This is the code people are likely to want.
There are many ways to rewrite the above, but that is a simple way to show how it works.
Let’s do an IE8 check of the tests. All passed.
Ready to check in, preparation steps.
Run compile.bat to combine and minify the JS code.
Run the tests against the minified version. I like to do that in Chrome, just for something different.
Let’s check that code in. Don’t forget a handy comment.
We’re done! Let’s eat tea.
Believe it or not, that’s how I code. It is far less adhoc than it seems. And when I am less tired, I might even add in all the extra screenshots I captured and this post may even make sense.