Working at an agency I am involved more and more on projects in which client side code is developed internally then sent out to a separate team for implementation. You provide static HTML, CSS and JavaScript which then get placed into the CMS and brought to life as an actual website. As you can imagine this can sometimes lead to frustrations. However many safeguards you include, handing over your code to someone else is always a difficult thing to do effectively.
In this article I will show you how you can create a JavaScript implementation checker and that will give you more time for drink based activity as your web site and apps are launched quicker and with less unwanted drama!
An all too frequent occurrence
You’ve been working on a project for weeks, fixed all your bugs and send it to be implemented. You hear nothing and assume all is going well then a few days before it’s meant to launch you get an email from the implementation team informing you of bugs in your code that you need to urgently fix.
The 24ways website with a misspelt ID for the years menu
Being paranoid you trawl through the preview URL, check they have the latest files, check your code for errors then notice that a required HTML attribute has been omitted from the build and therefore CSS or JavaScript you’ve hooked onto that particular attribute isn’t being applied and that’s what is causing the “bug”.
It takes you seconds drafting an email informing them of this, it takes then seconds putting the required attribute in and low and behold the bug is fixed, everyone is happy but you’ve lost a good few hours of your life – this time could have been better spent in the pub.
I’m going to show you a way that these kind of errors can be alerted immediately during implementation of your code and ensure that when you are contacted you know that there actually is a bug to fix. You probably already know the things that could be omitted from a build and look like bugs so you’ll soon be creating tests to look for these and alert when they are not found on the rendered page. The error is reported directly to those who need to know about it and fix it. Less errant bug reports and less frantic emails ahoy!
A page with an implementation issue and instant feedback on the problem
JavaScript selector engines to the rescue
Whether you’re using a library or indeed tapping into the loveliness of the new JavaScript Selector APIs looking for particular HTML elements in JavaScript is fairly trivial now.
For instance this is how you look for a div element with the id attribute of year (the missing attribute from top image) using jQuery (the library I’ll be coding my examples in):
if ($(‘div#year’).length) {alert(‘win’);}
Using this logic you can probably imagine how you can write up a quick method to check for the existence of a particular element and alert when it’s not present — but assuming you have a complex page you’re going to be repeating yourself a fair bit and we don’t want to be doing that.
Test scripts
If you’ve got a lot of complex HTML patterns that need testing across a number of different pages it makes sense to keep your tests out of production code. Chances are you’ve already got a load of heavy JavaScript assets, and when it comes to file size saving every little helps.
I don’t think that tests should contain code inside of them so keep mine externally as JSON. This also means that you can use the one set of tests in multiple places. We already know that it’s a good idea to keep our CSS and JavaScript separate so lets continue along those lines here.
The test script for this example looks like this:
{"title": "JS tabs implementation test","description": "Check that the correct HTML patterns has been used","author": "Ross Bruniges","created": "20th July 2009","tests": [{"name": "JS tabs elements","description": "Checking that correct HTML elements including class/IDs are used on the page for the JS to progressively enhance","selector": "div.tabbed_content","message": "We couldn't find VAR on the page - it's required for our JavaScript to function correctly","check_for": {"contains": {"elements": ["div.tab_content", "h2"],"message": "We've noticed some missing HTML:</p><ul><li>VAR</li></ul><p>please refer to the examples sent for reference"}}}]}
The first four lines are just a little bit of meta data so we remember what this test was all about when we look at it again in the future, or indeed if it ever breaks. The tests are the really cool parts and firstly you’ll notice that it’s an array – we’re only going to show one example test here but there is no reason why you can’t place in as many as you want. I’ll explain what each of the lines in the example test means:
name– short test name, I use this in pass/fail messaging laterdescription– meta data for future referenceselector– the root HTML element from which your HTML will be searchedmessage– what the app will alert if the initial selector isn’t foundcheck_for– a wrapper to hold inner tests – those run if the initial selector does matchcontains– the type of check, we’re checking that the selector contains specified elementselements– the HTML elements we are searching formessage– a message for when these don’t match (VARis substituted when it’s appended to the page with the name of any elements that don’t exist)
It’s very important to pass the function valid JSON (JSONLint is a great tool for this) otherwise you might get a console showing no tests have even been run.
The JavaScript that makes this helpful
Again, this code should never hit a production server so I’ve kept it external. This also means that the only thing that’s needed to be done by the implementation team when they are ready to build is that they delete this code.
<script src="sleuth.js" type="text/javascript"></script><script type="text/javascript">$(document).ready(function() {sleuth.test_page.init(‘js_tabs_test.js');});</script>
“View the full JavaScript:/examples/self-testing-pages-with-javascript/js/tests/test_suite.js
The init function appends the test console to the page and inserts the CSS file required to style it (you don’t need to use pictures of me when tests pass and fail though I see no reason why you shouldn’t), goes and grabs the JSON file referenced and parses it. The methods to pass (tests_pass) and fail (haz_fail) the test I hope are pretty self-explanatory as is the one which creates the test summary once everything has been run (create_summary).
The two interesting functions are init_tests and confirm_html.
init_tests
init_tests:function(i,obj) {var $master_elm = $(obj.selector);sleuth.test_page.$logger.append("<div id='test_" + i + "' class='message'><p><em>" + obj.name + "</em></p></div>");var $container = $('#test_' + i);if (!$master_elm.length) {var err_sum = obj.message.replace(/VAR/gi, obj.selector);sleuth.test_page.haz_failed(err_sum, $container);return;}if (obj.check_for) {$.each(obj.check_for,function(key, value){sleuth.test_page.assign_checks($master_elm, $container, key, value);});} else {sleuth.test_page.tests_passed($container);return;}}
The function gets sent the number of the current iteration (used to create a unique id for its test summary) and the current object that contains the data we’re testing against as parameters.
We grab a reference to the root element and this is used (pretty much in the example shown right at the start of this article) and its length is checked. If the length is positive we know we can continue to the inner tests (if they exist) but if not we fail the test and don’t go any further. We append the error to the test console for everyone to see.
If we pass the initial check we send the reference to the root element, message contains and the inner object to a function that in this example sends us on to confirm_html (if we had a more complex test suite it would do a lot more).
confirm_html
confirm_html:function(target_selector, error_elm, obj) {var missing_elms = [];$.each(obj.elements, function(i, val) {if (!target_selector.find(val).length) {missing_elms.push(val);}});if (missing_elms.length) {var file_list = missing_elms.join('</li><li>');var err_sum = obj.message.replace(/VAR/gi, file_list);sleuth.test_page.haz_failed(err_sum, error_elm);return;}sleuth.test_page.tests_passed(error_elm);return;}
We’re again using an array to check for a passed or failed test and checking its length but this time we push in a reference to each missing element we find.
If the test does fail we’re providing even more useful feedback by informing what elements have been missed out. All the implementation team need do is look for them in the files we’ve sent and include them as expected.
No more silly implementation bugs!
Here is an example of a successful implementation.
Here are some examples of failed implementations – one which fails at finding the root node and one that has the correct root node but none of the inner HTML tests pass.
Is this all we can check for?
Certainly not!
JavaScript provides pretty easy ways to check for attributes, included files (if the files being checked for are being referenced correctly and not 404ing) and even applied CSS.
Want to check that those ARIA attributes are being implemented correctly or that all images contain an alt attribute well this simple test suite can be extended to include tests for this – the sky is pretty much up to your imagination.


Comments
Got something to add? You can just leave a comment.
12/12/2009
In this case, where you’re just checking if a certain element exists, <code>$(‘div#year’).length</code> can be shortened to <code>$(‘div#year’)0</code>.
I was also wondering why you’re using the following code:
<pre><code> // load in CSS for the logger var style = document.createElement(‘link’); style.type = “text/css”; style.rel = “stylesheet”; style.href = sleuth.test_page.console_css_location; document.getElementsByTagName(‘head’)0.appendChild(style);
</code></pre>
When you could just do something like:
<pre><code>$(‘head’).append(’<link href=”’ + sleuth.test_page.console_css_location + ‘” rel=“stylesheet” type=“text/css”>’);</code></pre>
(Honest question; maybe there are performance implications I’m not aware of.)
Thanks for this great article. Definitely some food for thought here!
(P.S. This comment looks mangled in the comment preview even though I’m using perfectly valid Textile formatting. I’m confused, so submitting anyway ;) Please fix if necessary! Thanks.)
12/12/2009
Great article Ross; very useful and hopefully inspires testing in a whole new generation of front end coders.
However, I note that
sleuth.jsisn’t linked to anywhere—any chance you could put it on GitHub or somewhere similar for download? Might be helpful to those who want to get started on testing their own sites.PS: Drew, are you sure this comment system takes valid Textile? I can’t generate code blocks (see sleuth.js above).
12/12/2009
Very cool idea. I love seeing testing brought into more places and would love to see more designers handing off designs to us (devs) with test cases already built in.
@Bradley: I pulled the code from the demo and posted it on GitHub. Feel free to fork away: http://gist.github.com/254984
12/12/2009
So instead of spending hours debugging HTML and seeing what the implementers missed I can spend hours writing JSON tests. I don’t think this solves the initial problem.
12/12/2009
So now that this masterpiece is out there and everyone is using it, I expect to see you and your fellow interface developers in the pub much earlier. Problems all solved. Hooray.
12/12/2009
Thanks for all the comments guys.
In regard to open sourcing as the initial code was developed on agency time I unfortunately aren’t allowed to however some friends and I are looking to re-write and address a few of the issues I currently have with it and that will be open-sourced and on github!
Currently it’s in a very incomplete state as we try and re-factor the API (potentially removing the need for JSON based tests) but as we head into Christmas time I’ll be getting more time to work on it and hoping to get it running along quicker.
Follow the project at http://github.com/rossbruniges/os_sleuth and if you want to contribute I’d be very happy indeed :)
@Mathias – good points both. Due to the comment formatting I can’t be 100% what you’re suggesting but personally I just find the syntax a bit nicer using .length.
Have to remember that jQuery will always return a jQuery object regardless of if it matches the selector or not so the .length is needed to check that it both returns something and what it returns isn’t empty.
The second point is just a little bit of code creep. The version I created at LBi first checks to see if jQuery is included and if not loads it into the page. Inserting the stylesheet is one of this things it does before loading the library so therefore needs to work without it which is why the more verbose syntax is used. It’s by no means essential.
@Russell – it’s true that you could spend ages writing JSON tests but I’ve not found that when using it on my projects. You can kinda guess what things the external teams could get wrong and provide the tests for that.
Where it will save time is with off-shore development teams – those who are maybe at work when you’re at home asleep. If you can provide them hints at their errors they won’t need to wait for you to be in the office to start fixing them.
Some things will always go wrong and certainly to catch every single potential problem could be counter productive but if you pick your battles it should prove useful (it has been for me). I’m aware it might not be helpful in all situations though.
14/12/2009
Just a minor thing. The code snippets contain the character ‘ (LEFT SINGLE QUOTATION MARK, U+2018) instead of ‘ (APOSTROPHE, U+0027). This will trigger an annoying “illegal character” error if you copy and paste the code without noticing the wrong character.
So please use U+2018 only in English text, not in the code, where bare apostrophe should be used instead. Maybe you’re preprocessing the text, you need to skip such preprocessing on the code block (never used Textile or know what it does, so I don’t know).
Commenting is closed for this article.