Skip to content

Instantly share code, notes, and snippets.

@tswicegood
Created December 12, 2009 18:04
Show Gist options
  • Save tswicegood/254984 to your computer and use it in GitHub Desktop.
Save tswicegood/254984 to your computer and use it in GitHub Desktop.
/**
sleuth.js
@author Ross Bruniges
@version 0.9
A light-weight JavaScript based HTML/CSS/JS confirmation tool (NOT a validator)
Write tests in JSON (example found at - ????)
Call in page via sleuth.test_page.init(json_objs)
*/
var sleuth = window.sleuth || {};
sleuth.test_page={
// variable set for the main container - used for appending to later
$logger:"",
// number of tests run (in total if using multiple JSON)
num_passed:0,
// total number of failures
num_failed:0,
// CSS class applied to passing and failing tests
test_fail_class:"fail",
test_pass_class:"win",
// message for when tests pass
test_pass_message:"Tests run and passed :)",
// location of CSS for console - CSS is loaded in via JS
console_css_location:"js/tests/test_suite.css",
// version of jQuery to use if it's not already in the project
/**
Setting up the test suite:
- Loads jQuery if required
- appends the console element to the body
- loads in CSS for console
- applies click event to show/hide console
@param json_url the json objects used in this particular page
(can be either a single string or an array of strings)
*/
init:function(json_url) {
$('body').append('<div id="test_console"><div class="controls"><p class="title">sleuth.js</p><a href="#">Hide results</a></div><div class="bd"></div></div>');
sleuth.test_page.$logger = $('#test_console .bd');
// 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);
// assign functionality for the show/hide link
$('#test_console a').click(function() {
sleuth.test_page.toggle_results($(this));
return false;
});
// getting the JSON
sleuth.test_page.get_JSON(json_url);
},
/**
Helper function to show/hide the test results on click
@param elm the HTML element that the click event is to be attached to
*/
toggle_results:function(elm) {
if (sleuth.test_page.$logger.css('display') === "none") {
sleuth.test_page.$logger.show("slow");
elm.text("Hide results");
elm.removeClass('show');
elm.parent().fadeTo("slow",1);
} else {
sleuth.test_page.$logger.hide("slow");
elm.text("Show results");
elm.addClass('show');
elm.parent().fadeTo("slow", 0.3);
}
},
/**
Loads the JSON objects and on success calls parse_JSON to start the tests
@param json Either a string containing the path to a single JSON object
or an array containing multiple paths (as strings)
*/
get_JSON:function(json) {
$.getJSON(json, function(json) {
sleuth.test_page.parse_JSON(json);
});
},
/**
Parses through the tests contained in the JSON and calls init_tests for each,
appends a title to the console, if detects the last iteration
calls create_summary
@param data loaded JSON data sent from get_JSON
*/
parse_JSON:function(data) {
sleuth.test_page.$logger.append("<p class='title'>Results for " + data.title + "</p>");
$.each(data.tests, function(key, value) {
sleuth.test_page.init_tests(key, value);
});
sleuth.test_page.create_summary();
},
/**
Function containing the initial check run on each test:
- type check to see if we need run this test at all (used when rechecking)
- grabs selector defined in test.selector and checks for existance
- appends div to console for display of message
@param i current index of iteration through test (used for appending)
@param obj the current test we're running though (JSON segment)
@return true is test pass (and no further tests required)
@return false is test fails at the first hurdle
*/
init_tests:function(i,obj) {
// grabbing the selector we're looking for
var $master_elm = $(obj.selector);
// assigning this to a var as we'll use it in all places where we traverse through child elements
sleuth.test_page.$logger.append("<div id='test_" + i + "' class='message'><p><em>" + obj.name + "</em></p></div>");
// assing this to a varible here - we'll need it for both cases
var $container = $('#test_' + i);
// OK - so if we don't find the initial selector we have big fail, so lets check for this first
if (!$master_elm.length) {
var err_sum = obj.message.replace(/VAR/gi, obj.selector);
sleuth.test_page.haz_failed(err_sum, $container);
return;
}
/*
now the first hurdle has been cleared we perform the additional checks if required,
if not then woohoo we've passed!
*/
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;
}
},
/**
Method which define the test to be run on each JSON segment
If sent an invalid test it fails with a generic error message
@param target_selector HTML object to apply test to
@param error_elm the div to append message to
@param check_name name of the test to apply (used in switch statment)
@param obj JSON segment containing values to be used in test
*/
assign_checks:function(target_selector, error_elm, check_name, obj) {
// ok - so we know the checks we have and will use switch to pass over them and assign checks as appropriate
switch(check_name) {
case "contains":
sleuth.test_page.confirm_html(target_selector, error_elm, obj);
break;
default:
sleuth.test_page.haz_failed("This will fail as no code has been written to perform the check against " + check_name, error_elm);
break;
}
},
/**
Method called when tests fail:
- adds sleuth.test_page.test_fail_class to message container for styling
- increments sleuth.test_page.num_failed
- appends message to message container
@param msg the message we're providing to the user
@param elm the div element for this test to apply msg to
*/
haz_failed:function(msg,elm) {
elm.addClass(sleuth.test_page.test_fail_class);
sleuth.test_page.num_failed++;
elm.append("<p>" + msg + "</p>");
},
/**
Method called when tests pass:
- increments sleuth.test_page.num_passed
- appends message to message container
@param msg the message we're providing to the user
@param elm the div element for this test to apply msg to
*/
tests_passed:function(elm) {
elm.addClass(sleuth.test_page.test_pass_class);
sleuth.test_page.num_passed++;
elm.append("<p>" + sleuth.test_page.test_pass_message + "</p>");
},
/**
Method to create a summary of all tests to the user on tests completion
Creates and assigns click events to a link allowing user to recheck page
(which will also call AJAX only tests)
*/
create_summary:function() {
var num_tests = sleuth.test_page.num_passed + sleuth.test_page.num_failed;
// creating html string to append when finished
var summary_html = "<div class='test_summary'><p><strong>Summary</strong></p><ul>";
summary_html += "<li>Tests run: " + num_tests + "</li>";
summary_html += "<li>Tests passed: " + sleuth.test_page.num_passed + "</li>";
summary_html += "<li>Tests failed: " + sleuth.test_page.num_failed + "</li>";
summary_html += "</ul>";
summary_html += "</div>";
sleuth.test_page.$logger.prepend(summary_html);
},
/**
Method to parse page and look for specific HTML selectors:
- creates empty arrays for all missing elements
- checks for position length of jQuery object gained from using HTML selector
- pushes all un-matched files into array of missing files
- if unique object detected in JSON checks for length of jQuery object of greater than 1
@param target_selector jQuery object containing the element we're testing against
@param error_elm the div to apply message to on completion
@param obj JSON segment containing test data and nested
tests for asserting only one selector on the page (optional)
*/
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;
}
// we've made it through all the checks unscathed - so let's pass the test
sleuth.test_page.tests_passed(error_elm);
return;
}
};
#test_console {
border: 1px solid #999;
background: #fff;
position: absolute;
font-size: 10px;
top: 10px;
right: 10px;
text-align: left;
-moz-border-radius: 5px;
-moz-box-shadow: 5px 5px 5px #000;
padding: 10px;
/* we need a width to constrain the box */
_width: 320px;
}
#test_console .controls {
overflow: hidden;
zoom: 1;
}
#test_console a {
float: right;
font-weight: bold;
padding-right: 20px;
background: transparent url(icons/hide.png) no-repeat top right;
line-height: 1.6;
margin-left: 10px;
}
#test_console a.show {
background-image: url(icons/show.png);
}
#test_console .bd {
width: 300px;
height: 300px;
margin-top: 10px;
overflow-y:scroll;
}
#test_console .bd a {
float: none;
margin-top: 1em;
display: block;
background: none;
padding-right: 0;
margin-left: 0;
}
#test_console .title {
font-weight: bold;
font-size: 1.6em;
float: left;
}
#test_console .bd .title {
font-size: 1.2em;
padding-top: 0.5em;
float: none;
clear:both;
}
#test_console .bd .message {
border-bottom: 2px solid #fff;
padding: 0.2em 5px 0.2em 50px;
background: transparent url(icons/win.jpg) no-repeat 5px 5px;
min-height: 50px;
/* IE has epic min-height FAIL */
_height: 50px;
margin-right: 10px;
background-color: #E5EECC;
color: #617F10;
}
#test_console .bd div:last-child {
border-bottom: none;
}
#test_console .bd div.fail {
background-image: url(icons/fail.jpg);
background-color: #FCEEF0;
color: #B90D33;
}
#test_console p,
#test_console ul,
#test_console fieldset {
font-size: 1.2em;
}
#test_console p {
margin-bottom: 0.5em;
}
#test_console li {
list-style-type: disc;
margin-left: 20px;
}
#test_console p em {
font-style: normal;
font-weight: bold;
}
#test_console div.test_summary {
background: none;
padding-left: 0;
color: #666;
width: 45%;
float: left;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment