{"rowid": 190, "title": "Self-Testing Pages with JavaScript", "contents": "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.\n\nIn 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!\n\nAn all too frequent occurrence\n\nYou\u2019ve 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\u2019s meant to launch you get an email from the implementation team informing you of bugs in your code that you need to urgently fix.\n\n The 24ways website with a misspelt ID for the years menu\n\nBeing 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\u2019ve hooked onto that particular attribute isn\u2019t being applied and that\u2019s what is causing the \u201cbug\u201d.\n\nIt 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\u2019ve lost a good few hours of your life \u2013 this time could have been better spent in the pub.\n\nI\u2019m 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\u2019ll 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!\n\n A page with an implementation issue and instant feedback on the problem\n\nJavaScript selector engines to the rescue\n\nWhether you\u2019re 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. \n\nFor 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\u2019ll be coding my examples in): \n\nif ($(\u2018div#year\u2019).length) {\n\talert(\u2018win\u2019);\n}\n\nUsing 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\u2019s not present \u2014 but assuming you have a complex page you\u2019re going to be repeating yourself a fair bit and we don\u2019t want to be doing that.\n\nTest scripts\n\nIf you\u2019ve 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\u2019ve already got a load of heavy JavaScript assets, and when it comes to file size saving every little helps.\n\nI don\u2019t 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\u2019s a good idea to keep our CSS and JavaScript separate so lets continue along those lines here.\n\nThe test script for this example looks like this:\n\n{\n\t\"title\": \"JS tabs implementation test\",\n\t\"description\": \"Check that the correct HTML patterns has been used\",\n\t\"author\": \"Ross Bruniges\",\n\t\"created\": \"20th July 2009\",\n\t\"tests\": [\n\t\t{\n\t\t\t\"name\": \"JS tabs elements\",\n\t\t\t\"description\": \"Checking that correct HTML elements including class/IDs are used on the page for the JS to progressively enhance\",\n\t\t\t\"selector\": \"div.tabbed_content\",\n\t\t\t\"message\": \"We couldn't find VAR on the page - it's required for our JavaScript to function correctly\",\n\t\t\t\"check_for\": {\n\t\t\t\t\"contains\": {\n\t\t\t\t\t\"elements\": [\n\t\t\t\t\t\t\"div.tab_content\", \"h2\" \n\t\t\t\t\t],\n\t\t\t\t\t\"message\": \"We've noticed some missing HTML:

please refer to the examples sent for reference\" \n\t\t\t\t} \n\t\t\t} \n\t\t} \n\t]\n}\n\nThe 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\u2019ll notice that it\u2019s an array \u2013 we\u2019re only going to show one example test here but there is no reason why you can\u2019t place in as many as you want. I\u2019ll explain what each of the lines in the example test means:\n\n\n\tname \u2013 short test name, I use this in pass/fail messaging later\n\tdescription \u2013 meta data for future reference\n\tselector \u2013 the root HTML element from which your HTML will be searched\n\tmessage \u2013 what the app will alert if the initial selector isn\u2019t found\n\tcheck_for \u2013 a wrapper to hold inner tests \u2013 those run if the initial selector does match\n\t\n\t\tcontains \u2013 the type of check, we\u2019re checking that the selector contains specified elements\n\t\t\n\t\t\telements \u2013 the HTML elements we are searching for\n\t\t\tmessage \u2013 a message for when these don\u2019t match (VAR is substituted when it\u2019s appended to the page with the name of any elements that don\u2019t exist)\n\t\t\n\t\t\n\t\t\n\nIt\u2019s 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. \n\nThe JavaScript that makes this helpful\n\nAgain, this code should never hit a production server so I\u2019ve kept it external. This also means that the only thing that\u2019s needed to be done by the implementation team when they are ready to build is that they delete this code.\n\n\n\n\n\u201cView the full JavaScript:/examples/self-testing-pages-with-javascript/js/tests/test_suite.js\n\nThe init function appends the test console to the page and inserts the CSS file required to style it (you don\u2019t need to use pictures of me when tests pass and fail though I see no reason why you shouldn\u2019t), 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).\n\nThe two interesting functions are init_tests and confirm_html.\n\ninit_tests\n\ninit_tests:function(i,obj) {\n\tvar $master_elm = $(obj.selector);\n\tsleuth.test_page.$logger.append(\"

\" + obj.name + \"

\");\n\tvar $container = $('#test_' + i);\n\tif (!$master_elm.length) {\n\t\tvar err_sum = obj.message.replace(/VAR/gi, obj.selector);\n\t\tsleuth.test_page.haz_failed(err_sum, $container);\n\t\treturn;\n\t}\n\tif (obj.check_for) {\n\t\t$.each(obj.check_for,function(key, value){\n\t\t\tsleuth.test_page.assign_checks($master_elm, $container, key, value);\n\t\t});\n\t} else {\n\t\tsleuth.test_page.tests_passed($container);\n\t\treturn;\n\t}\n}\n\nThe 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\u2019re testing against as parameters.\n\nWe 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\u2019t go any further. We append the error to the test console for everyone to see.\n\nIf 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). \n\nconfirm_html\n\nconfirm_html:function(target_selector, error_elm, obj) {\n\tvar missing_elms = [];\n\t$.each(obj.elements, function(i, val) {\n\t\tif (!target_selector.find(val).length) {\n\t\t\tmissing_elms.push(val);\n\t\t}\t\n\t});\n\tif (missing_elms.length) {\n\t\tvar file_list = missing_elms.join('
  • ');\n\t\tvar err_sum = obj.message.replace(/VAR/gi, file_list);\n\t\tsleuth.test_page.haz_failed(err_sum, error_elm);\n\t\treturn;\n\t}\n\tsleuth.test_page.tests_passed(error_elm);\n\treturn;\n}\n\nWe\u2019re 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.\n\nIf the test does fail we\u2019re 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\u2019ve sent and include them as expected.\n\nNo more silly implementation bugs!\n\nHere is an example of a successful implementation.\n\nHere are some examples of failed implementations \u2013 one which fails at finding the root node and one that has the correct root node but none of the inner HTML tests pass.\n\nIs this all we can check for?\n\nCertainly not!\n\nJavaScript 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.\n\nWant 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 \u2013 the sky is pretty much up to your imagination.", "year": "2009", "author": "Ross Bruniges", "author_slug": "rossbruniges", "published": "2009-12-12T00:00:00+00:00", "url": "https://24ways.org/2009/self-testing-pages-with-javascript/", "topic": "process"}