lib/jspec.js in jspec-2.11.13 vs lib/jspec.js in jspec-3.0.0

- old
+ new

@@ -1,22 +1,22 @@ // JSpec - Core - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed) -(function(){ +;(function(){ JSpec = { - - version : '2.11.13', + version : '3.1.0', + assert : true, cache : {}, suites : [], modules : [], allSuites : [], matchers : {}, stubbed : [], + options : {}, request : 'XMLHttpRequest' in this ? XMLHttpRequest : null, stats : { specs: 0, assertions: 0, failures: 0, passes: 0, specsFinished: 0, suitesFinished: 0 }, - options : { profile: false }, /** * Default context in which bodies are evaluated. * * Replace context simply by setting JSpec.context @@ -49,40 +49,33 @@ an_instance_of : function(constructor) { return { an_instance_of : constructor } }, /** - * Load fixture at _path_. This utility function - * supplies the means to resolve, and cache fixture contents - * via the DOM or Rhino. + * Load fixture at _path_. * * Fixtures are resolved as: * * - <path> - * - fixtures/<path> - * - fixtures/<path>.html + * - <path>.html * * @param {string} path * @return {string} * @api public */ fixture : function(path) { if (JSpec.cache[path]) return JSpec.cache[path] return JSpec.cache[path] = - JSpec.tryLoading(path) || - JSpec.tryLoading('fixtures/' + path) || - JSpec.tryLoading('fixtures/' + path + '.html') || - JSpec.tryLoading('spec/' + path) || - JSpec.tryLoading('spec/fixtures/' + path) || - JSpec.tryLoading('spec/fixtures/' + path + '.html') + JSpec.tryLoading(JSpec.options.fixturePath + '/' + path) || + JSpec.tryLoading(JSpec.options.fixturePath + '/' + path + '.html') } }, // --- Objects - formatters : { + reporters : { /** * Report to server. * * Options: @@ -121,11 +114,11 @@ }) if ('close' in main) main.close() }, /** - * Default formatter, outputting to the DOM. + * Default reporter, outputting to the DOM. * * Options: * - reportToId id of element to output reports to, defaults to 'jspec' * - failuresOnly displays only suites with failing specs * @@ -147,10 +140,11 @@ } report.innerHTML = '<div id="jspec-report" class="' + classes + '"><div class="heading"> \ <span class="passes">Passes: <em>' + results.stats.passes + '</em></span> \ <span class="failures">Failures: <em>' + results.stats.failures + '</em></span> \ + <span class="passes">Duration: <em>' + results.duration + '</em> ms</span> \ </div><table class="suites">' + map(results.allSuites, function(suite) { var displaySuite = failuresOnly ? suite.ran && !suite.passed() : suite.ran if (displaySuite && suite.hasSpecs()) return '<tr class="description"><td colspan="2">' + escape(suite.description) + '</td></tr>' + map(suite.specs, function(i, spec) { @@ -168,19 +162,20 @@ }).join('') + '</tr>' }).join('') + '</table></div>' }, /** - * Terminal formatter. + * Terminal reporter. * * @api public */ Terminal : function(results, options) { failuresOnly = option('failuresOnly') print(color("\n Passes: ", 'bold') + color(results.stats.passes, 'green') + - color(" Failures: ", 'bold') + color(results.stats.failures, 'red') + "\n") + color(" Failures: ", 'bold') + color(results.stats.failures, 'red') + + color(" Duration: ", 'bold') + color(results.duration, 'green') + " ms \n") function indent(string) { return string.replace(/^(.)/gm, ' $1') } @@ -206,11 +201,11 @@ quit(results.stats.failures) }, /** - * Console formatter. + * Console reporter. * * @api public */ Console : function(results, options) { @@ -244,11 +239,12 @@ expected: expected, // Report assertion results report : function() { - this.passed ? JSpec.stats.passes++ : JSpec.stats.failures++ + if (JSpec.assert) + this.passed ? JSpec.stats.passes++ : JSpec.stats.failures++ return this }, // Run the assertion @@ -268,11 +264,11 @@ var old = object[method] // Proxy object[method] = function(){ - args = argumentsToArray(arguments) + args = toArray(arguments) result = old.apply(object, args) self.calls.push({ args : args, result : result }) return result } @@ -300,31 +296,31 @@ }, // Proxy arguments passed with_args : function() { - this.expectedArgs = argumentsToArray(arguments) + this.expectedArgs = toArray(arguments) return this }, // Check if any calls have failing results anyResultsFail : function() { return any(this.calls, function(call){ return self.expectedResult.an_instance_of ? call.result.constructor != self.expectedResult.an_instance_of: - hash(self.expectedResult) != hash(call.result) + !equal(self.expectedResult, call.result) }) }, // Check if any calls have passing results anyResultsPass : function() { return any(this.calls, function(call){ return self.expectedResult.an_instance_of ? call.result.constructor == self.expectedResult.an_instance_of: - hash(self.expectedResult) == hash(call.result) + equal(self.expectedResult, call.result) }) }, // Return the passing result @@ -344,11 +340,11 @@ return any(this.calls, function(call){ return any(self.expectedArgs, function(i, arg){ if (arg == null) return call.args[i] == null return arg.an_instance_of ? call.args[i].constructor != arg.an_instance_of: - hash(arg) != hash(call.args[i]) + !equal(arg, call.args[i]) }) }) }, @@ -357,11 +353,11 @@ anyArgsPass : function() { return any(this.calls, function(call){ return any(self.expectedArgs, function(i, arg){ return arg.an_instance_of ? call.args[i].constructor == arg.an_instance_of: - hash(arg) == hash(call.args[i]) + equal(arg, call.args[i]) }) }) }, @@ -378,11 +374,12 @@ }, // Report assertion results report : function() { - this.passed ? ++JSpec.stats.passes : ++JSpec.stats.failures + if (JSpec.assert) + this.passed ? ++JSpec.stats.passes : ++JSpec.stats.failures return this }, // Run the assertion @@ -504,18 +501,18 @@ // Add passing assertion pass : function(message) { this.assertions.push({ passed: true, message: message }) - ++JSpec.stats.passes + if (JSpec.assert) ++JSpec.stats.passes }, // Add failing assertion fail : function(message) { this.assertions.push({ passed: false, message: message }) - ++JSpec.stats.failures + if (JSpec.assert) ++JSpec.stats.failures }, // Run deferred assertions runDeferredAssertions : function() { @@ -677,22 +674,22 @@ }, /** * Include _object_ which may be a hash or Module instance. * - * @param {has, Module} object + * @param {hash, Module} object * @return {JSpec} * @api public */ include : function(object) { var module = object.constructor == JSpec.Module ? object : new JSpec.Module(object) this.modules.push(module) if ('init' in module) module.init() if ('utilities' in module) extend(this.defaultContext, module.utilities) if ('matchers' in module) this.addMatchers(module.matchers) - if ('formatters' in module) extend(this.formatters, module.formatters) + if ('reporters' in module) extend(this.reporters, module.reporters) if ('DSLs' in module) each(module.DSLs, function(name, methods){ JSpec.DSLs[name] = JSpec.DSLs[name] || {} extend(JSpec.DSLs[name], methods) }) @@ -709,11 +706,11 @@ * @return {array} * @api private */ hook : function(name, args) { - args = argumentsToArray(arguments, 1) + args = toArray(arguments, 1) return inject(JSpec.modules, [], function(results, module){ if (typeof module[name] == 'function') results.push(JSpec.evalHook(module, name, args)) }) }, @@ -803,11 +800,11 @@ * @param {int} offset * @return {array} * @api public */ - argumentsToArray : function(arguments, offset) { + toArray : function(arguments, offset) { return Array.prototype.slice.call(arguments, offset || 0) }, /** * Return ANSI-escaped colored string. @@ -840,11 +837,13 @@ defaultMatcherMessage : function(actual, expected, negate, name) { return 'expected ' + puts(actual) + ' to ' + (negate ? 'not ' : '') + name.replace(/_/g, ' ') + - ' ' + puts.apply(this, expected.slice(1)) + ' ' + (expected.length > 1 ? + puts.apply(this, expected.slice(1)) : + '') }, /** * Normalize a matcher message. * @@ -905,40 +904,41 @@ option : function(key) { return (value = query(key)) !== null ? value : JSpec.options[key] || null }, + + /** + * Check if object _a_, is equal to object _b_. + * + * @param {object} a + * @param {object} b + * @return {bool} + * @api private + */ + + equal: function(a, b) { + if (typeof a != typeof b) return + if (a === b) return true + if (a instanceof RegExp) + return a.toString() === b.toString() + if (a instanceof Date) + return Number(a) === Number(b) + if (typeof a != 'object') return + if (a.length !== undefined) + if (a.length !== b.length) return + else + for (var i = 0, len = a.length; i < len; ++i) + if (!equal(a[i], b[i])) + return + for (var key in a) + if (!equal(a[key], b[key])) + return + return true + }, /** - * Generates a hash of the object passed. - * - * @param {object} object - * @return {string} - * @api private - */ - - hash : function(object) { - if (object == null) return 'null' - if (object == undefined) return 'undefined' - function serialize(prefix) { - return inject(object, prefix + ':', function(buffer, key, value){ - return buffer += hash(value) - }) - } - switch (object.constructor) { - case Array : return serialize('a') - case RegExp: return 'r:' + object.toString() - case Number: return 'n:' + object.toString() - case String: return 's:' + object.toString() - case Object: return 'o:' + inject(object, [], function(array, key, value){ - array.push([key, hash(value)]) - }).sort() - default: return object.toString() - } - }, - - /** * Return last element of an array. * * @param {array} array * @return {object} * @api public @@ -955,34 +955,39 @@ * @return {string} * @api public */ puts : function(object) { - if (arguments.length > 1) { - return map(argumentsToArray(arguments), function(arg){ + if (arguments.length > 1) + return map(toArray(arguments), function(arg){ return puts(arg) }).join(', ') - } - if (object === undefined) return '' + if (object === undefined) return 'undefined' if (object === null) return 'null' if (object === true) return 'true' if (object === false) return 'false' if (object.an_instance_of) return 'an instance of ' + object.an_instance_of.name - if (object.jquery && object.selector.length > 0) return 'selector ' + puts(object.selector) + '' + if (object.jquery && object.selector.length > 0) return 'selector ' + puts(object.selector) if (object.jquery) return object.get(0).outerHTML if (object.nodeName) return object.outerHTML switch (object.constructor) { - case String: return "'" + object + "'" - case Number: return object case Function: return object.name || object + case String: + return '"' + object + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\t/g, '\\t') + + '"' case Array: return inject(object, '[', function(b, v){ return b + ', ' + puts(v) }).replace('[,', '[') + ' ]' case Object: + object.__hit__ = true return inject(object, '{', function(b, k, v) { - return b + ', ' + puts(k) + ' : ' + puts(v) + if (k == '__hit__') return b + return b + ', ' + k + ': ' + (v && v.__hit__ ? '<circular reference>' : puts(v)) }).replace('{,', '{') + ' }' default: return object.toString() } }, @@ -1022,11 +1027,11 @@ * @return {mixed} * @api private */ does : function(actual, matcher, expected) { - var assertion = new JSpec.Assertion(JSpec.matchers[matcher], actual, argumentsToArray(arguments, 2)) + var assertion = new JSpec.Assertion(JSpec.matchers[matcher], actual, toArray(arguments, 2)) return assertion.run().result }, /** * Perform an assertion. @@ -1040,11 +1045,11 @@ * @api public */ expect : function(actual) { assert = function(matcher, args, negate) { - var expected = argumentsToArray(args, 1) + var expected = toArray(args, 1) matcher.negate = negate assertion = new JSpec.Assertion(matcher, actual, expected, negate) hook('beforeAssertion', assertion) if (matcher.defer) assertion.run() else JSpec.currentSpec.assertions.push(assertion.run().report()), hook('afterAssertion', assertion) @@ -1112,21 +1117,24 @@ }, /** * Iterate an object, invoking the given callback. * - * @param {hash, array, string} object + * @param {hash, array} object * @param {function} callback * @return {JSpec} * @api public */ each : function(object, callback) { - if (typeof object == 'string') object = object.split(' ') - for (key in object) - if (object.hasOwnProperty(key)) - callIterator(callback, key, object[key]) + if (object.constructor == Array) + for (var i = 0, len = object.length; i < len; ++i) + callIterator(callback, i, object[i]) + else + for (var key in object) + if (object.hasOwnProperty(key)) + callIterator(callback, key, object[key]) }, /** * Iterate with memo. * @@ -1324,11 +1332,11 @@ var dsl = this.DSL || this.DSLs.snake var matchers = this.matchers var context = this.context || this.defaultContext var contents = this.contentsOf(body) hook('evaluatingBody', dsl, matchers, context, contents) - try { eval('with (dsl){ with (context) { with (matchers) { ' + contents + ' }}}') } + try { with (dsl){ with (context) { with (matchers) { eval(contents) }}} } catch(e) { error(errorMessage, e) } }, /** * Pre-process a string of JSpec. @@ -1342,11 +1350,11 @@ if (typeof input != 'string') return input = hookImmutable('preprocessing', input) return input. replace(/\t/g, ' '). replace(/\r\n|\n|\r/g, '\n'). - replace(/__END__[^]*/, ''). + split('__END__')[0]. replace(/([\w\.]+)\.(stub|destub)\((.*?)\)$/gm, '$2($1, $3)'). replace(/describe\s+(.*?)$/gm, 'describe($1, function(){'). replace(/^\s+it\s+(.*?)$/gm, ' it($1, function(){'). replace(/^ *(before_each|after_each|before|after)(?= |\n|$)/gm, 'JSpec.currentSuite.addHook("$1", function(){'). replace(/^\s*end(?=\s|$)/gm, '});'). @@ -1379,12 +1387,13 @@ * * @api public */ report : function() { + this.duration = Number(new Date) - this.start hook('reporting', JSpec.options) - new (JSpec.options.formatter || JSpec.formatters.DOM)(JSpec, JSpec.options) + new (JSpec.options.reporter || JSpec.reporters.DOM)(JSpec, JSpec.options) }, /** * Run the spec suites. Options are merged * with JSpec options when present. @@ -1395,13 +1404,12 @@ */ run : function(options) { if (any(hook('running'), haveStopped)) return this if (options) extend(this.options, options) - if (option('profile')) console.group('Profile') + this.start = Number(new Date) each(this.suites, function(suite) { JSpec.runSuite(suite) }) - if (option('profile')) console.groupEnd() return this }, /** * Run a suite. @@ -1460,14 +1468,12 @@ * @api public */ runSpec : function(spec) { this.currentSpec = spec - if (option('profile')) console.time(spec.description) try { this.evalBody(spec.body) } catch (e) { fail(e) } - if (option('profile')) console.timeEnd(spec.description) spec.runDeferredAssertions() destub() this.stats.specsFinished++ this.stats.assertions += spec.assertions.length }, @@ -1586,12 +1592,11 @@ * @return {string} * @api public */ tryLoading : function(file) { - try { return JSpec.load(file) } - catch (e) {} + try { return JSpec.load(file) } catch (e) {} }, /** * Load a _file_'s contents. * @@ -1636,18 +1641,19 @@ // --- Utility functions var main = this var find = JSpec.any var utils = 'haveStopped stub hookImmutable hook destub map any last pass fail range each option inject select \ - error escape extend puts hash query strip color does addMatchers callIterator argumentsToArray'.split(/\s+/) - while (utils.length) util = utils.shift(), eval('var ' + util + ' = JSpec.' + util) + error escape extend puts query strip color does addMatchers callIterator toArray equal'.split(/\s+/) + while (utils.length) eval('var ' + utils[0] + ' = JSpec.' + utils.shift()) if (!main.setTimeout) main.setTimeout = function(callback){ callback() } // --- Matchers addMatchers({ equal : "===", + eql : "equal(actual, expected)", be : "alias equal", be_greater_than : ">", be_less_than : "<", be_at_least : ">=", be_at_most : "<=", @@ -1662,17 +1668,10 @@ match : "typeof actual == 'string' ? actual.match(expected) : false", respond_to : "typeof actual[expected] == 'function'", have_length : "actual.length == expected", be_within : "actual >= expected[0] && actual <= last(expected)", have_length_within : "actual.length >= expected[0] && actual.length <= last(expected)", - - eql : function(actual, expected) { - return actual.constructor == Array || - actual instanceof Object ? - hash(actual) == hash(expected): - actual == expected - }, receive : { defer : true, match : function(actual, method, times) { proxy = new JSpec.ProxyAssertion(actual, method, times, this.negate) JSpec.currentSpec.assertions.push(proxy) return proxy @@ -1699,11 +1698,11 @@ case Object: state = arg in actual break case Array: - state = any(actual, function(value){ return hash(value) == hash(arg) }) + state = any(actual, function(value){ return equal(value, arg) }) break } if (!state) return false } return true @@ -1713,13 +1712,13 @@ try { actual() } catch (e) { this.e = e var assert = function(arg) { switch (arg.constructor) { - case RegExp : return arg.test(e) + case RegExp : return arg.test(e.message || e.toString()) case String : return arg == (e.message || e.toString()) - case Function : return (e.name || 'Error') == arg.name + case Function : return e instanceof arg || e.name == arg.name } } return message ? assert(expected) && assert(message) : expected ? assert(expected) : true @@ -1769,8 +1768,6 @@ value == null ? true: value === actual[property] } }) - if ('exports' in main) exports.JSpec = JSpec - })() \ No newline at end of file