Skip to content

Instantly share code, notes, and snippets.

@sidonath
Created December 12, 2009 18:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sidonath/255007 to your computer and use it in GitHub Desktop.
Save sidonath/255007 to your computer and use it in GitHub Desktop.
(function($) {
function print_array(obj, opts) {
var result = [];
for (var i = 0; i < Math.min(opts.max_array, obj.length); i++)
result.push($.print(obj[i], $.extend({}, opts, { max_array: 3, max_string: 40 })));
if (obj.length > opts.max_array)
result.push((obj.length - opts.max_array) + ' more...');
if (result.length == 0) return "[]"
return "[ " + result.join(", ") + " ]";
}
function print_element(obj) {
if (obj.nodeType == 1) {
var result = [];
var properties = [ 'className', 'id' ];
var extra = {
'input': ['type', 'name', 'value'],
'a': ['href', 'target'],
'form': ['method', 'action'],
'script': ['src'],
'link': ['href'],
'img': ['src']
};
$.each(properties.concat(extra[obj.tagName.toLowerCase()] || []), function(){
if (obj[this])
result.push(' ' + this.replace('className', 'class') + "=" + $.print(obj[this]))
});
return "<" + obj.tagName.toLowerCase()
+ result.join('') + ">";
}
}
function print_object(obj, opts) {
var seen = opts.seen || [ obj ];
var result = [], key, value;
for (var k in obj) {
if (obj.hasOwnProperty(k) && $.inArray(obj[k], seen) < 0) {
seen.push(obj[k]);
value = $.print(obj[k], $.extend({}, opts, { max_array: 6, max_string: 40, seen: seen }));
} else
value = "...";
result.push(k + ": " + value);
}
if (result.length == 0) return "{}";
return "{ " + result.join(", ") + " }";
}
function print_string(value, opts) {
var character_substitutions = {
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
};
var r = /["\\\x00-\x1f\x7f-\x9f]/g;
var str = r.test(value)
? '"' + value.replace(r, function (a) {
var c = character_substitutions[a];
if (c) return c;
c = a.charCodeAt();
return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
}) + '"'
: '"' + value + '"';
if (str.length > opts.max_string)
return str.slice(0, opts.max_string + 1) + '..."';
else
return str;
}
$.print = function(obj, options) {
var opts = $.extend({}, { max_array: 10, max_string: 100 }, options);
if (typeof obj == 'undefined')
return "undefined";
else if (typeof obj == 'boolean')
return obj.toString();
else if (!obj && typeof obj == 'number')
return 'NaN';
else if (!obj)
return "null";
else if (typeof obj == 'string')
return print_string(obj, opts);
else if (obj instanceof RegExp)
return obj.toString();
else if (obj instanceof Array || obj.callee || obj.item)
return print_array(obj, opts);
else if (typeof obj == 'function' || obj instanceof Function)
return obj.toString().match(/^([^)]*\))/)[1];
else if (obj.nodeType)
return print_element(obj);
else if (obj instanceof jQuery)
return "$(" + $.print(obj.get()) + ")";
else if (obj instanceof Error)
return print_object(obj, $.extend({}, options, { max_string: 200 }));
else if (obj instanceof Object)
return print_object(obj, opts);
else
return obj.toString().replace(/\n\s*/g, '');
};
})(jQuery);
var Screw = (function() {
function Context(name) {
this.tc = null;
this.name = '';
if (name) {
this.tc = TestCase(name);
this.name = name;
}
this.befores = [];
this.afters = [];
this.parent = null;
/** Returns function that executes a given array of functions */
function functionListInvoker(fns) {
return function() {
var i;
for (i = 0; i < fns.length; i++) {
fns[i].call();
}
};
};
/** Push new context as a child of the current one
* A Screw.Unit's `define` maps to JTD's `TestCase`
*/
this.push = function(name) {
var
i, oldBefores, oldAfters,
c = new Context((this.name ? this.name + ': ' : '') + name)
;
// simply copy all parent's befores and afters to current context
c.befores = [];
c.afters = [];
for(i = 0; i < this.befores.length; i++) {
c.befores.push(this.befores[i]);
}
for(i = 0; i < this.afters.length; i++) {
c.afters.push(this.afters[i]);
}
c.parent = this;
return c;
};
/** Adds an `it` as a test to the current TestCase */
this.defineTest = function(name, fn) {
this.tc.prototype.setUp = functionListInvoker(this.befores);
this.tc.prototype.tearDown = functionListInvoker(this.afters);
this.tc.prototype['test ' + name] = fn;
};
}
var screw = {
Unit: function(fn) {
var contents = fn.toString().match(/^[^\{]*{((.*\n*)*)}/m)[1];
var fn = new Function("matchers", "specifications",
"with (specifications) { with (matchers) { " + contents + " } }"
);
fn.call(this, Screw.Matchers, Screw.Specifications);
},
Specifications: {
context: new Context(),
describe: function(name, fn) {
var dc = this.context.push(name)
this.context = dc;
fn.call();
this.context = dc.parent;
},
it: function(name, fn) {
this.context.defineTest(name, fn);
},
before: function(fn) {
this.context.befores.push(fn);
},
after: function(fn) {
this.context.afters.push(fn);
}
}
};
return screw;
})();
Screw.Matchers = (function($) {
return matchers = {
expect: function(actual) {
return {
to: function(matcher, expected, not) {
var matched = matcher.match(expected, actual);
if (not ? matched : !matched) {
fail(matcher.failure_message(expected, actual, not));
}
},
to_not: function(matcher, expected) {
this.to(matcher, expected, true);
}
}
},
equal: {
match: function(expected, actual) {
if (expected instanceof Array) {
for (var i = 0; i < actual.length; i++)
if (!Screw.Matchers.equal.match(expected[i], actual[i])) return false;
return actual.length == expected.length;
} else if (expected instanceof Object) {
for (var key in expected) {
if (!Screw.Matchers.equal.match(expected[key], actual[key])) {
return false;
}
}
for (var key in actual) {
if (!Screw.Matchers.equal.match(actual[key], expected[key])) {
return false;
}
}
return true;
} else {
return expected == actual;
}
},
failure_message: function(expected, actual, not) {
return 'expected \n' + $.print(actual) +
(not ? ' to not equal ' : ' to equal ') + '\n' + $.print(expected);
}
},
match: {
match: function(expected, actual) {
if (expected.constructor == RegExp)
return expected.exec(actual.toString());
else
return actual.indexOf(expected) > -1;
},
failure_message: function(expected, actual, not) {
return 'expected ' + $.print(actual) + (not ? ' to not match ' : ' to match ') + $.print(expected);
}
},
be_empty: {
match: function(expected, actual) {
if (actual.length == undefined) throw(actual.toString() + " does not respond to length");
return actual.length == 0;
},
failure_message: function(expected, actual, not) {
return 'expected ' + $.print(actual) + (not ? ' to not be empty' : ' to be empty');
}
},
have_length: {
match: function(expected, actual) {
if (actual.length == undefined) throw(actual.toString() + " does not respond to length");
return actual.length == expected;
},
failure_message: function(expected, actual, not) {
return 'expected ' + $.print(actual) + (not ? ' to not' : ' to') + ' have length ' + expected;
}
},
be_null: {
match: function(expected, actual) {
return actual == null;
},
failure_message: function(expected, actual, not) {
return 'expected ' + $.print(actual) + (not ? ' to not be null' : ' to be null');
}
},
be_undefined: {
match: function(expected, actual) {
return actual == undefined;
},
failure_message: function(expected, actual, not) {
return 'expected ' + $.print(actual) + (not ? ' to not be undefined' : ' to be undefined');
}
},
be_true: {
match: function(expected, actual) {
return actual;
},
failure_message: function(expected, actual, not) {
return 'expected ' + $.print(actual) + (not ? ' to not be true' : ' to be true');
}
},
be_false: {
match: function(expected, actual) {
return !actual;
},
failure_message: function(expected, actual, not) {
return 'expected ' + $.print(actual) + (not ? ' to not be false' : ' to be false');
}
},
match_selector: {
match: function(expected, actual) {
if (!(actual instanceof jQuery)) {
throw expected.toString() + " must be an instance of jQuery to match against a selector"
}
return actual.is(expected);
},
failure_message: function(expected, actual, not) {
return 'expected ' + $.print(actual) + (not ? ' to not match selector ' : ' to match selector ') + expected;
}
},
contain_selector: {
match: function(expected, actual) {
if (!(actual instanceof jQuery)) {
throw expected.toString() + " must be an instance of jQuery to match against a selector"
}
return actual.find(expected).length > 0;
},
failure_message: function(expected, actual, not) {
return 'expected ' + $.print(actual) + (not ? ' to not contain selector ' : ' to contain selector ') + expected;
}
}
}
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment