blob: 1da4812577c08702ee3bfd02092cf612ebcc900c [file] [log] [blame]
var easyTest = (function(){
var _messages;
var _start;
var MessageType = {
Error: 1,
Success: 2,
Info: 3
};
/**
* Logs the message to the body
* @param {String} msg The message to displey
* @param {MessageType} type The messagetype
*/
function _log(msg, type){
var el = _messages.appendChild(document.createElement("div"));
el.innerHTML = msg;
_messages.scrollTop = _messages.scrollHeight;
switch (type) {
case MessageType.Error:
el.className = "easyTest_error";
break;
case MessageType.Success:
el.className = "easyTest_success";
break;
default:
el.className = "easyTest_msg";
break;
}
}
var Assert = {
// Type checks
isTypeOf: function(type, obj){
return typeof obj === type;
},
isInstanceOf: function(type, obj){
return obj instanceof type;
},
isString: function(obj){
return this.isTypeOf("string", obj);
},
isNumber: function(obj){
return this.isTypeOf("number", obj);
},
isObject: function(obj){
return this.isTypeOf("object", obj);
},
isBoolean: function(obj){
return this.isTypeOf("boolean", obj);
},
isFunction: function(obj){
return this.isTypeOf("function", obj);
},
// Equality
areEqual: function(a, b){
return a == b;
},
areNotEqual: function(a, b){
return a != b;
},
// Identical
areSame: function(a, b){
return a === b;
},
areNotSame: function(a, b){
return a !== b;
}
};
function Test(test, fn){
var _scope, _steps = test.steps, _step, _stepIndex = 0;
var _timer, _runStep, _startedAt, _stepStartedAt;
/**
* Clean up and tear down the test.<br/>
* Calls back to notify that the test is complete
*/
function _endTest(){
// Tear down the test
if (test.tearDown) {
try {
test.tearDown.call(_scope);
}
catch (ex) {
_log("Teardown '" + ex.message + "(" + ex.fileName + ", " + ex.lineNumber + ")" + "'", MessageType.Error);
}
}
for (var key in _scope) {
if (_scope.hasOwnProperty(key)) {
delete _scope[key];
}
}
// Call back
fn();
}
/**
* Used to notify the framework of the result of the test
* @param {String} name The name of the test
* @param {Boolean} result The result of the test
* @param {String} reason An optional reason why the test returned the result
*/
function _notifyResult(name, result, reason){
var now = new Date().getTime();
var testsetTime = now - _start.getTime();
var testTime = now - _startedAt.getTime();
var stepTime = now - _stepStartedAt.getTime();
var times = testsetTime + "ms, " + testTime + "ms, " + stepTime + "ms - ";
if (result) {
_log(times + name + " succeeded! " + (reason || ""), MessageType.Success);
}
else {
_log(times + name + " failed! " + (reason || ""), MessageType.Error);
if (test.failedMessage) {
_log(test.failedMessage, MessageType.Info);
}
}
// Go to next step
if (result) {
_stepIndex++;
window.setTimeout(function(){
_runStep();
}, 0);
}
else {
_endTest();
}
}
/**
* Runs through the test step
*/
_runStep = function(){
if (_stepIndex < _steps.length) {
// We still have steps to run
_step = _steps[_stepIndex];
_stepStartedAt = new Date();
if (_step.timeout) {
// This an asynchronous test
_timer = window.setTimeout(function(){
_notifyResult(_step.name, false, "Failed due to timeout.");
}, _step.timeout);
try {
_step.run.call(_scope);
}
catch (ex) {
//If it fails we cancel the timeout
window.clearTimeout(_timer);
_notifyResult(_step.name, false, "'" + ex.message + "(" + ex.fileName + ", " + ex.lineNumber + ")" + "'");
}
}
else {
// This is a synchronous test
try {
var result = _step.run.call(_scope);
_notifyResult(_step.name, result);
}
catch (ex) {
_notifyResult(_step.name, false, "'" + ex.message + "(" + ex.fileName + ", " + ex.lineNumber + ")" + "'");
}
}
}
else {
_endTest();
}
};
return {
/**
* Runs the test.
* Will first try to execute the setup method before continuing the steps
*/
run: function(){
var excuse;
if (test.runIf) {
excuse = test.runIf();
}
if (excuse) {
_log("Skipping test ' " + test.name + "'. " + excuse);
fn();
}
else {
_log("Running test '" + test.name + "'");
_scope = {
Assert: Assert,
log: _log,
notifyResult: function(result){
window.clearTimeout(_timer);
_notifyResult(_step.name, result);
}
};
if (test.setUp) {
// Setup the test
try {
test.setUp.call(_scope);
_log("Setup succeeded", MessageType.Success);
}
catch (ex) {
_log("Setup failed", MessageType.Error);
}
}
_startedAt = new Date();
_runStep();
}
}
};
}
return {
/**
* Runs through all the tests
* @param {Array} tests The tests to run
*/
test: function(testset){
var tests = [], testConfig, i = testset.length, test;
// Prepare the messaging facilities
if (!_messages) {
_messages = document.createElement("div");
_messages.className = "easyTest_messages";
(document.getElementById("messages") || document.body).appendChild(_messages);
}
else {
_messages.innerHTML = "";
}
// Convert the testset
while (i--) {
testConfig = testset[i];
if (!testConfig.steps) {
// Convert a single step test to a proper test
testConfig = {
steps: testConfig
};
}
tests.push(new Test(testConfig, function(){
// Get the next test to run
test = tests.pop();
if (test) {
// This is needed to avoid a strange bug in Opera,
window.setTimeout(function(){
test.run();
}, 0);
}
else {
// No more tests to run
_log("Test run complete", MessageType.Info);
}
}));
}
// Start the first test
_start = new Date();
tests.pop().run();
}
};
}());